From 0f84903ebf9df197f1f6fa6c2ff257942d11a199 Mon Sep 17 00:00:00 2001 From: Nicolas De Loof Date: Sat, 14 Mar 2026 11:47:58 +0100 Subject: [PATCH] replace tests based on a huge "full" example and use tests dedicated to each attribute Signed-off-by: Nicolas De Loof --- loader/full-example.yml | 461 ------ loader/full-struct_test.go | 1742 ---------------------- loader/loader_test.go | 765 ---------- loader/tests/annotations_test.go | 49 + loader/tests/attach_test.go | 50 + loader/tests/blkio_config_test.go | 66 + loader/tests/build_extra_test.go | 146 ++ loader/tests/build_test.go | 138 ++ loader/tests/cap_add_drop_test.go | 48 + loader/tests/cgroup_parent_test.go | 43 + loader/tests/cgroup_test.go | 43 + loader/tests/command_test.go | 54 + loader/tests/configs_test.go | 86 ++ loader/tests/container_name_test.go | 43 + loader/tests/cpu_test.go | 86 ++ loader/tests/credential_spec_test.go | 43 + loader/tests/depends_on_test.go | 101 ++ loader/tests/deploy_pids_test.go | 45 + loader/tests/deploy_test.go | 190 +++ loader/tests/develop_test.go | 93 ++ loader/tests/device_cgroup_rules_test.go | 45 + loader/tests/devices_test.go | 88 ++ loader/tests/dns_opt_test.go | 45 + loader/tests/dns_test.go | 74 + loader/tests/domainname_test.go | 43 + loader/tests/entrypoint_test.go | 54 + loader/tests/env_file_test.go | 89 ++ loader/tests/environment_test.go | 139 ++ loader/tests/expose_test.go | 45 + loader/tests/extensions_test.go | 69 + loader/tests/external_links_test.go | 47 + loader/tests/extra_hosts_test.go | 94 ++ loader/tests/gpus_test.go | 54 + loader/tests/group_add_test.go | 45 + loader/tests/gw_priority_test.go | 47 + loader/tests/healthcheck_test.go | 55 + loader/tests/helpers_test.go | 87 ++ loader/tests/hostname_test.go | 43 + loader/tests/image_test.go | 42 + loader/tests/init_test.go | 50 + loader/tests/interface_name_test.go | 47 + loader/tests/ipc_uts_pid_test.go | 47 + loader/tests/isolation_test.go | 43 + loader/tests/labels_test.go | 69 + loader/tests/link_local_ips_test.go | 48 + loader/tests/links_test.go | 50 + loader/tests/logging_test.go | 49 + loader/tests/mac_address_test.go | 43 + loader/tests/models_test.go | 53 + loader/tests/network_driver_opts_test.go | 50 + loader/tests/network_internal_test.go | 72 + loader/tests/network_mode_test.go | 43 + loader/tests/networks_test.go | 122 ++ loader/tests/oom_test.go | 62 + loader/tests/pids_limit_test.go | 43 + loader/tests/platform_test.go | 43 + loader/tests/ports_test.go | 126 ++ loader/tests/privileged_test.go | 45 + loader/tests/profiles_test.go | 58 + loader/tests/provider_test.go | 54 + loader/tests/pull_policy_test.go | 57 + loader/tests/restart_test.go | 43 + loader/tests/runtime_test.go | 43 + loader/tests/scale_test.go | 43 + loader/tests/secrets_test.go | 94 ++ loader/tests/security_opt_test.go | 46 + loader/tests/service_hooks_test.go | 67 + loader/tests/stdin_tty_test.go | 45 + loader/tests/stop_test.go | 61 + loader/tests/storage_opt_test.go | 44 + loader/tests/sysctls_test.go | 61 + loader/tests/template_driver_test.go | 106 ++ loader/tests/tmpfs_test.go | 49 + loader/tests/ulimits_test.go | 53 + loader/tests/use_api_socket_test.go | 43 + loader/tests/user_test.go | 43 + loader/tests/userns_mode_test.go | 43 + loader/tests/volume_bind_test.go | 98 ++ loader/tests/volumes_from_test.go | 47 + loader/tests/volumes_test.go | 89 ++ loader/tests/working_dir_test.go | 43 + loader/types_test.go | 66 - 82 files changed, 4964 insertions(+), 3034 deletions(-) delete mode 100644 loader/full-example.yml delete mode 100644 loader/full-struct_test.go create mode 100644 loader/tests/annotations_test.go create mode 100644 loader/tests/attach_test.go create mode 100644 loader/tests/blkio_config_test.go create mode 100644 loader/tests/build_extra_test.go create mode 100644 loader/tests/build_test.go create mode 100644 loader/tests/cap_add_drop_test.go create mode 100644 loader/tests/cgroup_parent_test.go create mode 100644 loader/tests/cgroup_test.go create mode 100644 loader/tests/command_test.go create mode 100644 loader/tests/configs_test.go create mode 100644 loader/tests/container_name_test.go create mode 100644 loader/tests/cpu_test.go create mode 100644 loader/tests/credential_spec_test.go create mode 100644 loader/tests/depends_on_test.go create mode 100644 loader/tests/deploy_pids_test.go create mode 100644 loader/tests/deploy_test.go create mode 100644 loader/tests/develop_test.go create mode 100644 loader/tests/device_cgroup_rules_test.go create mode 100644 loader/tests/devices_test.go create mode 100644 loader/tests/dns_opt_test.go create mode 100644 loader/tests/dns_test.go create mode 100644 loader/tests/domainname_test.go create mode 100644 loader/tests/entrypoint_test.go create mode 100644 loader/tests/env_file_test.go create mode 100644 loader/tests/environment_test.go create mode 100644 loader/tests/expose_test.go create mode 100644 loader/tests/extensions_test.go create mode 100644 loader/tests/external_links_test.go create mode 100644 loader/tests/extra_hosts_test.go create mode 100644 loader/tests/gpus_test.go create mode 100644 loader/tests/group_add_test.go create mode 100644 loader/tests/gw_priority_test.go create mode 100644 loader/tests/healthcheck_test.go create mode 100644 loader/tests/helpers_test.go create mode 100644 loader/tests/hostname_test.go create mode 100644 loader/tests/image_test.go create mode 100644 loader/tests/init_test.go create mode 100644 loader/tests/interface_name_test.go create mode 100644 loader/tests/ipc_uts_pid_test.go create mode 100644 loader/tests/isolation_test.go create mode 100644 loader/tests/labels_test.go create mode 100644 loader/tests/link_local_ips_test.go create mode 100644 loader/tests/links_test.go create mode 100644 loader/tests/logging_test.go create mode 100644 loader/tests/mac_address_test.go create mode 100644 loader/tests/models_test.go create mode 100644 loader/tests/network_driver_opts_test.go create mode 100644 loader/tests/network_internal_test.go create mode 100644 loader/tests/network_mode_test.go create mode 100644 loader/tests/networks_test.go create mode 100644 loader/tests/oom_test.go create mode 100644 loader/tests/pids_limit_test.go create mode 100644 loader/tests/platform_test.go create mode 100644 loader/tests/ports_test.go create mode 100644 loader/tests/privileged_test.go create mode 100644 loader/tests/profiles_test.go create mode 100644 loader/tests/provider_test.go create mode 100644 loader/tests/pull_policy_test.go create mode 100644 loader/tests/restart_test.go create mode 100644 loader/tests/runtime_test.go create mode 100644 loader/tests/scale_test.go create mode 100644 loader/tests/secrets_test.go create mode 100644 loader/tests/security_opt_test.go create mode 100644 loader/tests/service_hooks_test.go create mode 100644 loader/tests/stdin_tty_test.go create mode 100644 loader/tests/stop_test.go create mode 100644 loader/tests/storage_opt_test.go create mode 100644 loader/tests/sysctls_test.go create mode 100644 loader/tests/template_driver_test.go create mode 100644 loader/tests/tmpfs_test.go create mode 100644 loader/tests/ulimits_test.go create mode 100644 loader/tests/use_api_socket_test.go create mode 100644 loader/tests/user_test.go create mode 100644 loader/tests/userns_mode_test.go create mode 100644 loader/tests/volume_bind_test.go create mode 100644 loader/tests/volumes_from_test.go create mode 100644 loader/tests/volumes_test.go create mode 100644 loader/tests/working_dir_test.go delete mode 100644 loader/types_test.go diff --git a/loader/full-example.yml b/loader/full-example.yml deleted file mode 100644 index 944b2d47..00000000 --- a/loader/full-example.yml +++ /dev/null @@ -1,461 +0,0 @@ -name: full_example_project_name -services: - - bar: - build: - dockerfile_inline: | - FROM alpine - RUN echo "hello" > /world.txt - - foo: - annotations: - - com.example.foo=bar - build: - context: ./dir - dockerfile: Dockerfile - args: - foo: bar - ssh: - - default - target: foo - network: foo - cache_from: - - foo - - bar - labels: [FOO=BAR] - additional_contexts: - foo: ./bar - secrets: - - source: secret1 - target: /run/secrets/secret1 - - source: secret2 - target: my_secret - uid: '103' - gid: '103' - mode: 0440 - tags: - - foo:v1.0.0 - - docker.io/username/foo:my-other-tag - - ${COMPOSE_PROJECT_NAME}:1.0.0 - platforms: - - linux/amd64 - - linux/arm64 - - - cap_add: - - ALL - - cap_drop: - - NET_ADMIN - - SYS_ADMIN - - cgroup_parent: m-executor-abcd - - # String or list - command: bundle exec thin -p 3000 - # command: ["bundle", "exec", "thin", "-p", "3000"] - - configs: - - config1 - - source: config2 - target: /my_config - uid: '103' - gid: '103' - mode: 0440 - - container_name: my-web-container - - depends_on: - - db - - redis - - deploy: - mode: replicated - replicas: 6 - labels: [FOO=BAR] - rollback_config: - parallelism: 3 - delay: 10s - failure_action: continue - monitor: 60s - max_failure_ratio: 0.3 - order: start-first - update_config: - parallelism: 3 - delay: 10s - failure_action: continue - monitor: 60s - max_failure_ratio: 0.3 - order: start-first - resources: - limits: - cpus: '0.001' - memory: 50M - reservations: - cpus: '0.0001' - memory: 20M - generic_resources: - - discrete_resource_spec: - kind: 'gpu' - value: 2 - - discrete_resource_spec: - kind: 'ssd' - value: 1 - restart_policy: - condition: on-failure - delay: 5s - max_attempts: 3 - window: 120s - placement: - constraints: [node=foo] - max_replicas_per_node: 5 - preferences: - - spread: node.labels.az - endpoint_mode: dnsrr - - device_cgroup_rules: - - "c 1:3 mr" - - "a 7:* rmw" - - devices: - - source: /dev/ttyUSB0 - target: /dev/ttyUSB0 - permissions: rwm - - # String or list - # dns: 8.8.8.8 - dns: - - 8.8.8.8 - - 9.9.9.9 - - # String or list - # dns_search: example.com - dns_search: - - dc1.example.com - - dc2.example.com - - domainname: foo.com - - # String or list - # entrypoint: /code/entrypoint.sh -p 3000 - entrypoint: ["/code/entrypoint.sh", "-p", "3000"] - - # String or list - # env_file: .env - env_file: - - ./example1.env - - path: ./example2.env - required: false - - # Mapping or list - # Mapping values can be strings, numbers or null - # Booleans are not allowed - must be quoted - environment: - BAZ: baz_from_service_def - QUX: - # environment: - # - RACK_ENV=development - # - SHOW=true - # - SESSION_SECRET - - # Items can be strings or numbers - expose: - - "3000" - - 8000 - - external_links: - - redis_1 - - project_db_1:mysql - - project_db_1:postgresql - - # Mapping or list - # Mapping values must be strings - # extra_hosts: - # somehost: "162.242.195.82" - # otherhost: "50.31.209.229" - extra_hosts: - - "otherhost:50.31.209.229" - - "somehost:162.242.195.82" - - hostname: foo - - healthcheck: - test: echo "hello world" - interval: 10s - timeout: 1s - retries: 5 - start_period: 15s - start_interval: 5s - - # Any valid image reference - repo, tag, id, sha - image: redis - # image: ubuntu:14.04 - # image: tutum/influxdb - # image: example-registry.com:4000/postgresql - # image: a4bc65fd - # image: busybox@sha256:38a203e1986cf79639cfb9b2e1d6e773de84002feea2d4eb006b52004ee8502d - - ipc: host - - uts: host - - # Mapping or list - # Mapping values can be strings, numbers or null - labels: - com.example.description: "Accounting webapp" - com.example.number: 42 - com.example.empty-label: - # labels: - # - "com.example.description=Accounting webapp" - # - "com.example.number=42" - # - "com.example.empty-label" - - label_file: - - ./example1.label - - ./example2.label - - links: - - db - - db:database - - redis - - logging: - driver: syslog - options: - syslog-address: "tcp://192.168.0.42:123" - - mac_address: 02:42:ac:11:65:43 - - # network_mode: "bridge" - # network_mode: "host" - # network_mode: "none" - # Use the network mode of an arbitrary container from another service - # network_mode: "service:db" - # Use the network mode of another container, specified by name or id - # network_mode: "container:some-container" - network_mode: "container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b" - - networks: - some-network: - aliases: - - alias1 - - alias3 - other-network: - ipv4_address: 172.16.238.10 - ipv6_address: 2001:3984:3989::10 - mac_address: 02:42:72:98:65:08 - other-other-network: - - pid: "host" - - ports: - - 3000 - - "3001-3005" - - "8000:8000" - - "9090-9091:8080-8081" - - "49100:22" - - "127.0.0.1:8001:8001" - - "127.0.0.1:5000-5010:5000-5010" - - privileged: true - - read_only: true - - restart: always - - secrets: - - source: secret1 - target: /run/secrets/secret1 - - source: secret2 - target: my_secret - uid: '103' - gid: '103' - mode: 0440 - - security_opt: - - label=level:s0:c100,c200 - - label=type:svirt_apache_t - - stdin_open: true - - stop_grace_period: 20s - - stop_signal: SIGUSR1 - storage_opt: - size: "20G" - sysctls: - net.core.somaxconn: 1024 - net.ipv4.tcp_syncookies: 0 - - # String or list - # tmpfs: /run - tmpfs: - - /run - - /tmp - - tty: true - - ulimits: - # Single number or mapping with soft + hard limits - nproc: 65535 - nofile: - soft: 20000 - hard: 40000 - - user: someone - - volumes: - # Just specify a path and let the Engine create a volume - - /var/lib/anonymous - # Specify an absolute path mapping - - /opt/data:/var/lib/data - # Path on the host, relative to the Compose file - - .:/code - - ./static:/var/www/html - # User-relative path - - ~/configs:/etc/configs:ro - # Named volume - - datavolume:/var/lib/volume - - type: bind - source: ./opt - target: /opt/cached - consistency: cached - - type: tmpfs - target: /opt/tmpfs - tmpfs: - size: 10000 - - working_dir: /code - x-bar: baz - x-foo: bar - -networks: - # Entries can be null, which specifies simply that a network - # called "{project name}_some-network" should be created and - # use the default driver - some-network: - - other-network: - driver: overlay - - driver_opts: - # Values can be strings or numbers - foo: "bar" - baz: 1 - - ipam: - driver: overlay - # driver_opts: - # # Values can be strings or numbers - # com.docker.network.enable_ipv6: "true" - # com.docker.network.numeric_value: 1 - config: - - subnet: 172.28.0.0/16 - ip_range: 172.28.5.0/24 - gateway: 172.28.5.254 - aux_addresses: - host1: 172.28.1.5 - host2: 172.28.1.6 - host3: 172.28.1.7 - - subnet: 2001:3984:3989::/64 - gateway: 2001:3984:3989::1 - - labels: - foo: bar - - external-network: - # Specifies that a pre-existing network called "external-network" - # can be referred to within this file as "external-network" - external: true - - other-external-network: - # Specifies that a pre-existing network called "my-cool-network" - # can be referred to within this file as "other-external-network" - external: - name: my-cool-network - x-bar: baz - x-foo: bar - -volumes: - # Entries can be null, which specifies simply that a volume - # called "{project name}_some-volume" should be created and - # use the default driver - some-volume: - - other-volume: - driver: flocker - - driver_opts: - # Values can be strings or numbers - foo: "bar" - baz: 1 - labels: - foo: bar - - another-volume: - name: "user_specified_name" - driver: vsphere - - driver_opts: - # Values can be strings or numbers - foo: "bar" - baz: 1 - - external-volume: - # Specifies that a pre-existing volume called "external-volume" - # can be referred to within this file as "external-volume" - external: true - - other-external-volume: - # Specifies that a pre-existing volume called "my-cool-volume" - # can be referred to within this file as "other-external-volume" - # This example uses the deprecated "volume.external.name" (replaced by "volume.name") - external: - name: my-cool-volume - - external-volume3: - # Specifies that a pre-existing volume called "this-is-volume3" - # can be referred to within this file as "external-volume3" - name: this-is-volume3 - external: true - x-bar: baz - x-foo: bar - -configs: - config1: - file: ./config_data - labels: - foo: bar - config2: - external: - name: my_config - config3: - external: true - config4: - name: foo - file: ~/config_data - x-bar: baz - x-foo: bar - -secrets: - secret1: - file: ./secret_data - labels: - foo: bar - secret2: - external: - name: my_secret - secret3: - external: true - secret4: - name: bar - environment: BAR - x-bar: baz - x-foo: bar - secret5: - file: /abs/secret_data -x-bar: baz -x-foo: bar -x-nested: - bar: baz - foo: bar diff --git a/loader/full-struct_test.go b/loader/full-struct_test.go deleted file mode 100644 index 77e05f08..00000000 --- a/loader/full-struct_test.go +++ /dev/null @@ -1,1742 +0,0 @@ -/* - Copyright 2020 The Compose Specification Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package loader - -import ( - "fmt" - "path/filepath" - "strings" - "time" - - "github.com/compose-spec/compose-go/v2/types" -) - -func fullExampleProject(workingDir, homeDir string) *types.Project { - return &types.Project{ - Name: "full_example_project_name", - Services: services(workingDir, homeDir), - Networks: networks(), - Volumes: volumes(), - Configs: configs(workingDir, homeDir), - Secrets: secrets(workingDir), - Extensions: map[string]interface{}{ - "x-foo": "bar", - "x-bar": "baz", - "x-nested": map[string]interface{}{ - "foo": "bar", - "bar": "baz", - }, - }, - } -} - -func services(workingDir, homeDir string) types.Services { - return types.Services{ - "foo": { - Name: "foo", - - Annotations: map[string]string{ - "com.example.foo": "bar", - }, - Build: &types.BuildConfig{ - Context: filepath.Join(workingDir, "dir"), - Dockerfile: "Dockerfile", - Args: map[string]*string{"foo": strPtr("bar")}, - SSH: []types.SSHKey{{ID: "default", Path: ""}}, - Target: "foo", - Network: "foo", - CacheFrom: []string{"foo", "bar"}, - AdditionalContexts: types.Mapping{"foo": filepath.Join(workingDir, "bar")}, - Labels: map[string]string{"FOO": "BAR"}, - Secrets: []types.ServiceSecretConfig{ - { - Source: "secret1", - Target: "/run/secrets/secret1", - }, - { - Source: "secret2", - Target: "my_secret", - UID: "103", - GID: "103", - Mode: ptr(types.FileMode(0o440)), - }, - }, - Tags: []string{"foo:v1.0.0", "docker.io/username/foo:my-other-tag", "full_example_project_name:1.0.0"}, - Platforms: []string{"linux/amd64", "linux/arm64"}, - }, - CapAdd: []string{"ALL"}, - CapDrop: []string{"NET_ADMIN", "SYS_ADMIN"}, - CgroupParent: "m-executor-abcd", - Command: []string{"bundle", "exec", "thin", "-p", "3000"}, - Configs: []types.ServiceConfigObjConfig{ - { - Source: "config1", - }, - { - Source: "config2", - Target: "/my_config", - UID: "103", - GID: "103", - Mode: ptr(types.FileMode(0o440)), - }, - }, - ContainerName: "my-web-container", - DependsOn: types.DependsOnConfig{ - "db": {Condition: types.ServiceConditionStarted, Required: true}, - "redis": {Condition: types.ServiceConditionStarted, Required: true}, - }, - Deploy: &types.DeployConfig{ - Mode: "replicated", - Replicas: ptr(6), - Labels: map[string]string{"FOO": "BAR"}, - RollbackConfig: &types.UpdateConfig{ - Parallelism: ptr(uint64(3)), - Delay: types.Duration(10 * time.Second), - FailureAction: "continue", - Monitor: types.Duration(60 * time.Second), - MaxFailureRatio: 0.3, - Order: "start-first", - }, - UpdateConfig: &types.UpdateConfig{ - Parallelism: ptr(uint64(3)), - Delay: types.Duration(10 * time.Second), - FailureAction: "continue", - Monitor: types.Duration(60 * time.Second), - MaxFailureRatio: 0.3, - Order: "start-first", - }, - Resources: types.Resources{ - Limits: &types.Resource{ - NanoCPUs: 0.001, - MemoryBytes: 50 * 1024 * 1024, - }, - Reservations: &types.Resource{ - NanoCPUs: 0.0001, - MemoryBytes: 20 * 1024 * 1024, - GenericResources: []types.GenericResource{ - { - DiscreteResourceSpec: &types.DiscreteGenericResource{ - Kind: "gpu", - Value: 2, - }, - }, - { - DiscreteResourceSpec: &types.DiscreteGenericResource{ - Kind: "ssd", - Value: 1, - }, - }, - }, - }, - }, - RestartPolicy: &types.RestartPolicy{ - Condition: types.RestartPolicyOnFailure, - Delay: ptr(types.Duration(5 * time.Second)), - MaxAttempts: ptr(uint64(3)), - Window: ptr(types.Duration(2 * time.Minute)), - }, - Placement: types.Placement{ - Constraints: []string{"node=foo"}, - MaxReplicas: uint64(5), - Preferences: []types.PlacementPreferences{ - { - Spread: "node.labels.az", - }, - }, - }, - EndpointMode: "dnsrr", - }, - DeviceCgroupRules: []string{ - "c 1:3 mr", - "a 7:* rmw", - }, - Devices: []types.DeviceMapping{ - { - Source: "/dev/ttyUSB0", Target: "/dev/ttyUSB0", Permissions: "rwm", - }, - }, - DNS: []string{"8.8.8.8", "9.9.9.9"}, - DNSSearch: []string{"dc1.example.com", "dc2.example.com"}, - DomainName: "foo.com", - Entrypoint: []string{"/code/entrypoint.sh", "-p", "3000"}, - Environment: map[string]*string{ - "FOO": strPtr("foo_from_env_file"), - "BAR": strPtr("bar_from_env_file_2"), - "BAZ": strPtr("baz_from_service_def"), - "QUX": strPtr("qux_from_environment"), - "ENV.WITH.DOT": strPtr("ok"), - "ENV_WITH_UNDERSCORE": strPtr("ok"), - }, - EnvFiles: []types.EnvFile{ - { - Path: filepath.Join(workingDir, "example1.env"), - Required: true, - }, - { - Path: filepath.Join(workingDir, "example2.env"), - Required: false, - }, - }, - Expose: []string{"3000", "8000"}, - ExternalLinks: []string{ - "redis_1", - "project_db_1:mysql", - "project_db_1:postgresql", - }, - ExtraHosts: types.HostsList{ - "somehost": []string{"162.242.195.82"}, - "otherhost": []string{"50.31.209.229"}, - }, - Extensions: map[string]interface{}{ - "x-bar": "baz", - "x-foo": "bar", - }, - HealthCheck: &types.HealthCheckConfig{ - Test: types.HealthCheckTest([]string{"CMD-SHELL", "echo \"hello world\""}), - Interval: ptr(types.Duration(10 * time.Second)), - Timeout: ptr(types.Duration(1 * time.Second)), - Retries: ptr(uint64(5)), - StartPeriod: ptr(types.Duration(15 * time.Second)), - StartInterval: ptr(types.Duration(5 * time.Second)), - }, - Hostname: "foo", - Image: "redis", - Ipc: "host", - Uts: "host", - Labels: map[string]string{ - "FOO": "foo_from_label_file", - "BAR": "bar_from_label_file_2", - "BAZ": "baz_from_label_file", - "QUX": "quz_from_label_file_2", - "LABEL.WITH.DOT": "ok", - "LABEL_WITH_UNDERSCORE": "ok", - "com.example.description": "Accounting webapp", - "com.example.number": "42", - "com.example.empty-label": "", - }, - LabelFiles: []string{ - filepath.Join(workingDir, "example1.label"), - filepath.Join(workingDir, "example2.label"), - }, - Links: []string{ - "db", - "db:database", - "redis", - }, - Logging: &types.LoggingConfig{ - Driver: "syslog", - Options: map[string]string{ - "syslog-address": "tcp://192.168.0.42:123", - }, - }, - MacAddress: "02:42:ac:11:65:43", - NetworkMode: "container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b", - Networks: map[string]*types.ServiceNetworkConfig{ - "some-network": { - Aliases: []string{"alias1", "alias3"}, - Ipv4Address: "", - Ipv6Address: "", - }, - "other-network": { - Ipv4Address: "172.16.238.10", - Ipv6Address: "2001:3984:3989::10", - MacAddress: "02:42:72:98:65:08", - }, - "other-other-network": nil, - }, - Pid: "host", - Ports: []types.ServicePortConfig{ - // "3000", - { - Mode: "ingress", - Target: 3000, - Protocol: "tcp", - }, - { - Mode: "ingress", - Target: 3001, - Protocol: "tcp", - }, - { - Mode: "ingress", - Target: 3002, - Protocol: "tcp", - }, - { - Mode: "ingress", - Target: 3003, - Protocol: "tcp", - }, - { - Mode: "ingress", - Target: 3004, - Protocol: "tcp", - }, - { - Mode: "ingress", - Target: 3005, - Protocol: "tcp", - }, - // "8000:8000", - { - Mode: "ingress", - Target: 8000, - Published: "8000", - Protocol: "tcp", - }, - // "9090-9091:8080-8081", - { - Mode: "ingress", - Target: 8080, - Published: "9090", - Protocol: "tcp", - }, - { - Mode: "ingress", - Target: 8081, - Published: "9091", - Protocol: "tcp", - }, - // "49100:22", - { - Mode: "ingress", - Target: 22, - Published: "49100", - Protocol: "tcp", - }, - // "127.0.0.1:8001:8001", - { - Mode: "ingress", - HostIP: "127.0.0.1", - Target: 8001, - Published: "8001", - Protocol: "tcp", - }, - // "127.0.0.1:5000-5010:5000-5010", - { - Mode: "ingress", - HostIP: "127.0.0.1", - Target: 5000, - Published: "5000", - Protocol: "tcp", - }, - { - Mode: "ingress", - HostIP: "127.0.0.1", - Target: 5001, - Published: "5001", - Protocol: "tcp", - }, - { - Mode: "ingress", - HostIP: "127.0.0.1", - Target: 5002, - Published: "5002", - Protocol: "tcp", - }, - { - Mode: "ingress", - HostIP: "127.0.0.1", - Target: 5003, - Published: "5003", - Protocol: "tcp", - }, - { - Mode: "ingress", - HostIP: "127.0.0.1", - Target: 5004, - Published: "5004", - Protocol: "tcp", - }, - { - Mode: "ingress", - HostIP: "127.0.0.1", - Target: 5005, - Published: "5005", - Protocol: "tcp", - }, - { - Mode: "ingress", - HostIP: "127.0.0.1", - Target: 5006, - Published: "5006", - Protocol: "tcp", - }, - { - Mode: "ingress", - HostIP: "127.0.0.1", - Target: 5007, - Published: "5007", - Protocol: "tcp", - }, - { - Mode: "ingress", - HostIP: "127.0.0.1", - Target: 5008, - Published: "5008", - Protocol: "tcp", - }, - { - Mode: "ingress", - HostIP: "127.0.0.1", - Target: 5009, - Published: "5009", - Protocol: "tcp", - }, - { - Mode: "ingress", - HostIP: "127.0.0.1", - Target: 5010, - Published: "5010", - Protocol: "tcp", - }, - }, - Privileged: true, - ReadOnly: true, - Restart: types.RestartPolicyAlways, - Secrets: []types.ServiceSecretConfig{ - { - Source: "secret1", - Target: "/run/secrets/secret1", - }, - { - Source: "secret2", - Target: "my_secret", - UID: "103", - GID: "103", - Mode: ptr(types.FileMode(0o440)), - }, - }, - SecurityOpt: []string{ - "label=level:s0:c100,c200", - "label=type:svirt_apache_t", - }, - StdinOpen: true, - StopSignal: "SIGUSR1", - StorageOpt: map[string]string{"size": "20G"}, - StopGracePeriod: ptr(types.Duration(20 * time.Second)), - Sysctls: map[string]string{ - "net.core.somaxconn": "1024", - "net.ipv4.tcp_syncookies": "0", - }, - Tmpfs: []string{"/run", "/tmp"}, - Tty: true, - Ulimits: map[string]*types.UlimitsConfig{ - "nproc": { - Single: 65535, - }, - "nofile": { - Soft: 20000, - Hard: 40000, - }, - }, - User: "someone", - Volumes: []types.ServiceVolumeConfig{ - {Target: "/var/lib/anonymous", Type: "volume", Volume: &types.ServiceVolumeVolume{}}, - {Source: "/opt/data", Target: "/var/lib/data", Type: "bind", Bind: &types.ServiceVolumeBind{CreateHostPath: true}}, - {Source: workingDir, Target: "/code", Type: "bind", Bind: &types.ServiceVolumeBind{CreateHostPath: true}}, - {Source: filepath.Join(workingDir, "static"), Target: "/var/www/html", Type: "bind", Bind: &types.ServiceVolumeBind{CreateHostPath: true}}, - {Source: filepath.Join(homeDir, "configs"), Target: "/etc/configs", Type: "bind", ReadOnly: true, Bind: &types.ServiceVolumeBind{CreateHostPath: true}}, - {Source: "datavolume", Target: "/var/lib/volume", Type: "volume", Volume: &types.ServiceVolumeVolume{}}, - {Source: filepath.Join(workingDir, "opt"), Target: "/opt/cached", Consistency: "cached", Type: "bind"}, - {Target: "/opt/tmpfs", Type: "tmpfs", Tmpfs: &types.ServiceVolumeTmpfs{ - Size: types.UnitBytes(10000), - }}, - }, - WorkingDir: "/code", - }, - "bar": { - Name: "bar", - Build: &types.BuildConfig{ - Context: workingDir, - DockerfileInline: "FROM alpine\nRUN echo \"hello\" > /world.txt\n", - }, - Environment: types.MappingWithEquals{}, - }, - } -} - -func networks() map[string]types.NetworkConfig { - return map[string]types.NetworkConfig{ - "some-network": {}, - - "other-network": { - Driver: "overlay", - DriverOpts: map[string]string{ - "foo": "bar", - "baz": "1", - }, - Ipam: types.IPAMConfig{ - Driver: "overlay", - Config: []*types.IPAMPool{ - { - Subnet: "172.28.0.0/16", - IPRange: "172.28.5.0/24", - Gateway: "172.28.5.254", - AuxiliaryAddresses: map[string]string{ - "host1": "172.28.1.5", - "host2": "172.28.1.6", - "host3": "172.28.1.7", - }, - }, - { - Subnet: "2001:3984:3989::/64", - Gateway: "2001:3984:3989::1", - }, - }, - }, - Labels: map[string]string{ - "foo": "bar", - }, - }, - - "external-network": { - External: true, - }, - - "other-external-network": { - Name: "my-cool-network", - External: true, - Extensions: map[string]interface{}{ - "x-bar": "baz", - "x-foo": "bar", - }, - }, - } -} - -func volumes() map[string]types.VolumeConfig { - return map[string]types.VolumeConfig{ - "some-volume": {}, - "other-volume": { - Driver: "flocker", - DriverOpts: map[string]string{ - "foo": "bar", - "baz": "1", - }, - Labels: map[string]string{ - "foo": "bar", - }, - }, - "another-volume": { - Name: "user_specified_name", - Driver: "vsphere", - DriverOpts: map[string]string{ - "foo": "bar", - "baz": "1", - }, - }, - "external-volume": { - External: true, - }, - "other-external-volume": { - Name: "my-cool-volume", - External: true, - }, - "external-volume3": { - Name: "this-is-volume3", - External: true, - Extensions: map[string]interface{}{ - "x-bar": "baz", - "x-foo": "bar", - }, - }, - } -} - -func configs(workingDir string, homeDir string) map[string]types.ConfigObjConfig { - return map[string]types.ConfigObjConfig{ - "config1": { - File: filepath.Join(workingDir, "config_data"), - Labels: map[string]string{ - "foo": "bar", - }, - }, - "config2": { - Name: "my_config", - External: true, - }, - "config3": { - External: true, - }, - "config4": { - Name: "foo", - File: filepath.Join(homeDir, "config_data"), - Extensions: map[string]interface{}{ - "x-bar": "baz", - "x-foo": "bar", - }, - }, - } -} - -func secrets(workingDir string) map[string]types.SecretConfig { - return map[string]types.SecretConfig{ - "secret1": { - File: filepath.Join(workingDir, "secret_data"), - Labels: map[string]string{ - "foo": "bar", - }, - }, - "secret2": { - Name: "my_secret", - External: true, - }, - "secret3": { - External: true, - }, - "secret4": { - Name: "bar", - Environment: "BAR", - Content: "this is a secret", - Extensions: map[string]interface{}{ - "x-bar": "baz", - "x-foo": "bar", - }, - }, - "secret5": { - File: "/abs/secret_data", - }, - } -} - -func fullExampleYAML(workingDir, homeDir string) string { - return fmt.Sprintf(`name: full_example_project_name -services: - bar: - build: - context: %s - dockerfile_inline: | - FROM alpine - RUN echo "hello" > /world.txt - foo: - annotations: - com.example.foo: bar - build: - context: %s - dockerfile: Dockerfile - args: - foo: bar - ssh: - - default - labels: - FOO: BAR - cache_from: - - foo - - bar - additional_contexts: - foo: %s - network: foo - target: foo - secrets: - - source: secret1 - target: /run/secrets/secret1 - - source: secret2 - target: my_secret - uid: "103" - gid: "103" - mode: "0440" - tags: - - foo:v1.0.0 - - docker.io/username/foo:my-other-tag - - full_example_project_name:1.0.0 - platforms: - - linux/amd64 - - linux/arm64 - cap_add: - - ALL - cap_drop: - - NET_ADMIN - - SYS_ADMIN - cgroup_parent: m-executor-abcd - command: - - bundle - - exec - - thin - - -p - - "3000" - configs: - - source: config1 - - source: config2 - target: /my_config - uid: "103" - gid: "103" - mode: "0440" - container_name: my-web-container - depends_on: - db: - condition: service_started - required: true - redis: - condition: service_started - required: true - deploy: - mode: replicated - replicas: 6 - labels: - FOO: BAR - update_config: - parallelism: 3 - delay: 10s - failure_action: continue - monitor: 1m0s - max_failure_ratio: 0.3 - order: start-first - rollback_config: - parallelism: 3 - delay: 10s - failure_action: continue - monitor: 1m0s - max_failure_ratio: 0.3 - order: start-first - resources: - limits: - cpus: 0.001 - memory: "52428800" - reservations: - cpus: 0.0001 - memory: "20971520" - generic_resources: - - discrete_resource_spec: - kind: gpu - value: 2 - - discrete_resource_spec: - kind: ssd - value: 1 - restart_policy: - condition: on-failure - delay: 5s - max_attempts: 3 - window: 2m0s - placement: - constraints: - - node=foo - preferences: - - spread: node.labels.az - max_replicas_per_node: 5 - endpoint_mode: dnsrr - device_cgroup_rules: - - c 1:3 mr - - a 7:* rmw - devices: - - source: /dev/ttyUSB0 - target: /dev/ttyUSB0 - permissions: rwm - dns: - - 8.8.8.8 - - 9.9.9.9 - dns_search: - - dc1.example.com - - dc2.example.com - domainname: foo.com - entrypoint: - - /code/entrypoint.sh - - -p - - "3000" - environment: - BAR: bar_from_env_file_2 - BAZ: baz_from_service_def - ENV.WITH.DOT: ok - ENV_WITH_UNDERSCORE: ok - FOO: foo_from_env_file - QUX: qux_from_environment - env_file: - - path: %s - - path: %s - required: false - expose: - - "3000" - - "8000" - external_links: - - redis_1 - - project_db_1:mysql - - project_db_1:postgresql - extra_hosts: - - otherhost=50.31.209.229 - - somehost=162.242.195.82 - hostname: foo - healthcheck: - test: - - CMD-SHELL - - echo "hello world" - timeout: 1s - interval: 10s - retries: 5 - start_period: 15s - start_interval: 5s - image: redis - ipc: host - labels: - BAR: bar_from_label_file_2 - BAZ: baz_from_label_file - FOO: foo_from_label_file - LABEL.WITH.DOT: ok - LABEL_WITH_UNDERSCORE: ok - QUX: quz_from_label_file_2 - com.example.description: Accounting webapp - com.example.empty-label: "" - com.example.number: "42" - label_file: - - %s - - %s - links: - - db - - db:database - - redis - logging: - driver: syslog - options: - syslog-address: tcp://192.168.0.42:123 - mac_address: 02:42:ac:11:65:43 - network_mode: container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b - networks: - other-network: - ipv4_address: 172.16.238.10 - ipv6_address: 2001:3984:3989::10 - mac_address: 02:42:72:98:65:08 - other-other-network: null - some-network: - aliases: - - alias1 - - alias3 - pid: host - ports: - - mode: ingress - target: 3000 - protocol: tcp - - mode: ingress - target: 3001 - protocol: tcp - - mode: ingress - target: 3002 - protocol: tcp - - mode: ingress - target: 3003 - protocol: tcp - - mode: ingress - target: 3004 - protocol: tcp - - mode: ingress - target: 3005 - protocol: tcp - - mode: ingress - target: 8000 - published: "8000" - protocol: tcp - - mode: ingress - target: 8080 - published: "9090" - protocol: tcp - - mode: ingress - target: 8081 - published: "9091" - protocol: tcp - - mode: ingress - target: 22 - published: "49100" - protocol: tcp - - mode: ingress - host_ip: 127.0.0.1 - target: 8001 - published: "8001" - protocol: tcp - - mode: ingress - host_ip: 127.0.0.1 - target: 5000 - published: "5000" - protocol: tcp - - mode: ingress - host_ip: 127.0.0.1 - target: 5001 - published: "5001" - protocol: tcp - - mode: ingress - host_ip: 127.0.0.1 - target: 5002 - published: "5002" - protocol: tcp - - mode: ingress - host_ip: 127.0.0.1 - target: 5003 - published: "5003" - protocol: tcp - - mode: ingress - host_ip: 127.0.0.1 - target: 5004 - published: "5004" - protocol: tcp - - mode: ingress - host_ip: 127.0.0.1 - target: 5005 - published: "5005" - protocol: tcp - - mode: ingress - host_ip: 127.0.0.1 - target: 5006 - published: "5006" - protocol: tcp - - mode: ingress - host_ip: 127.0.0.1 - target: 5007 - published: "5007" - protocol: tcp - - mode: ingress - host_ip: 127.0.0.1 - target: 5008 - published: "5008" - protocol: tcp - - mode: ingress - host_ip: 127.0.0.1 - target: 5009 - published: "5009" - protocol: tcp - - mode: ingress - host_ip: 127.0.0.1 - target: 5010 - published: "5010" - protocol: tcp - privileged: true - read_only: true - restart: always - secrets: - - source: secret1 - target: /run/secrets/secret1 - - source: secret2 - target: my_secret - uid: "103" - gid: "103" - mode: "0440" - security_opt: - - label=level:s0:c100,c200 - - label=type:svirt_apache_t - stdin_open: true - stop_grace_period: 20s - stop_signal: SIGUSR1 - storage_opt: - size: 20G - sysctls: - net.core.somaxconn: "1024" - net.ipv4.tcp_syncookies: "0" - tmpfs: - - /run - - /tmp - tty: true - ulimits: - nofile: - soft: 20000 - hard: 40000 - nproc: 65535 - user: someone - uts: host - volumes: - - type: volume - target: /var/lib/anonymous - volume: {} - - type: bind - source: /opt/data - target: /var/lib/data - bind: {} - - type: bind - source: %s - target: /code - bind: {} - - type: bind - source: %s - target: /var/www/html - bind: {} - - type: bind - source: %s - target: /etc/configs - read_only: true - bind: {} - - type: volume - source: datavolume - target: /var/lib/volume - volume: {} - - type: bind - source: %s - target: /opt/cached - consistency: cached - - type: tmpfs - target: /opt/tmpfs - tmpfs: - size: "10000" - working_dir: /code - x-bar: baz - x-foo: bar -networks: - external-network: - external: true - other-external-network: - name: my-cool-network - external: true - x-bar: baz - x-foo: bar - other-network: - driver: overlay - driver_opts: - baz: "1" - foo: bar - ipam: - driver: overlay - config: - - subnet: 172.28.0.0/16 - gateway: 172.28.5.254 - ip_range: 172.28.5.0/24 - aux_addresses: - host1: 172.28.1.5 - host2: 172.28.1.6 - host3: 172.28.1.7 - - subnet: 2001:3984:3989::/64 - gateway: 2001:3984:3989::1 - labels: - foo: bar - some-network: {} -volumes: - another-volume: - name: user_specified_name - driver: vsphere - driver_opts: - baz: "1" - foo: bar - external-volume: - external: true - external-volume3: - name: this-is-volume3 - external: true - x-bar: baz - x-foo: bar - other-external-volume: - name: my-cool-volume - external: true - other-volume: - driver: flocker - driver_opts: - baz: "1" - foo: bar - labels: - foo: bar - some-volume: {} -secrets: - secret1: - file: %s - labels: - foo: bar - secret2: - name: my_secret - external: true - secret3: - external: true - secret4: - name: bar - environment: BAR - x-bar: baz - x-foo: bar - secret5: - file: /abs/secret_data -configs: - config1: - file: %s - labels: - foo: bar - config2: - name: my_config - external: true - config3: - external: true - config4: - name: foo - file: %s - x-bar: baz - x-foo: bar -x-bar: baz -x-foo: bar -x-nested: - bar: baz - foo: bar -`, - workingDir, - filepath.Join(workingDir, "dir"), - filepath.Join(workingDir, "bar"), - filepath.Join(workingDir, "example1.env"), - filepath.Join(workingDir, "example2.env"), - filepath.Join(workingDir, "example1.label"), - filepath.Join(workingDir, "example2.label"), - workingDir, - filepath.Join(workingDir, "static"), - filepath.Join(homeDir, "configs"), - filepath.Join(workingDir, "opt"), - filepath.Join(workingDir, "secret_data"), - filepath.Join(workingDir, "config_data"), - filepath.Join(homeDir, "config_data")) -} - -func fullExampleJSON(workingDir, homeDir string) string { - return fmt.Sprintf(`{ - "configs": { - "config1": { - "file": "%s", - "labels": { - "foo": "bar" - } - }, - "config2": { - "name": "my_config", - "external": true - }, - "config3": { - "external": true - }, - "config4": { - "name": "foo", - "file": "%s" - } - }, - "name": "full_example_project_name", - "networks": { - "external-network": { - "ipam": {}, - "external": true - }, - "other-external-network": { - "name": "my-cool-network", - "ipam": {}, - "external": true - }, - "other-network": { - "driver": "overlay", - "driver_opts": { - "baz": "1", - "foo": "bar" - }, - "ipam": { - "driver": "overlay", - "config": [ - { - "subnet": "172.28.0.0/16", - "gateway": "172.28.5.254", - "ip_range": "172.28.5.0/24", - "aux_addresses": { - "host1": "172.28.1.5", - "host2": "172.28.1.6", - "host3": "172.28.1.7" - } - }, - { - "subnet": "2001:3984:3989::/64", - "gateway": "2001:3984:3989::1" - } - ] - }, - "labels": { - "foo": "bar" - } - }, - "some-network": { - "ipam": {} - } - }, - "secrets": { - "secret1": { - "file": "%s", - "labels": { - "foo": "bar" - } - }, - "secret2": { - "name": "my_secret", - "external": true - }, - "secret3": { - "external": true - }, - "secret4": { - "name": "bar", - "environment": "BAR" - }, - "secret5": { - "file": "/abs/secret_data" - } - }, - "services": { - "bar": { - "build": { - "context": "%s", - "dockerfile_inline": "FROM alpine\nRUN echo \"hello\" \u003e /world.txt\n" - }, - "command": null, - "entrypoint": null - }, - "foo": { - "annotations": { - "com.example.foo": "bar" - }, - "build": { - "context": "%s", - "dockerfile": "Dockerfile", - "args": { - "foo": "bar" - }, - "ssh": [ - "default" - ], - "labels": { - "FOO": "BAR" - }, - "cache_from": [ - "foo", - "bar" - ], - "additional_contexts": { - "foo": "%s" - }, - "network": "foo", - "target": "foo", - "secrets": [ - { - "source": "secret1", - "target": "/run/secrets/secret1" - }, - { - "source": "secret2", - "target": "my_secret", - "uid": "103", - "gid": "103", - "mode": "0440" - } - ], - "tags": [ - "foo:v1.0.0", - "docker.io/username/foo:my-other-tag", - "full_example_project_name:1.0.0" - ], - "platforms": [ - "linux/amd64", - "linux/arm64" - ] - }, - "cap_add": [ - "ALL" - ], - "cap_drop": [ - "NET_ADMIN", - "SYS_ADMIN" - ], - "cgroup_parent": "m-executor-abcd", - "command": [ - "bundle", - "exec", - "thin", - "-p", - "3000" - ], - "configs": [ - { - "source": "config1" - }, - { - "source": "config2", - "target": "/my_config", - "uid": "103", - "gid": "103", - "mode": "0440" - } - ], - "container_name": "my-web-container", - "depends_on": { - "db": { - "condition": "service_started", - "required": true - }, - "redis": { - "condition": "service_started", - "required": true - } - }, - "deploy": { - "mode": "replicated", - "replicas": 6, - "labels": { - "FOO": "BAR" - }, - "update_config": { - "parallelism": 3, - "delay": "10s", - "failure_action": "continue", - "monitor": "1m0s", - "max_failure_ratio": 0.3, - "order": "start-first" - }, - "rollback_config": { - "parallelism": 3, - "delay": "10s", - "failure_action": "continue", - "monitor": "1m0s", - "max_failure_ratio": 0.3, - "order": "start-first" - }, - "resources": { - "limits": { - "cpus": 0.001, - "memory": "52428800" - }, - "reservations": { - "cpus": 0.0001, - "memory": "20971520", - "generic_resources": [ - { - "discrete_resource_spec": { - "kind": "gpu", - "value": 2 - } - }, - { - "discrete_resource_spec": { - "kind": "ssd", - "value": 1 - } - } - ] - } - }, - "restart_policy": { - "condition": "on-failure", - "delay": "5s", - "max_attempts": 3, - "window": "2m0s" - }, - "placement": { - "constraints": [ - "node=foo" - ], - "preferences": [ - { - "spread": "node.labels.az" - } - ], - "max_replicas_per_node": 5 - }, - "endpoint_mode": "dnsrr" - }, - "device_cgroup_rules": [ - "c 1:3 mr", - "a 7:* rmw" - ], - "devices": [ - { - "source": "/dev/ttyUSB0", - "target": "/dev/ttyUSB0", - "permissions": "rwm" - } - ], - "dns": [ - "8.8.8.8", - "9.9.9.9" - ], - "dns_search": [ - "dc1.example.com", - "dc2.example.com" - ], - "domainname": "foo.com", - "entrypoint": [ - "/code/entrypoint.sh", - "-p", - "3000" - ], - "environment": { - "BAR": "bar_from_env_file_2", - "BAZ": "baz_from_service_def", - "ENV.WITH.DOT": "ok", - "ENV_WITH_UNDERSCORE": "ok", - "FOO": "foo_from_env_file", - "QUX": "qux_from_environment" - }, - "env_file": [ - { - "path": "%s" - }, - { - "path": "%s", - "required": false - } - ], - "expose": [ - "3000", - "8000" - ], - "external_links": [ - "redis_1", - "project_db_1:mysql", - "project_db_1:postgresql" - ], - "extra_hosts": [ - "otherhost=50.31.209.229", - "somehost=162.242.195.82" - ], - "hostname": "foo", - "healthcheck": { - "test": [ - "CMD-SHELL", - "echo \"hello world\"" - ], - "timeout": "1s", - "interval": "10s", - "retries": 5, - "start_period": "15s", - "start_interval": "5s" - }, - "image": "redis", - "ipc": "host", - "labels": { - "BAR": "bar_from_label_file_2", - "BAZ": "baz_from_label_file", - "FOO": "foo_from_label_file", - "LABEL.WITH.DOT": "ok", - "LABEL_WITH_UNDERSCORE": "ok", - "QUX": "quz_from_label_file_2", - "com.example.description": "Accounting webapp", - "com.example.empty-label": "", - "com.example.number": "42" - }, - "label_file": [ - "%s", - "%s" - ], - "links": [ - "db", - "db:database", - "redis" - ], - "logging": { - "driver": "syslog", - "options": { - "syslog-address": "tcp://192.168.0.42:123" - } - }, - "mac_address": "02:42:ac:11:65:43", - "network_mode": "container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b", - "networks": { - "other-network": { - "ipv4_address": "172.16.238.10", - "ipv6_address": "2001:3984:3989::10", - "mac_address": "02:42:72:98:65:08" - }, - "other-other-network": null, - "some-network": { - "aliases": [ - "alias1", - "alias3" - ] - } - }, - "pid": "host", - "ports": [ - { - "mode": "ingress", - "target": 3000, - "protocol": "tcp" - }, - { - "mode": "ingress", - "target": 3001, - "protocol": "tcp" - }, - { - "mode": "ingress", - "target": 3002, - "protocol": "tcp" - }, - { - "mode": "ingress", - "target": 3003, - "protocol": "tcp" - }, - { - "mode": "ingress", - "target": 3004, - "protocol": "tcp" - }, - { - "mode": "ingress", - "target": 3005, - "protocol": "tcp" - }, - { - "mode": "ingress", - "target": 8000, - "published": "8000", - "protocol": "tcp" - }, - { - "mode": "ingress", - "target": 8080, - "published": "9090", - "protocol": "tcp" - }, - { - "mode": "ingress", - "target": 8081, - "published": "9091", - "protocol": "tcp" - }, - { - "mode": "ingress", - "target": 22, - "published": "49100", - "protocol": "tcp" - }, - { - "mode": "ingress", - "host_ip": "127.0.0.1", - "target": 8001, - "published": "8001", - "protocol": "tcp" - }, - { - "mode": "ingress", - "host_ip": "127.0.0.1", - "target": 5000, - "published": "5000", - "protocol": "tcp" - }, - { - "mode": "ingress", - "host_ip": "127.0.0.1", - "target": 5001, - "published": "5001", - "protocol": "tcp" - }, - { - "mode": "ingress", - "host_ip": "127.0.0.1", - "target": 5002, - "published": "5002", - "protocol": "tcp" - }, - { - "mode": "ingress", - "host_ip": "127.0.0.1", - "target": 5003, - "published": "5003", - "protocol": "tcp" - }, - { - "mode": "ingress", - "host_ip": "127.0.0.1", - "target": 5004, - "published": "5004", - "protocol": "tcp" - }, - { - "mode": "ingress", - "host_ip": "127.0.0.1", - "target": 5005, - "published": "5005", - "protocol": "tcp" - }, - { - "mode": "ingress", - "host_ip": "127.0.0.1", - "target": 5006, - "published": "5006", - "protocol": "tcp" - }, - { - "mode": "ingress", - "host_ip": "127.0.0.1", - "target": 5007, - "published": "5007", - "protocol": "tcp" - }, - { - "mode": "ingress", - "host_ip": "127.0.0.1", - "target": 5008, - "published": "5008", - "protocol": "tcp" - }, - { - "mode": "ingress", - "host_ip": "127.0.0.1", - "target": 5009, - "published": "5009", - "protocol": "tcp" - }, - { - "mode": "ingress", - "host_ip": "127.0.0.1", - "target": 5010, - "published": "5010", - "protocol": "tcp" - } - ], - "privileged": true, - "read_only": true, - "restart": "always", - "secrets": [ - { - "source": "secret1", - "target": "/run/secrets/secret1" - }, - { - "source": "secret2", - "target": "my_secret", - "uid": "103", - "gid": "103", - "mode": "0440" - } - ], - "security_opt": [ - "label=level:s0:c100,c200", - "label=type:svirt_apache_t" - ], - "stdin_open": true, - "stop_grace_period": "20s", - "stop_signal": "SIGUSR1", - "storage_opt": { - "size": "20G" - }, - "sysctls": { - "net.core.somaxconn": "1024", - "net.ipv4.tcp_syncookies": "0" - }, - "tmpfs": [ - "/run", - "/tmp" - ], - "tty": true, - "ulimits": { - "nofile": { - "soft": 20000, - "hard": 40000 - }, - "nproc": 65535 - }, - "user": "someone", - "uts": "host", - "volumes": [ - { - "type": "volume", - "target": "/var/lib/anonymous", - "volume": {} - }, - { - "type": "bind", - "source": "/opt/data", - "target": "/var/lib/data", - "bind": {} - }, - { - "type": "bind", - "source": "%s", - "target": "/code", - "bind": {} - }, - { - "type": "bind", - "source": "%s", - "target": "/var/www/html", - "bind": {} - }, - { - "type": "bind", - "source": "%s", - "target": "/etc/configs", - "read_only": true, - "bind": {} - }, - { - "type": "volume", - "source": "datavolume", - "target": "/var/lib/volume", - "volume": {} - }, - { - "type": "bind", - "source": "%s", - "target": "/opt/cached", - "consistency": "cached" - }, - { - "type": "tmpfs", - "target": "/opt/tmpfs", - "tmpfs": { - "size": "10000" - } - } - ], - "working_dir": "/code" - } - }, - "volumes": { - "another-volume": { - "name": "user_specified_name", - "driver": "vsphere", - "driver_opts": { - "baz": "1", - "foo": "bar" - } - }, - "external-volume": { - "external": true - }, - "external-volume3": { - "name": "this-is-volume3", - "external": true - }, - "other-external-volume": { - "name": "my-cool-volume", - "external": true - }, - "other-volume": { - "driver": "flocker", - "driver_opts": { - "baz": "1", - "foo": "bar" - }, - "labels": { - "foo": "bar" - } - }, - "some-volume": {} - }, - "x-bar": "baz", - "x-foo": "bar", - "x-nested": { - "bar": "baz", - "foo": "bar" - } -}`, - toPath(workingDir, "config_data"), - toPath(homeDir, "config_data"), - toPath(workingDir, "secret_data"), - toPath(workingDir), - toPath(workingDir, "dir"), - toPath(workingDir, "bar"), - toPath(workingDir, "example1.env"), - toPath(workingDir, "example2.env"), - toPath(workingDir, "example1.label"), - toPath(workingDir, "example2.label"), - toPath(workingDir), - toPath(workingDir, "static"), - toPath(homeDir, "configs"), - toPath(workingDir, "opt")) -} - -func toPath(path ...string) string { - return strings.ReplaceAll(filepath.Join(path...), "\\", "\\\\") -} diff --git a/loader/loader_test.go b/loader/loader_test.go index 20fd147f..89c04f46 100644 --- a/loader/loader_test.go +++ b/loader/loader_test.go @@ -32,7 +32,6 @@ import ( is "gotest.tools/v3/assert/cmp" "github.com/compose-spec/compose-go/v2/types" - "github.com/compose-spec/compose-go/v2/utils" ) func buildConfigDetails(yaml string, env map[string]string) types.ConfigDetails { @@ -1127,34 +1126,6 @@ func ptr[T any](t T) *T { return &t } -func TestFullExample(t *testing.T) { - b, err := os.ReadFile("full-example.yml") - assert.NilError(t, err) - - homeDir, err := os.UserHomeDir() - assert.NilError(t, err) - env := map[string]string{ - "HOME": homeDir, - "BAR": "this is a secret", - "QUX": "qux_from_environment", - } - config, err := loadYAMLWithEnv(string(b), env) - assert.NilError(t, err) - - workingDir, err := os.Getwd() - assert.NilError(t, err) - - expectedConfig := fullExampleProject(workingDir, homeDir) - - assert.Check(t, is.DeepEqual(expectedConfig.Name, config.Name)) - assert.Check(t, is.DeepEqual(expectedConfig.Services, config.Services)) - assert.Check(t, is.DeepEqual(expectedConfig.Networks, config.Networks)) - assert.Check(t, is.DeepEqual(expectedConfig.Volumes, config.Volumes)) - assert.Check(t, is.DeepEqual(expectedConfig.Secrets, config.Secrets, cmpopts.IgnoreUnexported(types.SecretConfig{}))) - assert.Check(t, is.DeepEqual(expectedConfig.Configs, config.Configs, cmpopts.IgnoreUnexported(types.ConfigObjConfig{}))) - assert.Check(t, is.DeepEqual(expectedConfig.Extensions, config.Extensions)) -} - func TestLoadTmpfsVolume(t *testing.T) { for _, size := range []string{"1kb", "1024"} { yaml := fmt.Sprintf(` @@ -1369,32 +1340,6 @@ services: assert.ErrorContains(t, err, "services.tmpfs.volumes.0.tmpfs.size must be a integer") } -func TestLoadAttachableNetwork(t *testing.T) { - config, err := loadYAML(` -name: load-attachable-network -networks: - mynet1: - driver: overlay - attachable: true - mynet2: - driver: bridge -`) - assert.NilError(t, err) - - expected := types.Networks{ - "mynet1": { - Driver: "overlay", - Attachable: true, - }, - "mynet2": { - Driver: "bridge", - Attachable: false, - }, - } - - assert.Check(t, is.DeepEqual(expected, config.Networks)) -} - func TestLoadVolumeSubpath(t *testing.T) { config, err := loadYAML(` name: load-volume-subpath @@ -1667,392 +1612,6 @@ networks: assert.DeepEqual(t, config, expected, cmpopts.EquateEmpty()) } -func TestLoadIPv6Only(t *testing.T) { - config, err := loadYAML(` -name: load-network-ipv6only -services: - foo: - image: alpine - networks: - network1: -networks: - network1: - driver: bridge - enable_ipv4: false - enable_ipv6: true - name: network1 -`) - assert.NilError(t, err) - - workingDir, err := os.Getwd() - assert.NilError(t, err) - enableIPv4 := false - enableIPv6 := true - expected := &types.Project{ - Name: "load-network-ipv6only", - WorkingDir: workingDir, - Services: types.Services{ - "foo": { - Name: "foo", - Image: "alpine", - Networks: map[string]*types.ServiceNetworkConfig{ - "network1": nil, - }, - }, - }, - Networks: map[string]types.NetworkConfig{ - "network1": { - Name: "network1", - Driver: "bridge", - EnableIPv4: &enableIPv4, - EnableIPv6: &enableIPv6, - }, - }, - Environment: types.Mapping{ - "COMPOSE_PROJECT_NAME": "load-network-ipv6only", - }, - } - assert.DeepEqual(t, config, expected, cmpopts.EquateEmpty()) -} - -func TestLoadNetworkLinkLocalIPs(t *testing.T) { - config, err := loadYAML(` -name: load-network-link-local-ips -services: - foo: - image: alpine - networks: - network1: - ipv4_address: 10.1.0.100 - ipv6_address: 2001:db8:0:1::100 - link_local_ips: - - fe80::1:95ff:fe20:100 -networks: - network1: - driver: bridge - enable_ipv6: true - name: network1 - ipam: - config: - - subnet: 10.1.0.0/16 - - subnet: 2001:db8:0:1::/64 -`) - assert.NilError(t, err) - - workingDir, err := os.Getwd() - assert.NilError(t, err) - enableIPv6 := true - expected := &types.Project{ - Name: "load-network-link-local-ips", - WorkingDir: workingDir, - Services: types.Services{ - "foo": { - Name: "foo", - Image: "alpine", - Networks: map[string]*types.ServiceNetworkConfig{ - "network1": { - Ipv4Address: "10.1.0.100", - Ipv6Address: "2001:db8:0:1::100", - LinkLocalIPs: []string{ - "fe80::1:95ff:fe20:100", - }, - }, - }, - }, - }, - Networks: map[string]types.NetworkConfig{ - "network1": { - Name: "network1", - Driver: "bridge", - EnableIPv6: &enableIPv6, - Ipam: types.IPAMConfig{ - Config: []*types.IPAMPool{ - {Subnet: "10.1.0.0/16"}, - {Subnet: "2001:db8:0:1::/64"}, - }, - }, - }, - }, - Environment: types.Mapping{ - "COMPOSE_PROJECT_NAME": "load-network-link-local-ips", - }, - } - assert.DeepEqual(t, config, expected, cmpopts.EquateEmpty()) -} - -func TestLoadServiceNetworkDriverOpts(t *testing.T) { - config, err := loadYAML(` -name: load-service-network-driver-opts -services: - foo: - image: alpine - networks: - network1: - driver_opts: - com.docker.network.endpoint.sysctls: "ipv6.conf.accept_ra=0" -networks: - network1: -`) - assert.NilError(t, err) - - workingDir, err := os.Getwd() - assert.NilError(t, err) - expected := &types.Project{ - Name: "load-service-network-driver-opts", - WorkingDir: workingDir, - Services: types.Services{ - "foo": { - Name: "foo", - Image: "alpine", - Networks: map[string]*types.ServiceNetworkConfig{ - "network1": { - DriverOpts: types.Options{ - "com.docker.network.endpoint.sysctls": "ipv6.conf.accept_ra=0", - }, - }, - }, - }, - }, - Networks: map[string]types.NetworkConfig{ - "network1": {}, - }, - Environment: types.Mapping{ - "COMPOSE_PROJECT_NAME": "load-service-network-driver-opts", - }, - } - assert.DeepEqual(t, config, expected, cmpopts.EquateEmpty()) -} - -func TestLoadInit(t *testing.T) { - booleanTrue := true - booleanFalse := false - - testcases := []struct { - doc string - yaml string - init *bool - }{ - { - doc: "no init defined", - yaml: ` -name: no-init-defined -services: - foo: - image: alpine`, - }, - { - doc: "has true init", - yaml: ` -name: has-true-init -services: - foo: - image: alpine - init: true`, - init: &booleanTrue, - }, - { - doc: "has false init", - yaml: ` -name: has-false-init -services: - foo: - image: alpine - init: false`, - init: &booleanFalse, - }, - } - for _, testcase := range testcases { - t.Run(testcase.doc, func(t *testing.T) { - config, err := loadYAML(testcase.yaml) - assert.NilError(t, err) - assert.Check(t, is.Len(config.Services, 1)) - assert.Check(t, is.DeepEqual(config.Services["foo"].Init, testcase.init)) - }) - } -} - -func TestLoadSysctls(t *testing.T) { - config, err := loadYAML(` -name: load-sysctls -services: - web: - image: busybox - sysctls: - - net.core.somaxconn=1024 - - net.ipv4.tcp_syncookies=0 - - testing.one.one= - - testing.one.two -`) - assert.NilError(t, err) - - expected := types.Mapping{ - "net.core.somaxconn": "1024", - "net.ipv4.tcp_syncookies": "0", - "testing.one.one": "", - "testing.one.two": "", - } - - assert.Assert(t, is.Len(config.Services, 1)) - assert.Check(t, is.DeepEqual(expected, config.Services["web"].Sysctls)) - - config, err = loadYAML(` -name: load-sysctls -services: - web: - image: busybox - sysctls: - net.core.somaxconn: 1024 - net.ipv4.tcp_syncookies: 0 - testing.one.one: "" - testing.one.two: -`) - assert.NilError(t, err) - - assert.Assert(t, is.Len(config.Services, 1)) - assert.Check(t, is.DeepEqual(expected, config.Services["web"].Sysctls)) -} - -func TestLoadTemplateDriver(t *testing.T) { - config, err := loadYAML(` -name: load-template-driver -services: - hello-world: - image: redis:alpine - secrets: - - secret - configs: - - config - -configs: - config: - name: config - external: true - template_driver: config-driver - -secrets: - secret: - name: secret - external: true - template_driver: secret-driver -`) - assert.NilError(t, err) - - workingDir, err := os.Getwd() - assert.NilError(t, err) - - expected := &types.Project{ - Name: "load-template-driver", - WorkingDir: workingDir, - Services: types.Services{ - "hello-world": { - Name: "hello-world", - Image: "redis:alpine", - Configs: []types.ServiceConfigObjConfig{ - { - Source: "config", - }, - }, - Secrets: []types.ServiceSecretConfig{ - { - Source: "secret", - Target: "/run/secrets/secret", - }, - }, - }, - }, - Configs: map[string]types.ConfigObjConfig{ - "config": { - Name: "config", - External: true, - TemplateDriver: "config-driver", - }, - }, - Secrets: map[string]types.SecretConfig{ - "secret": { - Name: "secret", - External: true, - TemplateDriver: "secret-driver", - }, - }, - Environment: types.Mapping{ - "COMPOSE_PROJECT_NAME": "load-template-driver", - }, - } - assertEqual(t, expected, config) -} - -func TestLoadSecretDriver(t *testing.T) { - config, err := loadYAML(` -name: load-secret-driver -services: - hello-world: - image: redis:alpine - secrets: - - secret - configs: - - config - -configs: - config: - name: config - external: true - -secrets: - secret: - name: secret - driver: secret-bucket - driver_opts: - OptionA: value for driver option A - OptionB: value for driver option B -`) - assert.NilError(t, err) - - workingDir, err := os.Getwd() - assert.NilError(t, err) - - expected := &types.Project{ - Name: "load-secret-driver", - WorkingDir: workingDir, - Services: types.Services{ - "hello-world": { - Name: "hello-world", - Image: "redis:alpine", - Configs: []types.ServiceConfigObjConfig{ - { - Source: "config", - }, - }, - Secrets: []types.ServiceSecretConfig{ - { - Source: "secret", - Target: "/run/secrets/secret", - }, - }, - }, - }, - Configs: map[string]types.ConfigObjConfig{ - "config": { - Name: "config", - External: true, - }, - }, - Secrets: map[string]types.SecretConfig{ - "secret": { - Name: "secret", - Driver: "secret-bucket", - DriverOpts: map[string]string{ - "OptionA": "value for driver option A", - "OptionB": "value for driver option B", - }, - }, - }, - Environment: types.Mapping{ - "COMPOSE_PROJECT_NAME": "load-secret-driver", - }, - } - assertEqual(t, config, expected) -} - func assertEqual(t *testing.T, config *types.Project, expected *types.Project) { assert.DeepEqual(t, config, expected, cmpopts.EquateEmpty(), cmpopts.IgnoreUnexported(types.SecretConfig{}), cmpopts.IgnoreUnexported(types.ConfigObjConfig{})) } @@ -2268,32 +1827,6 @@ services: assert.ErrorContains(t, err, "missing property 'capabilities'") } -func TestServiceGpus(t *testing.T) { - p, err := loadYAML(` -name: service-gpus -services: - test: - image: redis:alpine - gpus: - - driver: nvidia - - driver: 3dfx - device_ids: ["voodoo2"] - capabilities: ["directX"] -`) - assert.NilError(t, err) - assert.DeepEqual(t, p.Services["test"].Gpus, []types.DeviceRequest{ - { - Driver: "nvidia", - Count: -1, - }, - { - Capabilities: []string{"directX"}, - Driver: "3dfx", - IDs: []string{"voodoo2"}, - }, - }) -} - func TestServicePullPolicy(t *testing.T) { actual, err := loadYAML(` name: service-pull-policy @@ -2614,44 +2147,6 @@ services: assert.Equal(t, project.Services["extension"].Extensions["x-foo"], "bar") } -func TestDeviceWriteBps(t *testing.T) { - p, err := loadYAML(` - name: test - services: - foo: - image: busybox - blkio_config: - device_read_bps: - - path: /dev/test - rate: 1024k - device_write_bps: - - path: /dev/test - rate: 1024 -`) - assert.NilError(t, err) - assert.DeepEqual(t, p.Services, types.Services{ - "foo": { - Name: "foo", - Image: "busybox", - Environment: types.MappingWithEquals{}, - BlkioConfig: &types.BlkioConfig{ - DeviceReadBps: []types.ThrottleDevice{ - { - Path: "/dev/test", - Rate: types.UnitBytes(1024 * 1024), - }, - }, - DeviceWriteBps: []types.ThrottleDevice{ - { - Path: "/dev/test", - Rate: types.UnitBytes(1024), - }, - }, - }, - }, - }) -} - func TestInvalidProjectNameType(t *testing.T) { p, err := loadYAML(`name: 123`) assert.Error(t, err, "validating filename0.yml: name must be a string") @@ -3495,54 +2990,6 @@ secrets: }, cmpopts.IgnoreUnexported(types.SecretConfig{})) } -func TestLoadDeviceMapping(t *testing.T) { - config, err := loadYAML(` -name: load-device-mapping -services: - test: - devices: - - /dev/source:/dev/target:permissions - - /dev/single -`) - assert.NilError(t, err) - assert.DeepEqual(t, config.Services["test"].Devices, []types.DeviceMapping{ - { - Source: "/dev/source", - Target: "/dev/target", - Permissions: "permissions", - }, - { - Source: "/dev/single", - Target: "/dev/single", - Permissions: "rwm", - }, - }) -} - -func TestLoadDeviceMappingLongSyntax(t *testing.T) { - config, err := loadYAML(` -name: load-device-mapping-long-syntax -services: - test: - devices: - - source: /dev/source - target: /dev/target - permissions: permissions - x-foo: bar -`) - assert.NilError(t, err) - assert.DeepEqual(t, config.Services["test"].Devices, []types.DeviceMapping{ - { - Source: "/dev/source", - Target: "/dev/target", - Permissions: "permissions", - Extensions: map[string]any{ - "x-foo": "bar", - }, - }, - }) -} - func TestLoadExtraHostsRepeated(t *testing.T) { p, err := loadYAML(` name: load-extra-hosts @@ -3596,80 +3043,6 @@ services: }) } -func TestLoadDeviceReservation(t *testing.T) { - config, err := loadYAML(` -name: load-device-reservation -services: - test: - deploy: - resources: - reservations: - devices: - - driver: richard_feynman - capabilities: ["quantic"] - count: all - options: - q_bits: 42 -`) - assert.NilError(t, err) - assert.DeepEqual(t, config.Services["test"].Deploy.Resources.Reservations.Devices, []types.DeviceRequest{ - { - Capabilities: []string{"quantic"}, - Driver: "richard_feynman", - Count: -1, - Options: types.Mapping{ - "q_bits": "42", - }, - }, - }) -} - -func TestLoadServiceHooks(t *testing.T) { - p, err := loadYAML(` -name: load-service-hooks -services: - test: - post_start: - - command: echo start - user: root - privileged: true - working_dir: / - environment: - - FOO=BAR - pre_stop: - - command: echo stop - user: root - working_dir: / - environment: - FOO: BAR - -`) - assert.NilError(t, err) - start := p.Services["test"].PostStart - assert.DeepEqual(t, start, []types.ServiceHook{ - { - Command: types.ShellCommand{"echo", "start"}, - User: "root", - Privileged: true, - WorkingDir: "/", - Environment: types.MappingWithEquals{ - "FOO": strPtr("BAR"), - }, - }, - }) - stop := p.Services["test"].PreStop - assert.DeepEqual(t, stop, []types.ServiceHook{ - { - Command: types.ShellCommand{"echo", "stop"}, - User: "root", - WorkingDir: "/", - Environment: types.MappingWithEquals{ - "FOO": strPtr("BAR"), - }, - }, - }) -} - func TestOmitEmptyDNS(t *testing.T) { p, err := loadYAML(` name: load-empty-dsn @@ -3681,45 +3054,6 @@ services: assert.Equal(t, len(p.Services["test"].DNS), 0) } -func TestAllGPUS(t *testing.T) { - p, err := loadYAML(` -name: load-all-gpus -services: - test: - gpus: all -`) - assert.NilError(t, err) - assert.Equal(t, len(p.Services["test"].Gpus), 1) - assert.Equal(t, p.Services["test"].Gpus[0].Count, types.DeviceCount(-1)) -} - -func TestGwPriority(t *testing.T) { - p, err := loadYAML(` -name: load-gw_priority -services: - test: - networks: - test: - gw_priority: 42 -`) - assert.NilError(t, err) - assert.Equal(t, p.Services["test"].Networks["test"].GatewayPriority, 42) -} - -func TestPullRefresh(t *testing.T) { - p, err := loadYAML(` -name: load-all-gpus -services: - test: - pull_policy: every_2d -`) - assert.NilError(t, err) - policy, duration, err := p.Services["test"].GetPullPolicy() - assert.NilError(t, err) - assert.Equal(t, policy, types.PullPolicyRefresh) - assert.Equal(t, duration, 2*24*time.Hour) -} - func TestEnvironmentWhitespace(t *testing.T) { _, err := loadYAML(` name: environment_whitespace @@ -3761,50 +3095,6 @@ services: assert.Equal(t, *p.Services["test"].Secrets[0].Mode, types.FileMode(0o440)) } -func TestServiceProvider(t *testing.T) { - p, err := loadYAML(` -name: service-provider -services: - test: - provider: - type: foo - options: - bar: zot - strings: - - foo - - bar - numbers: - - 12 - - 34 - booleans: - - true - - false -`) - assert.NilError(t, err) - assert.DeepEqual(t, p.Services["test"].Provider, &types.ServiceProviderConfig{ - Type: "foo", - Options: types.MultiOptions{ - "bar": []string{"zot"}, - "strings": []string{"foo", "bar"}, - "numbers": []string{"12", "34"}, - "booleans": []string{"true", "false"}, - }, - }) - - _, err = loadYAML(` -name: service-provider -services: - test: - provider: - options: - bar: zot - strings: foo - numbers: 12 - booleans: true -`) - assert.ErrorContains(t, err, "services.test.provider missing property 'type'") -} - func TestImageVolume(t *testing.T) { p, err := loadYAML(` name: imageVolume @@ -3847,61 +3137,6 @@ services: }) } -func TestInterfaceName(t *testing.T) { - p, err := loadYAML(` -name: interface-name -services: - test: - networks: - test: - interface_name: eth0 -`) - assert.NilError(t, err) - assert.DeepEqual(t, p.Services["test"].Networks["test"], &types.ServiceNetworkConfig{ - InterfaceName: "eth0", - }) -} - -func TestModel(t *testing.T) { - p, err := loadYAML(` -name: model -services: - test_array: - models: - - foo - - test_mapping: - models: - foo: - endpoint_var: MODEL_URL - model_var: MODEL - -models: - foo: - model: ai/model - context_size: 1024 - runtime_flags: - - "--some-flag" -`) - assert.NilError(t, err) - assert.DeepEqual(t, p.Models["foo"], types.ModelConfig{ - Model: "ai/model", - ContextSize: 1024, - RuntimeFlags: []string{"--some-flag"}, - }) - assert.DeepEqual(t, p.Services["test_array"].Models, map[string]*types.ServiceModelConfig{ - "foo": nil, - }) - assert.DeepEqual(t, p.Services["test_mapping"].Models, map[string]*types.ServiceModelConfig{ - "foo": { - EndpointVariable: "MODEL_URL", - ModelVariable: "MODEL", - }, - }) - assert.DeepEqual(t, p.ModelNames(), []string{"foo"}) - assert.Check(t, utils.ArrayContains(p.ServicesWithModels(), []string{"test_array", "test_mapping"}), p.ServicesWithModels()) -} - func TestAttestations(t *testing.T) { p, err := loadYAML(` name: attestations diff --git a/loader/tests/annotations_test.go b/loader/tests/annotations_test.go new file mode 100644 index 00000000..acf4e534 --- /dev/null +++ b/loader/tests/annotations_test.go @@ -0,0 +1,49 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestAnnotations(t *testing.T) { + p := load(t, ` +name: test +services: + list: + image: alpine + annotations: + - com.example.foo=bar + map: + image: alpine + annotations: + com.example.foo: bar +`) + expect := func(p *types.Project) { + expected := types.Mapping{"com.example.foo": "bar"} + assert.DeepEqual(t, p.Services["list"].Annotations, expected) + assert.DeepEqual(t, p.Services["map"].Annotations, expected) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/attach_test.go b/loader/tests/attach_test.go new file mode 100644 index 00000000..5e42ce6f --- /dev/null +++ b/loader/tests/attach_test.go @@ -0,0 +1,50 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestAttach(t *testing.T) { + p := load(t, ` +name: test +services: + attached: + image: alpine + attach: true + detached: + image: alpine + attach: false + default: + image: alpine +`) + + expect := func(p *types.Project) { + assert.Equal(t, *p.Services["attached"].Attach, true) + assert.Equal(t, *p.Services["detached"].Attach, false) + assert.Assert(t, p.Services["default"].Attach == nil) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/blkio_config_test.go b/loader/tests/blkio_config_test.go new file mode 100644 index 00000000..e6f42793 --- /dev/null +++ b/loader/tests/blkio_config_test.go @@ -0,0 +1,66 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestBlkioConfig(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: busybox + blkio_config: + weight: 300 + weight_device: + - path: /dev/sda + weight: 400 + device_read_bps: + - path: /dev/sda + rate: 1024k + device_write_bps: + - path: /dev/sda + rate: 1024 + device_read_iops: + - path: /dev/sda + rate: 100 + device_write_iops: + - path: /dev/sda + rate: 200 +`) + expect := func(p *types.Project) { + bc := p.Services["foo"].BlkioConfig + assert.Equal(t, bc.Weight, uint16(300)) + assert.Equal(t, bc.WeightDevice[0].Path, "/dev/sda") + assert.Equal(t, bc.WeightDevice[0].Weight, uint16(400)) + assert.Equal(t, bc.DeviceReadBps[0].Path, "/dev/sda") + assert.Equal(t, bc.DeviceReadBps[0].Rate, types.UnitBytes(1024*1024)) + assert.Equal(t, bc.DeviceWriteBps[0].Rate, types.UnitBytes(1024)) + assert.Equal(t, bc.DeviceReadIOps[0].Rate, types.UnitBytes(100)) + assert.Equal(t, bc.DeviceWriteIOps[0].Rate, types.UnitBytes(200)) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/build_extra_test.go b/loader/tests/build_extra_test.go new file mode 100644 index 00000000..6bf565e9 --- /dev/null +++ b/loader/tests/build_extra_test.go @@ -0,0 +1,146 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestBuildCacheTo(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + build: + context: . + cache_to: + - user/app:cache + - type=local,dest=path/to/cache +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].Build.CacheTo, types.StringList{"user/app:cache", "type=local,dest=path/to/cache"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestBuildNoCache(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + build: + context: . + no_cache: true +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Build.NoCache, true) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestBuildPull(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + build: + context: . + pull: true +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Build.Pull, true) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestBuildShmSize(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + build: + context: . + shm_size: 128m +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Build.ShmSize, types.UnitBytes(128*1024*1024)) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestBuildIsolation(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + build: + context: . + isolation: process +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Build.Isolation, "process") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestBuildPrivileged(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + build: + context: . + privileged: true +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Build.Privileged, true) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/build_test.go b/loader/tests/build_test.go new file mode 100644 index 00000000..34e072cd --- /dev/null +++ b/loader/tests/build_test.go @@ -0,0 +1,138 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestBuildConfig(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + build: + context: ./dir + dockerfile: Dockerfile + args: + foo: bar + target: foo + network: foo + cache_from: + - foo + - bar + labels: [FOO=BAR] + additional_contexts: + foo: ./bar + tags: + - foo:v1.0.0 + - docker.io/username/foo:my-other-tag + platforms: + - linux/amd64 + - linux/arm64 +`) + + expect := func(p *types.Project) { + b := p.Services["foo"].Build + assert.Equal(t, b.Dockerfile, "Dockerfile") + assert.DeepEqual(t, b.Args, types.MappingWithEquals{"foo": ptr("bar")}) + assert.Equal(t, b.Target, "foo") + assert.Equal(t, b.Network, "foo") + assert.DeepEqual(t, b.CacheFrom, types.StringList{"foo", "bar"}) + assert.DeepEqual(t, b.Labels, types.Labels{"FOO": "BAR"}) + assert.DeepEqual(t, b.Tags, types.StringList{"foo:v1.0.0", "docker.io/username/foo:my-other-tag"}) + assert.DeepEqual(t, b.Platforms, types.StringList{"linux/amd64", "linux/arm64"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestDockerfileInline(t *testing.T) { + p := load(t, ` +name: test +services: + bar: + build: + dockerfile_inline: | + FROM alpine + RUN echo "hello" > /world.txt +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["bar"].Build.DockerfileInline, "FROM alpine\nRUN echo \"hello\" > /world.txt\n") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestBuildSSH(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + build: + context: . + ssh: + - default +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].Build.SSH, types.SSHConfig{{ID: "default", Path: ""}}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestBuildSecrets(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + build: + context: . + secrets: + - source: secret1 + target: /run/secrets/secret1 + - source: secret2 + target: my_secret + uid: '103' + gid: '103' + mode: 0440 +secrets: + secret1: + file: ./secret_data + secret2: + external: true +`) + secrets := p.Services["foo"].Build.Secrets + assert.Equal(t, len(secrets), 2) + assert.Equal(t, secrets[0].Source, "secret1") + assert.Equal(t, secrets[1].UID, "103") + assert.Equal(t, *secrets[1].Mode, types.FileMode(0o440)) +} diff --git a/loader/tests/cap_add_drop_test.go b/loader/tests/cap_add_drop_test.go new file mode 100644 index 00000000..b841f3cc --- /dev/null +++ b/loader/tests/cap_add_drop_test.go @@ -0,0 +1,48 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestCapAddDrop(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + cap_add: + - ALL + cap_drop: + - NET_ADMIN + - SYS_ADMIN +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].CapAdd, []string{"ALL"}) + assert.DeepEqual(t, p.Services["foo"].CapDrop, []string{"NET_ADMIN", "SYS_ADMIN"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/cgroup_parent_test.go b/loader/tests/cgroup_parent_test.go new file mode 100644 index 00000000..f6ec36b0 --- /dev/null +++ b/loader/tests/cgroup_parent_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestCgroupParent(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + cgroup_parent: m-executor-abcd +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].CgroupParent, "m-executor-abcd") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/cgroup_test.go b/loader/tests/cgroup_test.go new file mode 100644 index 00000000..563d2537 --- /dev/null +++ b/loader/tests/cgroup_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestCgroup(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + cgroup: private +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Cgroup, "private") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/command_test.go b/loader/tests/command_test.go new file mode 100644 index 00000000..faaef0d8 --- /dev/null +++ b/loader/tests/command_test.go @@ -0,0 +1,54 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestCommandString(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + command: bundle exec thin -p 3000 +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].Command, types.ShellCommand{"bundle", "exec", "thin", "-p", "3000"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestCommandList(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + command: ["bundle", "exec", "thin", "-p", "3000"] +`) + assert.DeepEqual(t, p.Services["foo"].Command, types.ShellCommand{"bundle", "exec", "thin", "-p", "3000"}) +} diff --git a/loader/tests/configs_test.go b/loader/tests/configs_test.go new file mode 100644 index 00000000..c4aa120c --- /dev/null +++ b/loader/tests/configs_test.go @@ -0,0 +1,86 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestServiceConfigs(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + configs: + - config1 + - source: config2 + target: /my_config + uid: '103' + gid: '103' + mode: 0440 +configs: + config1: + file: ./config_data + config2: + external: true +`) + configs := p.Services["foo"].Configs + assert.Equal(t, len(configs), 2) + assert.Equal(t, configs[0].Source, "config1") + assert.Equal(t, configs[1].Source, "config2") + assert.Equal(t, configs[1].Target, "/my_config") + assert.Equal(t, configs[1].UID, "103") + assert.Equal(t, configs[1].GID, "103") + assert.Equal(t, *configs[1].Mode, types.FileMode(0o440)) +} + +func TestTopLevelConfigs(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine +configs: + config1: + file: ./config_data + labels: + foo: bar + config2: + external: + name: my_config + config3: + external: true + config4: + name: foo + file: ./config_data + x-bar: baz + x-foo: bar +`) + assert.Equal(t, p.Configs["config2"].Name, "my_config") + assert.Equal(t, p.Configs["config2"].External, types.External(true)) + assert.Equal(t, p.Configs["config3"].External, types.External(true)) + assert.Equal(t, p.Configs["config4"].Name, "foo") + assert.DeepEqual(t, p.Configs["config1"].Labels, types.Labels{"foo": "bar"}) + assert.DeepEqual(t, p.Configs["config4"].Extensions, types.Extensions{ + "x-bar": "baz", + "x-foo": "bar", + }) +} diff --git a/loader/tests/container_name_test.go b/loader/tests/container_name_test.go new file mode 100644 index 00000000..d64dad44 --- /dev/null +++ b/loader/tests/container_name_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestContainerName(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + container_name: my-web-container +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].ContainerName, "my-web-container") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/cpu_test.go b/loader/tests/cpu_test.go new file mode 100644 index 00000000..00a20e7a --- /dev/null +++ b/loader/tests/cpu_test.go @@ -0,0 +1,86 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestCPUFields(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + cpu_count: 4 + cpu_percent: 50 + cpu_shares: 1024 + cpu_quota: 50000 + cpu_period: 100000 + cpu_rt_period: 1000000 + cpu_rt_runtime: 950000 + cpus: 1.5 + cpuset: "0,1" +`) + expect := func(p *types.Project) { + s := p.Services["foo"] + assert.Equal(t, s.CPUCount, int64(4)) + assert.Equal(t, s.CPUPercent, float32(50)) + assert.Equal(t, s.CPUShares, int64(1024)) + assert.Equal(t, s.CPUQuota, int64(50000)) + assert.Equal(t, s.CPUPeriod, int64(100000)) + assert.Equal(t, s.CPURTPeriod, int64(1000000)) + assert.Equal(t, s.CPURTRuntime, int64(950000)) + assert.Equal(t, s.CPUS, float32(1.5)) + assert.Equal(t, s.CPUSet, "0,1") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestMemoryFields(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + mem_limit: 512m + mem_reservation: 256m + mem_swappiness: 60 + memswap_limit: 1g + shm_size: 64m +`) + expect := func(p *types.Project) { + s := p.Services["foo"] + assert.Equal(t, s.MemLimit, types.UnitBytes(512*1024*1024)) + assert.Equal(t, s.MemReservation, types.UnitBytes(256*1024*1024)) + assert.Equal(t, s.MemSwappiness, types.UnitBytes(60)) + assert.Equal(t, s.MemSwapLimit, types.UnitBytes(1024*1024*1024)) + assert.Equal(t, s.ShmSize, types.UnitBytes(64*1024*1024)) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/credential_spec_test.go b/loader/tests/credential_spec_test.go new file mode 100644 index 00000000..866db186 --- /dev/null +++ b/loader/tests/credential_spec_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestCredentialSpec(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + credential_spec: + config: "0bt9dmxjvjiqermk6xrop3ekq" +`) + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].CredentialSpec.Config, "0bt9dmxjvjiqermk6xrop3ekq") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/depends_on_test.go b/loader/tests/depends_on_test.go new file mode 100644 index 00000000..fd272e6d --- /dev/null +++ b/loader/tests/depends_on_test.go @@ -0,0 +1,101 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestDependsOnList(t *testing.T) { + p := load(t, ` +name: test +services: + web: + image: alpine + depends_on: + - db + - redis + db: + image: postgres + redis: + image: redis +`) + expect := func(p *types.Project) { + deps := p.Services["web"].DependsOn + assert.Equal(t, len(deps), 2) + assert.Equal(t, deps["db"].Condition, types.ServiceConditionStarted) + assert.Equal(t, deps["db"].Required, true) + assert.Equal(t, deps["redis"].Condition, types.ServiceConditionStarted) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestDependsOnObject(t *testing.T) { + p := load(t, ` +name: test +services: + web: + image: alpine + depends_on: + db: + condition: service_healthy + restart: true + redis: + condition: service_started + db: + image: postgres + redis: + image: redis +`) + expect := func(p *types.Project) { + deps := p.Services["web"].DependsOn + assert.Equal(t, deps["db"].Condition, "service_healthy") + assert.Equal(t, deps["db"].Restart, true) + assert.Equal(t, deps["redis"].Condition, types.ServiceConditionStarted) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestDependsOnXService(t *testing.T) { + p := load(t, ` +name: test +services: + test: + image: test + depends_on: + - x-foo + x-foo: + image: foo +`) + assert.DeepEqual(t, p.Services["test"].DependsOn, types.DependsOnConfig{ + "x-foo": types.ServiceDependency{ + Condition: types.ServiceConditionStarted, + Required: true, + }, + }) +} diff --git a/loader/tests/deploy_pids_test.go b/loader/tests/deploy_pids_test.go new file mode 100644 index 00000000..a9c95b8d --- /dev/null +++ b/loader/tests/deploy_pids_test.go @@ -0,0 +1,45 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestDeployResourcesPids(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + deploy: + resources: + limits: + pids: 100 +`) + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Deploy.Resources.Limits.Pids, int64(100)) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/deploy_test.go b/loader/tests/deploy_test.go new file mode 100644 index 00000000..12f8f43d --- /dev/null +++ b/loader/tests/deploy_test.go @@ -0,0 +1,190 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + "time" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestDeployModeReplicas(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + deploy: + mode: replicated + replicas: 6 + labels: [FOO=BAR] + endpoint_mode: dnsrr +`) + + expect := func(p *types.Project) { + d := p.Services["foo"].Deploy + assert.Equal(t, d.Mode, "replicated") + assert.Equal(t, *d.Replicas, 6) + assert.DeepEqual(t, d.Labels, types.Labels{"FOO": "BAR"}) + assert.Equal(t, d.EndpointMode, "dnsrr") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestDeployUpdateRollbackConfig(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + deploy: + update_config: + parallelism: 3 + delay: 10s + failure_action: continue + monitor: 60s + max_failure_ratio: 0.3 + order: start-first + rollback_config: + parallelism: 3 + delay: 10s + failure_action: continue + monitor: 60s + max_failure_ratio: 0.3 + order: start-first +`) + + expect := func(p *types.Project) { + uc := p.Services["foo"].Deploy.UpdateConfig + assert.Equal(t, *uc.Parallelism, uint64(3)) + assert.Equal(t, uc.Delay, types.Duration(10*time.Second)) + assert.Equal(t, uc.FailureAction, "continue") + assert.Equal(t, uc.Monitor, types.Duration(60*time.Second)) + assert.Equal(t, uc.MaxFailureRatio, float32(0.3)) + assert.Equal(t, uc.Order, "start-first") + + rc := p.Services["foo"].Deploy.RollbackConfig + assert.Equal(t, *rc.Parallelism, uint64(3)) + assert.Equal(t, rc.Order, "start-first") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestDeployResources(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + deploy: + resources: + limits: + cpus: '0.001' + memory: 50M + reservations: + cpus: '0.0001' + memory: 20M + generic_resources: + - discrete_resource_spec: + kind: 'gpu' + value: 2 + - discrete_resource_spec: + kind: 'ssd' + value: 1 +`) + + expect := func(p *types.Project) { + r := p.Services["foo"].Deploy.Resources + assert.Equal(t, r.Limits.NanoCPUs, types.NanoCPUs(0.001)) + assert.Equal(t, r.Limits.MemoryBytes, types.UnitBytes(50*1024*1024)) + assert.Equal(t, r.Reservations.NanoCPUs, types.NanoCPUs(0.0001)) + assert.Equal(t, r.Reservations.MemoryBytes, types.UnitBytes(20*1024*1024)) + assert.Equal(t, len(r.Reservations.GenericResources), 2) + assert.Equal(t, r.Reservations.GenericResources[0].DiscreteResourceSpec.Kind, "gpu") + assert.Equal(t, r.Reservations.GenericResources[0].DiscreteResourceSpec.Value, int64(2)) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestDeployRestartPolicy(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + deploy: + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 3 + window: 120s +`) + + expect := func(p *types.Project) { + rp := p.Services["foo"].Deploy.RestartPolicy + assert.Equal(t, rp.Condition, "on-failure") + assert.Equal(t, *rp.Delay, types.Duration(5*time.Second)) + assert.Equal(t, *rp.MaxAttempts, uint64(3)) + assert.Equal(t, *rp.Window, types.Duration(2*time.Minute)) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestDeployPlacement(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + deploy: + placement: + constraints: [node=foo] + max_replicas_per_node: 5 + preferences: + - spread: node.labels.az +`) + + expect := func(p *types.Project) { + pl := p.Services["foo"].Deploy.Placement + assert.DeepEqual(t, pl.Constraints, []string{"node=foo"}) + assert.Equal(t, pl.MaxReplicas, uint64(5)) + assert.Equal(t, pl.Preferences[0].Spread, "node.labels.az") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/develop_test.go b/loader/tests/develop_test.go new file mode 100644 index 00000000..7ec4e36e --- /dev/null +++ b/loader/tests/develop_test.go @@ -0,0 +1,93 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "context" + "testing" + + "github.com/compose-spec/compose-go/v2/loader" + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestDevelopWatch(t *testing.T) { + p, err := loader.LoadWithContext(context.TODO(), types.ConfigDetails{ + ConfigFiles: []types.ConfigFile{{Filename: "compose.yml", Content: []byte(` +name: test +services: + web: + image: app + build: ./app + develop: + watch: + - path: ./src + action: sync + target: /app/src + ignore: + - node_modules/ + initial_sync: true + - path: ./requirements.txt + action: rebuild + - path: ./config.yml + action: sync+restart + target: /app/config.yml +`)}}, + Environment: map[string]string{}, + }, func(options *loader.Options) { + options.ResolvePaths = false + options.SkipValidation = true + }) + assert.NilError(t, err) + dev := p.Services["web"].Develop + assert.Assert(t, dev != nil) + + assert.Equal(t, len(dev.Watch), 3) + + assert.Equal(t, dev.Watch[0].Path, "./src") + assert.Equal(t, dev.Watch[0].Action, types.WatchActionSync) + assert.Equal(t, dev.Watch[0].Target, "/app/src") + assert.DeepEqual(t, dev.Watch[0].Ignore, []string{"node_modules/"}) + assert.Equal(t, dev.Watch[0].InitialSync, true) + + assert.Equal(t, dev.Watch[1].Path, "./requirements.txt") + assert.Equal(t, dev.Watch[1].Action, types.WatchActionRebuild) + + assert.Equal(t, dev.Watch[2].Path, "./config.yml") + assert.Equal(t, dev.Watch[2].Action, types.WatchActionSyncRestart) + assert.Equal(t, dev.Watch[2].Target, "/app/config.yml") +} + +func TestDevelopMissingAction(t *testing.T) { + _, err := loader.LoadWithContext(context.TODO(), types.ConfigDetails{ + ConfigFiles: []types.ConfigFile{{Filename: "compose.yml", Content: []byte(` +name: test +services: + web: + image: app + build: ./app + develop: + watch: + - path: ./src + target: /app/src +`)}}, + Environment: map[string]string{}, + }, func(options *loader.Options) { + options.ResolvePaths = false + }) + assert.ErrorContains(t, err, "services.web.develop.watch.0 missing property 'action'") +} diff --git a/loader/tests/device_cgroup_rules_test.go b/loader/tests/device_cgroup_rules_test.go new file mode 100644 index 00000000..3f437617 --- /dev/null +++ b/loader/tests/device_cgroup_rules_test.go @@ -0,0 +1,45 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestDeviceCgroupRules(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + device_cgroup_rules: + - "c 1:3 mr" + - "a 7:* rmw" +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].DeviceCgroupRules, []string{"c 1:3 mr", "a 7:* rmw"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/devices_test.go b/loader/tests/devices_test.go new file mode 100644 index 00000000..db87ac16 --- /dev/null +++ b/loader/tests/devices_test.go @@ -0,0 +1,88 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestDeviceMapping(t *testing.T) { + p := load(t, ` +name: test +services: + test: + image: alpine + devices: + - /dev/source:/dev/target:permissions + - /dev/single +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["test"].Devices, []types.DeviceMapping{ + {Source: "/dev/source", Target: "/dev/target", Permissions: "permissions"}, + {Source: "/dev/single", Target: "/dev/single", Permissions: "rwm"}, + }) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestDeviceMappingLongSyntax(t *testing.T) { + p := load(t, ` +name: test +services: + test: + image: alpine + devices: + - source: /dev/source + target: /dev/target + permissions: permissions +`) + assert.DeepEqual(t, p.Services["test"].Devices, []types.DeviceMapping{ + {Source: "/dev/source", Target: "/dev/target", Permissions: "permissions"}, + }) +} + +func TestDeviceReservation(t *testing.T) { + p := load(t, ` +name: test +services: + test: + image: alpine + deploy: + resources: + reservations: + devices: + - driver: nvidia + capabilities: ["gpu"] + count: all + options: + q_bits: 42 +`) + devs := p.Services["test"].Deploy.Resources.Reservations.Devices + assert.Equal(t, len(devs), 1) + assert.Equal(t, devs[0].Driver, "nvidia") + assert.DeepEqual(t, devs[0].Capabilities, []string{"gpu"}) + assert.Equal(t, devs[0].Count, types.DeviceCount(-1)) + assert.Equal(t, devs[0].Options["q_bits"], "42") +} diff --git a/loader/tests/dns_opt_test.go b/loader/tests/dns_opt_test.go new file mode 100644 index 00000000..78b5f8e0 --- /dev/null +++ b/loader/tests/dns_opt_test.go @@ -0,0 +1,45 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestDNSOpt(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + dns_opt: + - use-vc + - no-tld-query +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].DNSOpts, []string{"use-vc", "no-tld-query"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/dns_test.go b/loader/tests/dns_test.go new file mode 100644 index 00000000..9aa7546f --- /dev/null +++ b/loader/tests/dns_test.go @@ -0,0 +1,74 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestDNS(t *testing.T) { + p := load(t, ` +name: test +services: + list: + image: alpine + dns: + - 8.8.8.8 + - 9.9.9.9 + string: + image: alpine + dns: 8.8.8.8 +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["list"].DNS, types.StringList{"8.8.8.8", "9.9.9.9"}) + assert.DeepEqual(t, p.Services["string"].DNS, types.StringList{"8.8.8.8"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestDNSSearch(t *testing.T) { + p := load(t, ` +name: test +services: + list: + image: alpine + dns_search: + - dc1.example.com + - dc2.example.com + string: + image: alpine + dns_search: example.com +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["list"].DNSSearch, types.StringList{"dc1.example.com", "dc2.example.com"}) + assert.DeepEqual(t, p.Services["string"].DNSSearch, types.StringList{"example.com"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/domainname_test.go b/loader/tests/domainname_test.go new file mode 100644 index 00000000..59f89b07 --- /dev/null +++ b/loader/tests/domainname_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestDomainname(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + domainname: foo.com +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].DomainName, "foo.com") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/entrypoint_test.go b/loader/tests/entrypoint_test.go new file mode 100644 index 00000000..c21c6d98 --- /dev/null +++ b/loader/tests/entrypoint_test.go @@ -0,0 +1,54 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestEntrypointList(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + entrypoint: ["/code/entrypoint.sh", "-p", "3000"] +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].Entrypoint, types.ShellCommand{"/code/entrypoint.sh", "-p", "3000"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestEntrypointString(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + entrypoint: /code/entrypoint.sh -p 3000 +`) + assert.DeepEqual(t, p.Services["foo"].Entrypoint, types.ShellCommand{"/code/entrypoint.sh", "-p", "3000"}) +} diff --git a/loader/tests/env_file_test.go b/loader/tests/env_file_test.go new file mode 100644 index 00000000..42eee2cf --- /dev/null +++ b/loader/tests/env_file_test.go @@ -0,0 +1,89 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestEnvFileString(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + env_file: + - path: .env + required: false +`) + expect := func(p *types.Project) { + assert.Equal(t, len(p.Services["foo"].EnvFiles), 1) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestEnvFileList(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + env_file: + - path: .env + required: false + - path: .env.local + required: false +`) + expect := func(p *types.Project) { + assert.Equal(t, len(p.Services["foo"].EnvFiles), 2) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestEnvFileLongSyntax(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + env_file: + - path: .env + required: false + format: raw +`) + expect := func(p *types.Project) { + assert.Equal(t, len(p.Services["foo"].EnvFiles), 1) + assert.Equal(t, p.Services["foo"].EnvFiles[0].Format, "raw") + assert.Equal(t, bool(p.Services["foo"].EnvFiles[0].Required), false) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/environment_test.go b/loader/tests/environment_test.go new file mode 100644 index 00000000..3169430b --- /dev/null +++ b/loader/tests/environment_test.go @@ -0,0 +1,139 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "context" + "strings" + "testing" + + "github.com/compose-spec/compose-go/v2/loader" + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestEnvironmentMap(t *testing.T) { + p := loadWithEnv(t, ` +name: test +services: + foo: + image: alpine + environment: + FOO: "1" + BAR: 2 + GA: 2.5 + BU: "" + ZO: + MEU: +`, map[string]string{"MEU": "Shadoks"}) + + expect := func(p *types.Project) { + env := p.Services["foo"].Environment + assert.Equal(t, *env["FOO"], "1") + assert.Equal(t, *env["BAR"], "2") + assert.Equal(t, *env["GA"], "2.5") + assert.Equal(t, *env["BU"], "") + assert.Equal(t, *env["MEU"], "Shadoks") + assert.Assert(t, env["ZO"] == nil) + } + expect(p) +} + +func TestEnvironmentList(t *testing.T) { + p := loadWithEnv(t, ` +name: test +services: + foo: + image: alpine + environment: + - FOO=1 + - BAR=2 + - BU= + - ZO + - MEU +`, map[string]string{"MEU": "Shadoks"}) + + expect := func(p *types.Project) { + env := p.Services["foo"].Environment + assert.Equal(t, *env["FOO"], "1") + assert.Equal(t, *env["BAR"], "2") + assert.Equal(t, *env["BU"], "") + assert.Equal(t, *env["MEU"], "Shadoks") + assert.Assert(t, env["ZO"] == nil) + } + expect(p) +} + +func TestEnvironmentBoolean(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + environment: + FOO: true + BAR: false +`) + assert.Equal(t, *p.Services["foo"].Environment["FOO"], "true") + assert.Equal(t, *p.Services["foo"].Environment["BAR"], "false") +} + +func TestEnvironmentInvalidValue(t *testing.T) { + _, err := loader.LoadWithContext(context.TODO(), types.ConfigDetails{ + ConfigFiles: []types.ConfigFile{{Filename: "compose.yml", Content: []byte(` +name: test +services: + foo: + image: alpine + environment: + FOO: ["1"] +`)}}, + Environment: map[string]string{}, + }) + assert.ErrorContains(t, err, "services.foo.environment.FOO must be a boolean, null, number or string") +} + +func TestEnvironmentInvalidObject(t *testing.T) { + _, err := loader.LoadWithContext(context.TODO(), types.ConfigDetails{ + ConfigFiles: []types.ConfigFile{{Filename: "compose.yml", Content: []byte(` +name: test +services: + foo: + image: alpine + environment: "FOO=1" +`)}}, + Environment: map[string]string{}, + }) + assert.ErrorContains(t, err, "services.foo.environment must be a mapping") +} + +func TestEnvironmentWhitespace(t *testing.T) { + _, err := loader.LoadWithContext(context.TODO(), types.ConfigDetails{ + ConfigFiles: []types.ConfigFile{{Filename: "compose.yml", Content: []byte(` +name: test +services: + test: + environment: + - DEBUG = true +`)}}, + Environment: map[string]string{}, + }, func(options *loader.Options) { + options.SkipConsistencyCheck = true + options.SkipNormalization = true + }) + assert.Check(t, strings.Contains(err.Error(), "environment variable DEBUG is declared with a trailing space"), err.Error()) +} diff --git a/loader/tests/expose_test.go b/loader/tests/expose_test.go new file mode 100644 index 00000000..9e5e2089 --- /dev/null +++ b/loader/tests/expose_test.go @@ -0,0 +1,45 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestExpose(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + expose: + - "3000" + - 8000 +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].Expose, types.StringOrNumberList{"3000", "8000"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/extensions_test.go b/loader/tests/extensions_test.go new file mode 100644 index 00000000..e2090876 --- /dev/null +++ b/loader/tests/extensions_test.go @@ -0,0 +1,69 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestTopLevelExtensions(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine +x-foo: bar +x-bar: baz +x-nested: + foo: bar + bar: baz +`) + expect := func(p *types.Project) { + assert.Equal(t, p.Extensions["x-foo"], "bar") + assert.Equal(t, p.Extensions["x-bar"], "baz") + nested := p.Extensions["x-nested"].(map[string]any) + assert.Equal(t, nested["foo"], "bar") + assert.Equal(t, nested["bar"], "baz") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestServiceExtensions(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + x-bar: baz + x-foo: bar +`) + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Extensions["x-bar"], "baz") + assert.Equal(t, p.Services["foo"].Extensions["x-foo"], "bar") + } + expect(p) + + yamlP, _ := roundTrip(t, p) + expect(yamlP) +} diff --git a/loader/tests/external_links_test.go b/loader/tests/external_links_test.go new file mode 100644 index 00000000..60cbfb47 --- /dev/null +++ b/loader/tests/external_links_test.go @@ -0,0 +1,47 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestExternalLinks(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + external_links: + - redis_1 + - project_db_1:mysql + - project_db_1:postgresql +`) + + expect := func(p *types.Project) { + expected := []string{"redis_1", "project_db_1:mysql", "project_db_1:postgresql"} + assert.DeepEqual(t, p.Services["foo"].ExternalLinks, expected) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/extra_hosts_test.go b/loader/tests/extra_hosts_test.go new file mode 100644 index 00000000..1e114c14 --- /dev/null +++ b/loader/tests/extra_hosts_test.go @@ -0,0 +1,94 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestExtraHostsMap(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + extra_hosts: + alpha: "50.31.209.229" + zulu: "162.242.195.82" +`) + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].ExtraHosts, types.HostsList{ + "alpha": []string{"50.31.209.229"}, + "zulu": []string{"162.242.195.82"}, + }) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestExtraHostsList(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + extra_hosts: + - "alpha:50.31.209.229" + - "zulu:127.0.0.2" + - "zulu:ff02::1" +`) + assert.DeepEqual(t, p.Services["foo"].ExtraHosts, types.HostsList{ + "alpha": []string{"50.31.209.229"}, + "zulu": []string{"127.0.0.2", "ff02::1"}, + }) +} + +func TestExtraHostsRepeated(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + extra_hosts: + - "myhost=0.0.0.1,0.0.0.2" +`) + assert.DeepEqual(t, p.Services["foo"].ExtraHosts, types.HostsList{ + "myhost": []string{"0.0.0.1", "0.0.0.2"}, + }) +} + +func TestExtraHostsLongSyntax(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + extra_hosts: + myhost: + - "0.0.0.1" + - "0.0.0.2" +`) + assert.DeepEqual(t, p.Services["foo"].ExtraHosts, types.HostsList{ + "myhost": []string{"0.0.0.1", "0.0.0.2"}, + }) +} diff --git a/loader/tests/gpus_test.go b/loader/tests/gpus_test.go new file mode 100644 index 00000000..5c9d9e4e --- /dev/null +++ b/loader/tests/gpus_test.go @@ -0,0 +1,54 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestGpus(t *testing.T) { + p := load(t, ` +name: test +services: + test: + image: alpine + gpus: + - driver: nvidia + - driver: 3dfx + device_ids: ["voodoo2"] + capabilities: ["directX"] +`) + assert.DeepEqual(t, p.Services["test"].Gpus, []types.DeviceRequest{ + {Driver: "nvidia", Count: -1}, + {Capabilities: []string{"directX"}, Driver: "3dfx", IDs: []string{"voodoo2"}}, + }) +} + +func TestGpusAll(t *testing.T) { + p := load(t, ` +name: test +services: + test: + image: alpine + gpus: all +`) + assert.Equal(t, len(p.Services["test"].Gpus), 1) + assert.Equal(t, p.Services["test"].Gpus[0].Count, types.DeviceCount(-1)) +} diff --git a/loader/tests/group_add_test.go b/loader/tests/group_add_test.go new file mode 100644 index 00000000..326ec0a1 --- /dev/null +++ b/loader/tests/group_add_test.go @@ -0,0 +1,45 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestGroupAdd(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + group_add: + - mail + - "0" +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].GroupAdd, []string{"mail", "0"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/gw_priority_test.go b/loader/tests/gw_priority_test.go new file mode 100644 index 00000000..47c6d6b4 --- /dev/null +++ b/loader/tests/gw_priority_test.go @@ -0,0 +1,47 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestGwPriority(t *testing.T) { + p := load(t, ` +name: test +services: + test: + image: alpine + networks: + test: + gw_priority: 42 +networks: + test: +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["test"].Networks["test"].GatewayPriority, 42) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/healthcheck_test.go b/loader/tests/healthcheck_test.go new file mode 100644 index 00000000..5a3f806e --- /dev/null +++ b/loader/tests/healthcheck_test.go @@ -0,0 +1,55 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + "time" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestHealthcheck(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + healthcheck: + test: echo "hello world" + interval: 10s + timeout: 1s + retries: 5 + start_period: 15s + start_interval: 5s +`) + expect := func(p *types.Project) { + hc := p.Services["foo"].HealthCheck + assert.DeepEqual(t, hc.Test, types.HealthCheckTest{"CMD-SHELL", `echo "hello world"`}) + assert.Equal(t, *hc.Interval, types.Duration(10*time.Second)) + assert.Equal(t, *hc.Timeout, types.Duration(1*time.Second)) + assert.Equal(t, *hc.Retries, uint64(5)) + assert.Equal(t, *hc.StartPeriod, types.Duration(15*time.Second)) + assert.Equal(t, *hc.StartInterval, types.Duration(5*time.Second)) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/helpers_test.go b/loader/tests/helpers_test.go new file mode 100644 index 00000000..f7ff05d3 --- /dev/null +++ b/loader/tests/helpers_test.go @@ -0,0 +1,87 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "context" + "testing" + + "github.com/compose-spec/compose-go/v2/loader" + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func load(t *testing.T, yaml string) *types.Project { + t.Helper() + p, err := loader.LoadWithContext(context.TODO(), types.ConfigDetails{ + ConfigFiles: []types.ConfigFile{{Filename: "compose.yml", Content: []byte(yaml)}}, + Environment: map[string]string{}, + }, func(options *loader.Options) { + options.SkipConsistencyCheck = true + options.SkipNormalization = true + }) + assert.NilError(t, err) + return p +} + +func loadWithEnv(t *testing.T, yaml string, env map[string]string) *types.Project { + t.Helper() + p, err := loader.LoadWithContext(context.TODO(), types.ConfigDetails{ + ConfigFiles: []types.ConfigFile{{Filename: "compose.yml", Content: []byte(yaml)}}, + Environment: env, + }, func(options *loader.Options) { + options.SkipConsistencyCheck = true + options.SkipNormalization = true + }) + assert.NilError(t, err) + return p +} + +// loadRoundTrip reloads marshaled content, skipping schema validation since +// marshaled output may not match schema exactly (e.g. missing yaml tags on some structs). +func loadRoundTrip(t *testing.T, content string) *types.Project { + t.Helper() + p, err := loader.LoadWithContext(context.TODO(), types.ConfigDetails{ + ConfigFiles: []types.ConfigFile{{Filename: "compose.yml", Content: []byte(content)}}, + Environment: map[string]string{}, + }, func(options *loader.Options) { + options.SkipConsistencyCheck = true + options.SkipNormalization = true + options.SkipValidation = true + }) + assert.NilError(t, err) + return p +} + +// roundTrip marshals the project to YAML and JSON, reloads each, and returns both. +func roundTrip(t *testing.T, p *types.Project) (fromYAML, fromJSON *types.Project) { + t.Helper() + + yamlBytes, err := p.MarshalYAML() + assert.NilError(t, err) + fromYAML = loadRoundTrip(t, string(yamlBytes)) + + jsonBytes, err := p.MarshalJSON() + assert.NilError(t, err) + fromJSON = loadRoundTrip(t, string(jsonBytes)) + + return +} + +func ptr[T any](t T) *T { + return &t +} diff --git a/loader/tests/hostname_test.go b/loader/tests/hostname_test.go new file mode 100644 index 00000000..c25af05d --- /dev/null +++ b/loader/tests/hostname_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestHostname(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + hostname: foo +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Hostname, "foo") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/image_test.go b/loader/tests/image_test.go new file mode 100644 index 00000000..25bfcb7f --- /dev/null +++ b/loader/tests/image_test.go @@ -0,0 +1,42 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestImage(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: redis +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Image, "redis") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/init_test.go b/loader/tests/init_test.go new file mode 100644 index 00000000..8c25de42 --- /dev/null +++ b/loader/tests/init_test.go @@ -0,0 +1,50 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestInit(t *testing.T) { + p := load(t, ` +name: test +services: + with-init: + image: alpine + init: true + without-init: + image: alpine + init: false + default: + image: alpine +`) + + expect := func(p *types.Project) { + assert.Equal(t, *p.Services["with-init"].Init, true) + assert.Equal(t, *p.Services["without-init"].Init, false) + assert.Assert(t, p.Services["default"].Init == nil) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/interface_name_test.go b/loader/tests/interface_name_test.go new file mode 100644 index 00000000..37b394b0 --- /dev/null +++ b/loader/tests/interface_name_test.go @@ -0,0 +1,47 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestInterfaceName(t *testing.T) { + p := load(t, ` +name: test +services: + test: + image: alpine + networks: + test: + interface_name: eth0 +networks: + test: +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["test"].Networks["test"].InterfaceName, "eth0") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/ipc_uts_pid_test.go b/loader/tests/ipc_uts_pid_test.go new file mode 100644 index 00000000..58de3b36 --- /dev/null +++ b/loader/tests/ipc_uts_pid_test.go @@ -0,0 +1,47 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestIpcUtsPid(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + ipc: host + uts: host + pid: host +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Ipc, "host") + assert.Equal(t, p.Services["foo"].Uts, "host") + assert.Equal(t, p.Services["foo"].Pid, "host") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/isolation_test.go b/loader/tests/isolation_test.go new file mode 100644 index 00000000..486078ed --- /dev/null +++ b/loader/tests/isolation_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestIsolation(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + isolation: process +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Isolation, "process") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/labels_test.go b/loader/tests/labels_test.go new file mode 100644 index 00000000..9dff8b96 --- /dev/null +++ b/loader/tests/labels_test.go @@ -0,0 +1,69 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestServiceLabels(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + labels: + com.example.description: "Accounting webapp" + com.example.number: 42 + com.example.empty-label: +`) + expect := func(p *types.Project) { + expected := types.Labels{ + "com.example.description": "Accounting webapp", + "com.example.number": "42", + "com.example.empty-label": "", + } + assert.DeepEqual(t, p.Services["foo"].Labels, expected) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestServiceLabelsListSyntax(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + labels: + - "com.example.description=Accounting webapp" + - "com.example.number=42" + - "com.example.empty-label" +`) + expected := types.Labels{ + "com.example.description": "Accounting webapp", + "com.example.number": "42", + "com.example.empty-label": "", + } + assert.DeepEqual(t, p.Services["foo"].Labels, expected) +} diff --git a/loader/tests/link_local_ips_test.go b/loader/tests/link_local_ips_test.go new file mode 100644 index 00000000..8212358a --- /dev/null +++ b/loader/tests/link_local_ips_test.go @@ -0,0 +1,48 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestLinkLocalIPs(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + networks: + net1: + link_local_ips: + - "fe80::1:95ff:fe20:100" +networks: + net1: +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].Networks["net1"].LinkLocalIPs, []string{"fe80::1:95ff:fe20:100"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/links_test.go b/loader/tests/links_test.go new file mode 100644 index 00000000..bbcb485b --- /dev/null +++ b/loader/tests/links_test.go @@ -0,0 +1,50 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestLinks(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + links: + - db + - db:database + - redis + db: + image: postgres + redis: + image: redis +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].Links, []string{"db", "db:database", "redis"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/logging_test.go b/loader/tests/logging_test.go new file mode 100644 index 00000000..dd2d516f --- /dev/null +++ b/loader/tests/logging_test.go @@ -0,0 +1,49 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestLogging(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + logging: + driver: syslog + options: + syslog-address: "tcp://192.168.0.42:123" +`) + expect := func(p *types.Project) { + expected := &types.LoggingConfig{ + Driver: "syslog", + Options: map[string]string{"syslog-address": "tcp://192.168.0.42:123"}, + } + assert.DeepEqual(t, p.Services["foo"].Logging, expected) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/mac_address_test.go b/loader/tests/mac_address_test.go new file mode 100644 index 00000000..8c582fd9 --- /dev/null +++ b/loader/tests/mac_address_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestMacAddress(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + mac_address: "02:42:ac:11:65:43" +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].MacAddress, "02:42:ac:11:65:43") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/models_test.go b/loader/tests/models_test.go new file mode 100644 index 00000000..173a1773 --- /dev/null +++ b/loader/tests/models_test.go @@ -0,0 +1,53 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestModels(t *testing.T) { + p := load(t, ` +name: test +services: + test_array: + models: + - foo + test_mapping: + models: + foo: + endpoint_var: MODEL_URL + model_var: MODEL +models: + foo: + model: ai/model + context_size: 1024 + runtime_flags: + - "--some-flag" +`) + assert.DeepEqual(t, p.Models["foo"], types.ModelConfig{ + Model: "ai/model", + ContextSize: 1024, + RuntimeFlags: []string{"--some-flag"}, + }) + assert.Assert(t, p.Services["test_array"].Models["foo"] == nil) + assert.Equal(t, p.Services["test_mapping"].Models["foo"].EndpointVariable, "MODEL_URL") + assert.Equal(t, p.Services["test_mapping"].Models["foo"].ModelVariable, "MODEL") +} diff --git a/loader/tests/network_driver_opts_test.go b/loader/tests/network_driver_opts_test.go new file mode 100644 index 00000000..eeb15d6d --- /dev/null +++ b/loader/tests/network_driver_opts_test.go @@ -0,0 +1,50 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestServiceNetworkDriverOpts(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + networks: + net1: + driver_opts: + com.docker.network.endpoint.sysctls: "ipv6.conf.accept_ra=0" +networks: + net1: +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].Networks["net1"].DriverOpts, types.Options{ + "com.docker.network.endpoint.sysctls": "ipv6.conf.accept_ra=0", + }) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/network_internal_test.go b/loader/tests/network_internal_test.go new file mode 100644 index 00000000..b3395f99 --- /dev/null +++ b/loader/tests/network_internal_test.go @@ -0,0 +1,72 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestNetworkInternal(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine +networks: + internal-net: + internal: true + attachable-net: + driver: overlay + attachable: true +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Networks["internal-net"].Internal, true) + assert.Equal(t, p.Networks["attachable-net"].Attachable, true) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestNetworkEnableIPv4IPv6(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine +networks: + net1: + enable_ipv4: false + enable_ipv6: true +`) + + expect := func(p *types.Project) { + assert.Equal(t, *p.Networks["net1"].EnableIPv4, false) + assert.Equal(t, *p.Networks["net1"].EnableIPv6, true) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/network_mode_test.go b/loader/tests/network_mode_test.go new file mode 100644 index 00000000..3e056ce0 --- /dev/null +++ b/loader/tests/network_mode_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestNetworkMode(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + network_mode: "container:0cfeab0f748b" +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].NetworkMode, "container:0cfeab0f748b") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/networks_test.go b/loader/tests/networks_test.go new file mode 100644 index 00000000..8342e4b7 --- /dev/null +++ b/loader/tests/networks_test.go @@ -0,0 +1,122 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestServiceNetworks(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + networks: + some-network: + aliases: + - alias1 + - alias3 + other-network: + ipv4_address: 172.16.238.10 + ipv6_address: "2001:3984:3989::10" + mac_address: "02:42:72:98:65:08" + other-other-network: +networks: + some-network: + other-network: + other-other-network: +`) + nets := p.Services["foo"].Networks + assert.DeepEqual(t, nets["some-network"].Aliases, []string{"alias1", "alias3"}) + assert.Equal(t, nets["other-network"].Ipv4Address, "172.16.238.10") + assert.Equal(t, nets["other-network"].Ipv6Address, "2001:3984:3989::10") + assert.Equal(t, nets["other-network"].MacAddress, "02:42:72:98:65:08") + assert.Assert(t, nets["other-other-network"] == nil) +} + +func TestTopLevelNetworks(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine +networks: + some-network: + other-network: + driver: overlay + driver_opts: + foo: "bar" + baz: 1 + ipam: + driver: overlay + config: + - subnet: 172.28.0.0/16 + ip_range: 172.28.5.0/24 + gateway: 172.28.5.254 + aux_addresses: + host1: 172.28.1.5 + host2: 172.28.1.6 + host3: 172.28.1.7 + - subnet: "2001:3984:3989::/64" + gateway: "2001:3984:3989::1" + labels: + foo: bar + external-network: + external: true + other-external-network: + external: + name: my-cool-network + x-bar: baz + x-foo: bar +`) + + expect := func(p *types.Project) { + // Simple network + assert.Equal(t, p.Networks["some-network"].Driver, "") + + // Network with driver and IPAM + other := p.Networks["other-network"] + assert.Equal(t, other.Driver, "overlay") + assert.DeepEqual(t, other.DriverOpts, types.Options{"foo": "bar", "baz": "1"}) + assert.Equal(t, other.Ipam.Driver, "overlay") + assert.Equal(t, len(other.Ipam.Config), 2) + assert.Equal(t, other.Ipam.Config[0].Subnet, "172.28.0.0/16") + assert.Equal(t, other.Ipam.Config[0].IPRange, "172.28.5.0/24") + assert.Equal(t, other.Ipam.Config[0].Gateway, "172.28.5.254") + assert.Equal(t, other.Ipam.Config[0].AuxiliaryAddresses["host1"], "172.28.1.5") + assert.Equal(t, other.Ipam.Config[1].Subnet, "2001:3984:3989::/64") + assert.DeepEqual(t, other.Labels, types.Labels{"foo": "bar"}) + + // External networks + assert.Equal(t, p.Networks["external-network"].External, types.External(true)) + assert.Equal(t, p.Networks["other-external-network"].External, types.External(true)) + assert.Equal(t, p.Networks["other-external-network"].Name, "my-cool-network") + } + expect(p) + assert.DeepEqual(t, p.Networks["other-external-network"].Extensions, types.Extensions{ + "x-bar": "baz", + "x-foo": "bar", + }) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/oom_test.go b/loader/tests/oom_test.go new file mode 100644 index 00000000..9b91b2d7 --- /dev/null +++ b/loader/tests/oom_test.go @@ -0,0 +1,62 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestOomKillDisable(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + oom_kill_disable: true +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].OomKillDisable, true) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestOomScoreAdj(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + oom_score_adj: 500 +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].OomScoreAdj, int64(500)) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/pids_limit_test.go b/loader/tests/pids_limit_test.go new file mode 100644 index 00000000..31024011 --- /dev/null +++ b/loader/tests/pids_limit_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestPidsLimit(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + pids_limit: 100 +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].PidsLimit, int64(100)) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/platform_test.go b/loader/tests/platform_test.go new file mode 100644 index 00000000..d04a05ed --- /dev/null +++ b/loader/tests/platform_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestPlatform(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + platform: linux/amd64 +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Platform, "linux/amd64") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/ports_test.go b/loader/tests/ports_test.go new file mode 100644 index 00000000..ec045af5 --- /dev/null +++ b/loader/tests/ports_test.go @@ -0,0 +1,126 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestPortsShortSyntax(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + ports: + - "80:8080" + - "90:8090/udp" + - 8600 +`) + expect := func(p *types.Project) { + ports := p.Services["foo"].Ports + assert.Equal(t, len(ports), 3) + assert.Equal(t, ports[0].Target, uint32(8080)) + assert.Equal(t, ports[0].Published, "80") + assert.Equal(t, ports[1].Target, uint32(8090)) + assert.Equal(t, ports[1].Protocol, "udp") + assert.Equal(t, ports[2].Target, uint32(8600)) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestPortsLongSyntax(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + ports: + - target: 80 + published: 8080 + protocol: tcp + mode: host +`) + expect := func(p *types.Project) { + ports := p.Services["foo"].Ports + assert.Equal(t, len(ports), 1) + assert.Equal(t, ports[0].Target, uint32(80)) + assert.Equal(t, ports[0].Published, "8080") + assert.Equal(t, ports[0].Protocol, "tcp") + assert.Equal(t, ports[0].Mode, "host") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestPortsRange(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + ports: + - "80-82:8080-8082" +`) + ports := p.Services["foo"].Ports + assert.Equal(t, len(ports), 3) + assert.Equal(t, ports[0].Target, uint32(8080)) + assert.Equal(t, ports[0].Published, "80") + assert.Equal(t, ports[1].Target, uint32(8081)) + assert.Equal(t, ports[1].Published, "81") + assert.Equal(t, ports[2].Target, uint32(8082)) + assert.Equal(t, ports[2].Published, "82") +} + +func TestNamedPort(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + ports: + - name: http + published: 8080 + target: 80 +`) + assert.Equal(t, p.Services["foo"].Ports[0].Name, "http") +} + +func TestAppProtocol(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + ports: + - published: 8080 + target: 80 + protocol: tcp + app_protocol: http +`) + assert.Equal(t, p.Services["foo"].Ports[0].AppProtocol, "http") +} diff --git a/loader/tests/privileged_test.go b/loader/tests/privileged_test.go new file mode 100644 index 00000000..61917d7c --- /dev/null +++ b/loader/tests/privileged_test.go @@ -0,0 +1,45 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestPrivilegedReadOnly(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + privileged: true + read_only: true +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Privileged, true) + assert.Equal(t, p.Services["foo"].ReadOnly, true) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/profiles_test.go b/loader/tests/profiles_test.go new file mode 100644 index 00000000..8b00c117 --- /dev/null +++ b/loader/tests/profiles_test.go @@ -0,0 +1,58 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "context" + "testing" + + "github.com/compose-spec/compose-go/v2/loader" + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestProfiles(t *testing.T) { + yaml := ` +name: test +services: + foo: + image: alpine + profiles: + - debug + - dev + bar: + image: alpine +` + // Without profiles, "foo" is disabled + p := load(t, yaml) + _, hasFoo := p.Services["foo"] + assert.Assert(t, !hasFoo) + assert.Equal(t, p.Services["bar"].Image, "alpine") + + // With matching profile, "foo" is enabled + p2, err := loader.LoadWithContext(context.TODO(), types.ConfigDetails{ + ConfigFiles: []types.ConfigFile{{Filename: "compose.yml", Content: []byte(yaml)}}, + Environment: map[string]string{}, + }, func(options *loader.Options) { + options.SkipConsistencyCheck = true + options.SkipNormalization = true + options.Profiles = []string{"debug"} + }) + assert.NilError(t, err) + assert.Equal(t, p2.Services["foo"].Image, "alpine") + assert.DeepEqual(t, p2.Services["foo"].Profiles, []string{"debug", "dev"}) +} diff --git a/loader/tests/provider_test.go b/loader/tests/provider_test.go new file mode 100644 index 00000000..5f55357a --- /dev/null +++ b/loader/tests/provider_test.go @@ -0,0 +1,54 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestProvider(t *testing.T) { + p := load(t, ` +name: test +services: + test: + provider: + type: foo + options: + bar: zot + strings: + - foo + - bar + numbers: + - 12 + - 34 + booleans: + - true + - false +`) + assert.DeepEqual(t, p.Services["test"].Provider, &types.ServiceProviderConfig{ + Type: "foo", + Options: types.MultiOptions{ + "bar": []string{"zot"}, + "strings": []string{"foo", "bar"}, + "numbers": []string{"12", "34"}, + "booleans": []string{"true", "false"}, + }, + }) +} diff --git a/loader/tests/pull_policy_test.go b/loader/tests/pull_policy_test.go new file mode 100644 index 00000000..f5a11728 --- /dev/null +++ b/loader/tests/pull_policy_test.go @@ -0,0 +1,57 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + "time" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestPullPolicy(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + pull_policy: always +`) + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].PullPolicy, "always") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestPullRefreshAfter(t *testing.T) { + p := load(t, ` +name: test +services: + test: + image: alpine + pull_policy: every_2d +`) + policy, duration, err := p.Services["test"].GetPullPolicy() + assert.NilError(t, err) + assert.Equal(t, policy, types.PullPolicyRefresh) + assert.Equal(t, duration, 2*24*time.Hour) +} diff --git a/loader/tests/restart_test.go b/loader/tests/restart_test.go new file mode 100644 index 00000000..ba4541d2 --- /dev/null +++ b/loader/tests/restart_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestRestart(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + restart: always +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Restart, "always") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/runtime_test.go b/loader/tests/runtime_test.go new file mode 100644 index 00000000..815e4676 --- /dev/null +++ b/loader/tests/runtime_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestRuntime(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + runtime: nvidia +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Runtime, "nvidia") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/scale_test.go b/loader/tests/scale_test.go new file mode 100644 index 00000000..4d818863 --- /dev/null +++ b/loader/tests/scale_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestScale(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + scale: 3 +`) + + expect := func(p *types.Project) { + assert.Equal(t, *p.Services["foo"].Scale, 3) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/secrets_test.go b/loader/tests/secrets_test.go new file mode 100644 index 00000000..84d29cf4 --- /dev/null +++ b/loader/tests/secrets_test.go @@ -0,0 +1,94 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestServiceSecrets(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + secrets: + - source: secret1 + target: /run/secrets/secret1 + - source: secret2 + target: my_secret + uid: '103' + gid: '103' + mode: 0440 +secrets: + secret1: + file: ./secret_data + secret2: + external: true +`) + secrets := p.Services["foo"].Secrets + assert.Equal(t, len(secrets), 2) + assert.Equal(t, secrets[0].Source, "secret1") + assert.Equal(t, secrets[0].Target, "/run/secrets/secret1") + assert.Equal(t, secrets[1].Source, "secret2") + assert.Equal(t, secrets[1].Target, "my_secret") + assert.Equal(t, secrets[1].UID, "103") + assert.Equal(t, secrets[1].GID, "103") + assert.Equal(t, *secrets[1].Mode, types.FileMode(0o440)) +} + +func TestTopLevelSecrets(t *testing.T) { + p := loadWithEnv(t, ` +name: test +services: + foo: + image: alpine +secrets: + secret1: + file: ./secret_data + labels: + foo: bar + secret2: + external: + name: my_secret + secret3: + external: true + secret4: + name: bar + environment: BAR + x-bar: baz + x-foo: bar + secret5: + file: /abs/secret_data +`, map[string]string{"BAR": "this is a secret"}) + + assert.DeepEqual(t, p.Secrets["secret1"].Labels, types.Labels{"foo": "bar"}) + assert.Equal(t, p.Secrets["secret2"].Name, "my_secret") + assert.Equal(t, p.Secrets["secret2"].External, types.External(true)) + assert.Equal(t, p.Secrets["secret3"].External, types.External(true)) + assert.Equal(t, p.Secrets["secret4"].Name, "bar") + assert.Equal(t, p.Secrets["secret4"].Environment, "BAR") + assert.Equal(t, p.Secrets["secret4"].Content, "this is a secret") + assert.DeepEqual(t, p.Secrets["secret4"].Extensions, types.Extensions{ + "x-bar": "baz", + "x-foo": "bar", + }) + assert.Equal(t, p.Secrets["secret5"].File, "/abs/secret_data") +} diff --git a/loader/tests/security_opt_test.go b/loader/tests/security_opt_test.go new file mode 100644 index 00000000..6373608e --- /dev/null +++ b/loader/tests/security_opt_test.go @@ -0,0 +1,46 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestSecurityOpt(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + security_opt: + - label=level:s0:c100,c200 + - label=type:svirt_apache_t +`) + + expect := func(p *types.Project) { + expected := []string{"label=level:s0:c100,c200", "label=type:svirt_apache_t"} + assert.DeepEqual(t, p.Services["foo"].SecurityOpt, expected) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/service_hooks_test.go b/loader/tests/service_hooks_test.go new file mode 100644 index 00000000..9adf1104 --- /dev/null +++ b/loader/tests/service_hooks_test.go @@ -0,0 +1,67 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestServiceHooks(t *testing.T) { + p := load(t, ` +name: test +services: + test: + image: alpine + post_start: + - command: echo start + user: root + privileged: true + working_dir: / + environment: + - FOO=BAR + pre_stop: + - command: echo stop + user: root + working_dir: / + environment: + FOO: BAR +`) + assert.DeepEqual(t, p.Services["test"].PostStart, []types.ServiceHook{ + { + Command: types.ShellCommand{"echo", "start"}, + User: "root", + Privileged: true, + WorkingDir: "/", + Environment: types.MappingWithEquals{ + "FOO": ptr("BAR"), + }, + }, + }) + assert.DeepEqual(t, p.Services["test"].PreStop, []types.ServiceHook{ + { + Command: types.ShellCommand{"echo", "stop"}, + User: "root", + WorkingDir: "/", + Environment: types.MappingWithEquals{ + "FOO": ptr("BAR"), + }, + }, + }) +} diff --git a/loader/tests/stdin_tty_test.go b/loader/tests/stdin_tty_test.go new file mode 100644 index 00000000..9135da6f --- /dev/null +++ b/loader/tests/stdin_tty_test.go @@ -0,0 +1,45 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestStdinOpenTty(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + stdin_open: true + tty: true +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].StdinOpen, true) + assert.Equal(t, p.Services["foo"].Tty, true) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/stop_test.go b/loader/tests/stop_test.go new file mode 100644 index 00000000..e0c44254 --- /dev/null +++ b/loader/tests/stop_test.go @@ -0,0 +1,61 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + "time" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestStopGracePeriod(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + stop_grace_period: 20s +`) + expect := func(p *types.Project) { + assert.Equal(t, *p.Services["foo"].StopGracePeriod, types.Duration(20*time.Second)) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestStopSignal(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + stop_signal: SIGUSR1 +`) + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].StopSignal, "SIGUSR1") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/storage_opt_test.go b/loader/tests/storage_opt_test.go new file mode 100644 index 00000000..0cc31f4e --- /dev/null +++ b/loader/tests/storage_opt_test.go @@ -0,0 +1,44 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestStorageOpt(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + storage_opt: + size: "20G" +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].StorageOpt, map[string]string{"size": "20G"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/sysctls_test.go b/loader/tests/sysctls_test.go new file mode 100644 index 00000000..b3f44bd6 --- /dev/null +++ b/loader/tests/sysctls_test.go @@ -0,0 +1,61 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestSysctls(t *testing.T) { + p := load(t, ` +name: test +services: + list: + image: busybox + sysctls: + - net.core.somaxconn=1024 + - net.ipv4.tcp_syncookies=0 + - testing.one.one= + - testing.one.two + map: + image: busybox + sysctls: + net.core.somaxconn: 1024 + net.ipv4.tcp_syncookies: 0 + testing.one.one: "" + testing.one.two: +`) + + expect := func(p *types.Project) { + expected := types.Mapping{ + "net.core.somaxconn": "1024", + "net.ipv4.tcp_syncookies": "0", + "testing.one.one": "", + "testing.one.two": "", + } + assert.DeepEqual(t, p.Services["list"].Sysctls, expected) + assert.DeepEqual(t, p.Services["map"].Sysctls, expected) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/template_driver_test.go b/loader/tests/template_driver_test.go new file mode 100644 index 00000000..7a9a5ca9 --- /dev/null +++ b/loader/tests/template_driver_test.go @@ -0,0 +1,106 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestTemplateDriver(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine +configs: + config: + name: config + external: true + template_driver: config-driver +secrets: + secret: + name: secret + external: true + template_driver: secret-driver +`) + assert.Equal(t, p.Configs["config"].TemplateDriver, "config-driver") + assert.Equal(t, p.Secrets["secret"].TemplateDriver, "secret-driver") + + yamlP, jsonP := roundTrip(t, p) + assert.Equal(t, yamlP.Configs["config"].TemplateDriver, "config-driver") + assert.Equal(t, yamlP.Secrets["secret"].TemplateDriver, "secret-driver") + assert.Equal(t, jsonP.Configs["config"].TemplateDriver, "config-driver") + assert.Equal(t, jsonP.Secrets["secret"].TemplateDriver, "secret-driver") +} + +func TestSecretDriver(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine +secrets: + secret: + name: secret + driver: secret-bucket + driver_opts: + OptionA: value for driver option A + OptionB: value for driver option B +`) + assert.Equal(t, p.Secrets["secret"].Driver, "secret-bucket") + assert.Equal(t, p.Secrets["secret"].DriverOpts["OptionA"], "value for driver option A") + assert.Equal(t, p.Secrets["secret"].DriverOpts["OptionB"], "value for driver option B") + + yamlP, jsonP := roundTrip(t, p) + assert.Equal(t, yamlP.Secrets["secret"].Driver, "secret-bucket") + assert.Equal(t, jsonP.Secrets["secret"].Driver, "secret-bucket") +} + +func TestConfigContent(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine +configs: + my_config: + content: | + some config content here +`) + assert.Equal(t, p.Configs["my_config"].Content, "some config content here\n") + + yamlP, jsonP := roundTrip(t, p) + assert.Equal(t, yamlP.Configs["my_config"].Content, "some config content here\n") + assert.Equal(t, jsonP.Configs["my_config"].Content, "some config content here\n") +} + +func TestConfigEnvironment(t *testing.T) { + p := loadWithEnv(t, ` +name: test +services: + foo: + image: alpine +configs: + my_config: + environment: MY_VAR +`, map[string]string{"MY_VAR": "my_value"}) + + assert.Equal(t, p.Configs["my_config"].Environment, "MY_VAR") + assert.Equal(t, p.Configs["my_config"].Content, "my_value") +} diff --git a/loader/tests/tmpfs_test.go b/loader/tests/tmpfs_test.go new file mode 100644 index 00000000..18eba974 --- /dev/null +++ b/loader/tests/tmpfs_test.go @@ -0,0 +1,49 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestTmpfs(t *testing.T) { + p := load(t, ` +name: test +services: + list: + image: alpine + tmpfs: + - /run + - /tmp + string: + image: alpine + tmpfs: /run +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["list"].Tmpfs, types.StringList{"/run", "/tmp"}) + assert.DeepEqual(t, p.Services["string"].Tmpfs, types.StringList{"/run"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/ulimits_test.go b/loader/tests/ulimits_test.go new file mode 100644 index 00000000..a785f999 --- /dev/null +++ b/loader/tests/ulimits_test.go @@ -0,0 +1,53 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestUlimits(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + ulimits: + nproc: 65535 + nofile: + soft: 20000 + hard: 40000 +`) + expect := func(p *types.Project) { + expected := map[string]*types.UlimitsConfig{ + "nproc": {Single: 65535}, + "nofile": { + Soft: 20000, + Hard: 40000, + }, + } + assert.DeepEqual(t, p.Services["foo"].Ulimits, expected) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/use_api_socket_test.go b/loader/tests/use_api_socket_test.go new file mode 100644 index 00000000..5f34222d --- /dev/null +++ b/loader/tests/use_api_socket_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestUseAPISocket(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + use_api_socket: true +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].UseAPISocket, true) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/user_test.go b/loader/tests/user_test.go new file mode 100644 index 00000000..b0a6be79 --- /dev/null +++ b/loader/tests/user_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestUser(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + user: someone +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].User, "someone") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/userns_mode_test.go b/loader/tests/userns_mode_test.go new file mode 100644 index 00000000..9a5d3c6d --- /dev/null +++ b/loader/tests/userns_mode_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestUsernsMode(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + userns_mode: host +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].UserNSMode, "host") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/volume_bind_test.go b/loader/tests/volume_bind_test.go new file mode 100644 index 00000000..fcdde2fb --- /dev/null +++ b/loader/tests/volume_bind_test.go @@ -0,0 +1,98 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestVolumeBindPropagation(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + volumes: + - type: bind + source: /host + target: /container + bind: + propagation: rslave +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Volumes[0].Bind.Propagation, "rslave") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestVolumeBindSELinux(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + volumes: + - type: bind + source: /host + target: /container + bind: + selinux: z +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Volumes[0].Bind.SELinux, "z") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} + +func TestVolumeNoCopy(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + volumes: + - type: volume + source: mydata + target: /data + volume: + nocopy: true +volumes: + mydata: +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].Volumes[0].Volume.NoCopy, true) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/volumes_from_test.go b/loader/tests/volumes_from_test.go new file mode 100644 index 00000000..7ec859f4 --- /dev/null +++ b/loader/tests/volumes_from_test.go @@ -0,0 +1,47 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestVolumesFrom(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + volumes_from: + - bar + - bar:ro + bar: + image: alpine +`) + + expect := func(p *types.Project) { + assert.DeepEqual(t, p.Services["foo"].VolumesFrom, []string{"bar", "bar:ro"}) + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/volumes_test.go b/loader/tests/volumes_test.go new file mode 100644 index 00000000..ccf22437 --- /dev/null +++ b/loader/tests/volumes_test.go @@ -0,0 +1,89 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestTopLevelVolumes(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine +volumes: + some-volume: + other-volume: + driver: flocker + driver_opts: + foo: "bar" + baz: 1 + labels: + foo: bar + another-volume: + name: "user_specified_name" + driver: vsphere + driver_opts: + foo: "bar" + baz: 1 + external-volume: + external: true + other-external-volume: + external: + name: my-cool-volume + external-volume3: + name: this-is-volume3 + external: true + x-bar: baz + x-foo: bar +`) + + expect := func(p *types.Project) { + // Simple volume + assert.Equal(t, p.Volumes["some-volume"].Driver, "") + + // Volume with driver and opts + other := p.Volumes["other-volume"] + assert.Equal(t, other.Driver, "flocker") + assert.DeepEqual(t, other.DriverOpts, types.Options{"foo": "bar", "baz": "1"}) + assert.DeepEqual(t, other.Labels, types.Labels{"foo": "bar"}) + + // Named volume + assert.Equal(t, p.Volumes["another-volume"].Name, "user_specified_name") + assert.Equal(t, p.Volumes["another-volume"].Driver, "vsphere") + + // External volumes + assert.Equal(t, p.Volumes["external-volume"].External, types.External(true)) + assert.Equal(t, p.Volumes["other-external-volume"].Name, "my-cool-volume") + assert.Equal(t, p.Volumes["other-external-volume"].External, types.External(true)) + assert.Equal(t, p.Volumes["external-volume3"].Name, "this-is-volume3") + assert.Equal(t, p.Volumes["external-volume3"].External, types.External(true)) + } + expect(p) + assert.DeepEqual(t, p.Volumes["external-volume3"].Extensions, types.Extensions{ + "x-bar": "baz", + "x-foo": "bar", + }) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/tests/working_dir_test.go b/loader/tests/working_dir_test.go new file mode 100644 index 00000000..74b7419f --- /dev/null +++ b/loader/tests/working_dir_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 The Compose Specification Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package tests + +import ( + "testing" + + "github.com/compose-spec/compose-go/v2/types" + "gotest.tools/v3/assert" +) + +func TestWorkingDir(t *testing.T) { + p := load(t, ` +name: test +services: + foo: + image: alpine + working_dir: /code +`) + + expect := func(p *types.Project) { + assert.Equal(t, p.Services["foo"].WorkingDir, "/code") + } + expect(p) + + yamlP, jsonP := roundTrip(t, p) + expect(yamlP) + expect(jsonP) +} diff --git a/loader/types_test.go b/loader/types_test.go deleted file mode 100644 index c7188e59..00000000 --- a/loader/types_test.go +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright 2020 The Compose Specification Authors. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package loader - -import ( - "context" - "os" - "testing" - - "gotest.tools/v3/assert" - is "gotest.tools/v3/assert/cmp" -) - -func TestMarshalProject(t *testing.T) { - workingDir, err := os.Getwd() - assert.NilError(t, err) - homeDir, err := os.UserHomeDir() - assert.NilError(t, err) - project := fullExampleProject(workingDir, homeDir) - expected := fullExampleYAML(workingDir, homeDir) - - actual, err := project.MarshalYAML() - assert.NilError(t, err) - assert.Check(t, is.Equal(expected, string(actual))) - - // Make sure the expected still - _, err = LoadWithContext(context.TODO(), buildConfigDetails(expected, map[string]string{}), func(options *Options) { - options.SkipNormalization = true - options.SkipConsistencyCheck = true - }) - assert.NilError(t, err) -} - -func TestJSONMarshalProject(t *testing.T) { - workingDir, err := os.Getwd() - assert.NilError(t, err) - homeDir, err := os.UserHomeDir() - assert.NilError(t, err) - - project := fullExampleProject(workingDir, homeDir) - expected := fullExampleJSON(workingDir, homeDir) - - actual, err := project.MarshalJSON() - assert.NilError(t, err) - assert.Check(t, is.Equal(expected, string(actual))) - - _, err = LoadWithContext(context.TODO(), buildConfigDetails(expected, map[string]string{}), func(options *Options) { - options.SkipNormalization = true - options.SkipConsistencyCheck = true - }) - assert.NilError(t, err) -}