github.com/panekj/cli@v0.0.0-20230304125325-467dd2f3797e/cli/compose/loader/full-struct_test.go (about)

     1  package loader
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"time"
     7  
     8  	"github.com/docker/cli/cli/compose/types"
     9  )
    10  
    11  func fullExampleConfig(workingDir, homeDir string) *types.Config {
    12  	return &types.Config{
    13  		Version:  "3.10",
    14  		Services: services(workingDir, homeDir),
    15  		Networks: networks(),
    16  		Volumes:  volumes(),
    17  		Configs:  configs(workingDir),
    18  		Secrets:  secrets(workingDir),
    19  		Extras: map[string]interface{}{
    20  			"x-foo": "bar",
    21  			"x-bar": "baz",
    22  			"x-nested": map[string]interface{}{
    23  				"foo": "bar",
    24  				"bar": "baz",
    25  			},
    26  		},
    27  	}
    28  }
    29  
    30  func services(workingDir, homeDir string) []types.ServiceConfig {
    31  	return []types.ServiceConfig{
    32  		{
    33  			Name: "foo",
    34  
    35  			Build: types.BuildConfig{
    36  				Context:    "./dir",
    37  				Dockerfile: "Dockerfile",
    38  				Args:       map[string]*string{"foo": strPtr("bar")},
    39  				Target:     "foo",
    40  				Network:    "foo",
    41  				CacheFrom:  []string{"foo", "bar"},
    42  				ExtraHosts: types.HostsList{
    43  					"ipv4.example.com:127.0.0.1",
    44  					"ipv6.example.com:::1",
    45  				},
    46  				Labels: map[string]string{"FOO": "BAR"},
    47  			},
    48  			CapAdd:       []string{"ALL"},
    49  			CapDrop:      []string{"NET_ADMIN", "SYS_ADMIN"},
    50  			CgroupParent: "m-executor-abcd",
    51  			Command:      []string{"bundle", "exec", "thin", "-p", "3000"},
    52  			Configs: []types.ServiceConfigObjConfig{
    53  				{
    54  					Source: "config1",
    55  				},
    56  				{
    57  					Source: "config2",
    58  					Target: "/my_config",
    59  					UID:    "103",
    60  					GID:    "103",
    61  					Mode:   uint32Ptr(0o440),
    62  				},
    63  			},
    64  			ContainerName: "my-web-container",
    65  			DependsOn:     []string{"db", "redis"},
    66  			Deploy: types.DeployConfig{
    67  				Mode:     "replicated",
    68  				Replicas: uint64Ptr(6),
    69  				Labels:   map[string]string{"FOO": "BAR"},
    70  				RollbackConfig: &types.UpdateConfig{
    71  					Parallelism:     uint64Ptr(3),
    72  					Delay:           types.Duration(10 * time.Second),
    73  					FailureAction:   "continue",
    74  					Monitor:         types.Duration(60 * time.Second),
    75  					MaxFailureRatio: 0.3,
    76  					Order:           "start-first",
    77  				},
    78  				UpdateConfig: &types.UpdateConfig{
    79  					Parallelism:     uint64Ptr(3),
    80  					Delay:           types.Duration(10 * time.Second),
    81  					FailureAction:   "continue",
    82  					Monitor:         types.Duration(60 * time.Second),
    83  					MaxFailureRatio: 0.3,
    84  					Order:           "start-first",
    85  				},
    86  				Resources: types.Resources{
    87  					Limits: &types.ResourceLimit{
    88  						NanoCPUs:    "0.001",
    89  						MemoryBytes: 50 * 1024 * 1024,
    90  						Pids:        100,
    91  					},
    92  					Reservations: &types.Resource{
    93  						NanoCPUs:    "0.0001",
    94  						MemoryBytes: 20 * 1024 * 1024,
    95  						GenericResources: []types.GenericResource{
    96  							{
    97  								DiscreteResourceSpec: &types.DiscreteGenericResource{
    98  									Kind:  "gpu",
    99  									Value: 2,
   100  								},
   101  							},
   102  							{
   103  								DiscreteResourceSpec: &types.DiscreteGenericResource{
   104  									Kind:  "ssd",
   105  									Value: 1,
   106  								},
   107  							},
   108  						},
   109  					},
   110  				},
   111  				RestartPolicy: &types.RestartPolicy{
   112  					Condition:   "on-failure",
   113  					Delay:       durationPtr(5 * time.Second),
   114  					MaxAttempts: uint64Ptr(3),
   115  					Window:      durationPtr(2 * time.Minute),
   116  				},
   117  				Placement: types.Placement{
   118  					Constraints: []string{"node=foo"},
   119  					MaxReplicas: uint64(5),
   120  					Preferences: []types.PlacementPreferences{
   121  						{
   122  							Spread: "node.labels.az",
   123  						},
   124  					},
   125  				},
   126  				EndpointMode: "dnsrr",
   127  			},
   128  			Devices:    []string{"/dev/ttyUSB0:/dev/ttyUSB0"},
   129  			DNS:        []string{"8.8.8.8", "9.9.9.9"},
   130  			DNSSearch:  []string{"dc1.example.com", "dc2.example.com"},
   131  			DomainName: "foo.com",
   132  			Entrypoint: []string{"/code/entrypoint.sh", "-p", "3000"},
   133  			Environment: map[string]*string{
   134  				"FOO": strPtr("foo_from_env_file"),
   135  				"BAR": strPtr("bar_from_env_file_2"),
   136  				"BAZ": strPtr("baz_from_service_def"),
   137  				"QUX": strPtr("qux_from_environment"),
   138  			},
   139  			EnvFile: []string{
   140  				"./example1.env",
   141  				"./example2.env",
   142  			},
   143  			Expose: []string{"3000", "8000"},
   144  			ExternalLinks: []string{
   145  				"redis_1",
   146  				"project_db_1:mysql",
   147  				"project_db_1:postgresql",
   148  			},
   149  			ExtraHosts: []string{
   150  				"somehost:162.242.195.82",
   151  				"otherhost:50.31.209.229",
   152  				"host.docker.internal:host-gateway",
   153  			},
   154  			Extras: map[string]interface{}{
   155  				"x-bar": "baz",
   156  				"x-foo": "bar",
   157  			},
   158  			HealthCheck: &types.HealthCheckConfig{
   159  				Test:        types.HealthCheckTest([]string{"CMD-SHELL", "echo \"hello world\""}),
   160  				Interval:    durationPtr(10 * time.Second),
   161  				Timeout:     durationPtr(1 * time.Second),
   162  				Retries:     uint64Ptr(5),
   163  				StartPeriod: durationPtr(15 * time.Second),
   164  			},
   165  			Hostname: "foo",
   166  			Image:    "redis",
   167  			Ipc:      "host",
   168  			Labels: map[string]string{
   169  				"com.example.description": "Accounting webapp",
   170  				"com.example.number":      "42",
   171  				"com.example.empty-label": "",
   172  			},
   173  			Links: []string{
   174  				"db",
   175  				"db:database",
   176  				"redis",
   177  			},
   178  			Logging: &types.LoggingConfig{
   179  				Driver: "syslog",
   180  				Options: map[string]string{
   181  					"syslog-address": "tcp://192.168.0.42:123",
   182  				},
   183  			},
   184  			MacAddress:  "02:42:ac:11:65:43",
   185  			NetworkMode: "container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b",
   186  			Networks: map[string]*types.ServiceNetworkConfig{
   187  				"some-network": {
   188  					Aliases:     []string{"alias1", "alias3"},
   189  					Ipv4Address: "",
   190  					Ipv6Address: "",
   191  				},
   192  				"other-network": {
   193  					Ipv4Address: "172.16.238.10",
   194  					Ipv6Address: "2001:3984:3989::10",
   195  				},
   196  				"other-other-network": nil,
   197  			},
   198  			Pid: "host",
   199  			Ports: []types.ServicePortConfig{
   200  				// "3000",
   201  				{
   202  					Mode:     "ingress",
   203  					Target:   3000,
   204  					Protocol: "tcp",
   205  				},
   206  				{
   207  					Mode:     "ingress",
   208  					Target:   3001,
   209  					Protocol: "tcp",
   210  				},
   211  				{
   212  					Mode:     "ingress",
   213  					Target:   3002,
   214  					Protocol: "tcp",
   215  				},
   216  				{
   217  					Mode:     "ingress",
   218  					Target:   3003,
   219  					Protocol: "tcp",
   220  				},
   221  				{
   222  					Mode:     "ingress",
   223  					Target:   3004,
   224  					Protocol: "tcp",
   225  				},
   226  				{
   227  					Mode:     "ingress",
   228  					Target:   3005,
   229  					Protocol: "tcp",
   230  				},
   231  				// "8000:8000",
   232  				{
   233  					Mode:      "ingress",
   234  					Target:    8000,
   235  					Published: 8000,
   236  					Protocol:  "tcp",
   237  				},
   238  				// "9090-9091:8080-8081",
   239  				{
   240  					Mode:      "ingress",
   241  					Target:    8080,
   242  					Published: 9090,
   243  					Protocol:  "tcp",
   244  				},
   245  				{
   246  					Mode:      "ingress",
   247  					Target:    8081,
   248  					Published: 9091,
   249  					Protocol:  "tcp",
   250  				},
   251  				// "49100:22",
   252  				{
   253  					Mode:      "ingress",
   254  					Target:    22,
   255  					Published: 49100,
   256  					Protocol:  "tcp",
   257  				},
   258  				// "127.0.0.1:8001:8001",
   259  				{
   260  					Mode:      "ingress",
   261  					Target:    8001,
   262  					Published: 8001,
   263  					Protocol:  "tcp",
   264  				},
   265  				// "127.0.0.1:5000-5010:5000-5010",
   266  				{
   267  					Mode:      "ingress",
   268  					Target:    5000,
   269  					Published: 5000,
   270  					Protocol:  "tcp",
   271  				},
   272  				{
   273  					Mode:      "ingress",
   274  					Target:    5001,
   275  					Published: 5001,
   276  					Protocol:  "tcp",
   277  				},
   278  				{
   279  					Mode:      "ingress",
   280  					Target:    5002,
   281  					Published: 5002,
   282  					Protocol:  "tcp",
   283  				},
   284  				{
   285  					Mode:      "ingress",
   286  					Target:    5003,
   287  					Published: 5003,
   288  					Protocol:  "tcp",
   289  				},
   290  				{
   291  					Mode:      "ingress",
   292  					Target:    5004,
   293  					Published: 5004,
   294  					Protocol:  "tcp",
   295  				},
   296  				{
   297  					Mode:      "ingress",
   298  					Target:    5005,
   299  					Published: 5005,
   300  					Protocol:  "tcp",
   301  				},
   302  				{
   303  					Mode:      "ingress",
   304  					Target:    5006,
   305  					Published: 5006,
   306  					Protocol:  "tcp",
   307  				},
   308  				{
   309  					Mode:      "ingress",
   310  					Target:    5007,
   311  					Published: 5007,
   312  					Protocol:  "tcp",
   313  				},
   314  				{
   315  					Mode:      "ingress",
   316  					Target:    5008,
   317  					Published: 5008,
   318  					Protocol:  "tcp",
   319  				},
   320  				{
   321  					Mode:      "ingress",
   322  					Target:    5009,
   323  					Published: 5009,
   324  					Protocol:  "tcp",
   325  				},
   326  				{
   327  					Mode:      "ingress",
   328  					Target:    5010,
   329  					Published: 5010,
   330  					Protocol:  "tcp",
   331  				},
   332  			},
   333  			Privileged: true,
   334  			ReadOnly:   true,
   335  			Restart:    "always",
   336  			Secrets: []types.ServiceSecretConfig{
   337  				{
   338  					Source: "secret1",
   339  				},
   340  				{
   341  					Source: "secret2",
   342  					Target: "my_secret",
   343  					UID:    "103",
   344  					GID:    "103",
   345  					Mode:   uint32Ptr(0o440),
   346  				},
   347  			},
   348  			SecurityOpt: []string{
   349  				"label=level:s0:c100,c200",
   350  				"label=type:svirt_apache_t",
   351  			},
   352  			StdinOpen:       true,
   353  			StopSignal:      "SIGUSR1",
   354  			StopGracePeriod: durationPtr(20 * time.Second),
   355  			Sysctls: map[string]string{
   356  				"net.core.somaxconn":      "1024",
   357  				"net.ipv4.tcp_syncookies": "0",
   358  			},
   359  			Tmpfs: []string{"/run", "/tmp"},
   360  			Tty:   true,
   361  			Ulimits: map[string]*types.UlimitsConfig{
   362  				"nproc": {
   363  					Single: 65535,
   364  				},
   365  				"nofile": {
   366  					Soft: 20000,
   367  					Hard: 40000,
   368  				},
   369  			},
   370  			User: "someone",
   371  			Volumes: []types.ServiceVolumeConfig{
   372  				{Target: "/var/lib/mysql", Type: "volume"},
   373  				{Source: "/opt/data", Target: "/var/lib/mysql", Type: "bind"},
   374  				{Source: workingDir, Target: "/code", Type: "bind"},
   375  				{Source: filepath.Join(workingDir, "static"), Target: "/var/www/html", Type: "bind"},
   376  				{Source: homeDir + "/configs", Target: "/etc/configs/", Type: "bind", ReadOnly: true},
   377  				{Source: "datavolume", Target: "/var/lib/mysql", Type: "volume"},
   378  				{Source: filepath.Join(workingDir, "opt"), Target: "/opt", Consistency: "cached", Type: "bind"},
   379  				{Target: "/opt", Type: "tmpfs", Tmpfs: &types.ServiceVolumeTmpfs{
   380  					Size: int64(10000),
   381  				}},
   382  				{Source: "group:mygroup", Target: "/srv", Type: "cluster"},
   383  			},
   384  			WorkingDir: "/code",
   385  		},
   386  	}
   387  }
   388  
   389  func networks() map[string]types.NetworkConfig {
   390  	return map[string]types.NetworkConfig{
   391  		"some-network": {},
   392  
   393  		"other-network": {
   394  			Driver: "overlay",
   395  			DriverOpts: map[string]string{
   396  				"foo": "bar",
   397  				"baz": "1",
   398  			},
   399  			Ipam: types.IPAMConfig{
   400  				Driver: "overlay",
   401  				Config: []*types.IPAMPool{
   402  					{Subnet: "172.16.238.0/24"},
   403  					{Subnet: "2001:3984:3989::/64"},
   404  				},
   405  			},
   406  			Labels: map[string]string{
   407  				"foo": "bar",
   408  			},
   409  		},
   410  
   411  		"external-network": {
   412  			Name:     "external-network",
   413  			External: types.External{External: true},
   414  		},
   415  
   416  		"other-external-network": {
   417  			Name:     "my-cool-network",
   418  			External: types.External{External: true},
   419  			Extras: map[string]interface{}{
   420  				"x-bar": "baz",
   421  				"x-foo": "bar",
   422  			},
   423  		},
   424  	}
   425  }
   426  
   427  func volumes() map[string]types.VolumeConfig {
   428  	return map[string]types.VolumeConfig{
   429  		"some-volume": {},
   430  		"other-volume": {
   431  			Driver: "flocker",
   432  			DriverOpts: map[string]string{
   433  				"foo": "bar",
   434  				"baz": "1",
   435  			},
   436  			Labels: map[string]string{
   437  				"foo": "bar",
   438  			},
   439  		},
   440  		"another-volume": {
   441  			Name:   "user_specified_name",
   442  			Driver: "vsphere",
   443  			DriverOpts: map[string]string{
   444  				"foo": "bar",
   445  				"baz": "1",
   446  			},
   447  		},
   448  		"external-volume": {
   449  			Name:     "external-volume",
   450  			External: types.External{External: true},
   451  		},
   452  		"other-external-volume": {
   453  			Name:     "my-cool-volume",
   454  			External: types.External{External: true},
   455  		},
   456  		"external-volume3": {
   457  			Name:     "this-is-volume3",
   458  			External: types.External{External: true},
   459  			Extras: map[string]interface{}{
   460  				"x-bar": "baz",
   461  				"x-foo": "bar",
   462  			},
   463  		},
   464  		"cluster-volume": {
   465  			Driver: "my-csi-driver",
   466  			Spec: &types.ClusterVolumeSpec{
   467  				Group: "mygroup",
   468  				AccessMode: &types.AccessMode{
   469  					Scope:       "single",
   470  					Sharing:     "none",
   471  					BlockVolume: &types.BlockVolume{},
   472  				},
   473  				AccessibilityRequirements: &types.TopologyRequirement{
   474  					Requisite: []types.Topology{
   475  						{
   476  							Segments: types.Mapping{"region": "R1", "zone": "Z1"},
   477  						},
   478  						{
   479  							Segments: types.Mapping{"region": "R1", "zone": "Z2"},
   480  						},
   481  					},
   482  					Preferred: []types.Topology{
   483  						{
   484  							Segments: types.Mapping{"region": "R1", "zone": "Z1"},
   485  						},
   486  					},
   487  				},
   488  				CapacityRange: &types.CapacityRange{
   489  					RequiredBytes: types.UnitBytes(1 * 1024 * 1024 * 1024),
   490  					LimitBytes:    types.UnitBytes(8 * 1024 * 1024 * 1024),
   491  				},
   492  				Secrets: []types.VolumeSecret{
   493  					{Key: "mycsisecret", Secret: "secret1"},
   494  					{Key: "mycsisecret2", Secret: "secret4"},
   495  				},
   496  				Availability: "active",
   497  			},
   498  		},
   499  	}
   500  }
   501  
   502  func configs(workingDir string) map[string]types.ConfigObjConfig {
   503  	return map[string]types.ConfigObjConfig{
   504  		"config1": {
   505  			File: filepath.Join(workingDir, "config_data"),
   506  			Labels: map[string]string{
   507  				"foo": "bar",
   508  			},
   509  		},
   510  		"config2": {
   511  			Name:     "my_config",
   512  			External: types.External{External: true},
   513  		},
   514  		"config3": {
   515  			Name:     "config3",
   516  			External: types.External{External: true},
   517  		},
   518  		"config4": {
   519  			Name: "foo",
   520  			File: workingDir,
   521  			Extras: map[string]interface{}{
   522  				"x-bar": "baz",
   523  				"x-foo": "bar",
   524  			},
   525  		},
   526  	}
   527  }
   528  
   529  func secrets(workingDir string) map[string]types.SecretConfig {
   530  	return map[string]types.SecretConfig{
   531  		"secret1": {
   532  			File: filepath.Join(workingDir, "secret_data"),
   533  			Labels: map[string]string{
   534  				"foo": "bar",
   535  			},
   536  		},
   537  		"secret2": {
   538  			Name:     "my_secret",
   539  			External: types.External{External: true},
   540  		},
   541  		"secret3": {
   542  			Name:     "secret3",
   543  			External: types.External{External: true},
   544  		},
   545  		"secret4": {
   546  			Name: "bar",
   547  			File: workingDir,
   548  			Extras: map[string]interface{}{
   549  				"x-bar": "baz",
   550  				"x-foo": "bar",
   551  			},
   552  		},
   553  	}
   554  }
   555  
   556  func fullExampleYAML(workingDir string) string {
   557  	return fmt.Sprintf(`version: "3.10"
   558  services:
   559    foo:
   560      build:
   561        context: ./dir
   562        dockerfile: Dockerfile
   563        args:
   564          foo: bar
   565        labels:
   566          FOO: BAR
   567        cache_from:
   568        - foo
   569        - bar
   570        extra_hosts:
   571        - ipv4.example.com:127.0.0.1
   572        - ipv6.example.com:::1
   573        network: foo
   574        target: foo
   575      cap_add:
   576      - ALL
   577      cap_drop:
   578      - NET_ADMIN
   579      - SYS_ADMIN
   580      cgroup_parent: m-executor-abcd
   581      command:
   582      - bundle
   583      - exec
   584      - thin
   585      - -p
   586      - "3000"
   587      configs:
   588      - source: config1
   589      - source: config2
   590        target: /my_config
   591        uid: "103"
   592        gid: "103"
   593        mode: 288
   594      container_name: my-web-container
   595      depends_on:
   596      - db
   597      - redis
   598      deploy:
   599        mode: replicated
   600        replicas: 6
   601        labels:
   602          FOO: BAR
   603        update_config:
   604          parallelism: 3
   605          delay: 10s
   606          failure_action: continue
   607          monitor: 1m0s
   608          max_failure_ratio: 0.3
   609          order: start-first
   610        rollback_config:
   611          parallelism: 3
   612          delay: 10s
   613          failure_action: continue
   614          monitor: 1m0s
   615          max_failure_ratio: 0.3
   616          order: start-first
   617        resources:
   618          limits:
   619            cpus: "0.001"
   620            memory: "52428800"
   621            pids: 100
   622          reservations:
   623            cpus: "0.0001"
   624            memory: "20971520"
   625            generic_resources:
   626            - discrete_resource_spec:
   627                kind: gpu
   628                value: 2
   629            - discrete_resource_spec:
   630                kind: ssd
   631                value: 1
   632        restart_policy:
   633          condition: on-failure
   634          delay: 5s
   635          max_attempts: 3
   636          window: 2m0s
   637        placement:
   638          constraints:
   639          - node=foo
   640          preferences:
   641          - spread: node.labels.az
   642          max_replicas_per_node: 5
   643        endpoint_mode: dnsrr
   644      devices:
   645      - /dev/ttyUSB0:/dev/ttyUSB0
   646      dns:
   647      - 8.8.8.8
   648      - 9.9.9.9
   649      dns_search:
   650      - dc1.example.com
   651      - dc2.example.com
   652      domainname: foo.com
   653      entrypoint:
   654      - /code/entrypoint.sh
   655      - -p
   656      - "3000"
   657      environment:
   658        BAR: bar_from_env_file_2
   659        BAZ: baz_from_service_def
   660        FOO: foo_from_env_file
   661        QUX: qux_from_environment
   662      env_file:
   663      - ./example1.env
   664      - ./example2.env
   665      expose:
   666      - "3000"
   667      - "8000"
   668      external_links:
   669      - redis_1
   670      - project_db_1:mysql
   671      - project_db_1:postgresql
   672      extra_hosts:
   673      - somehost:162.242.195.82
   674      - otherhost:50.31.209.229
   675      - host.docker.internal:host-gateway
   676      hostname: foo
   677      healthcheck:
   678        test:
   679        - CMD-SHELL
   680        - echo "hello world"
   681        timeout: 1s
   682        interval: 10s
   683        retries: 5
   684        start_period: 15s
   685      image: redis
   686      ipc: host
   687      labels:
   688        com.example.description: Accounting webapp
   689        com.example.empty-label: ""
   690        com.example.number: "42"
   691      links:
   692      - db
   693      - db:database
   694      - redis
   695      logging:
   696        driver: syslog
   697        options:
   698          syslog-address: tcp://192.168.0.42:123
   699      mac_address: 02:42:ac:11:65:43
   700      network_mode: container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b
   701      networks:
   702        other-network:
   703          ipv4_address: 172.16.238.10
   704          ipv6_address: 2001:3984:3989::10
   705        other-other-network: null
   706        some-network:
   707          aliases:
   708          - alias1
   709          - alias3
   710      pid: host
   711      ports:
   712      - mode: ingress
   713        target: 3000
   714        protocol: tcp
   715      - mode: ingress
   716        target: 3001
   717        protocol: tcp
   718      - mode: ingress
   719        target: 3002
   720        protocol: tcp
   721      - mode: ingress
   722        target: 3003
   723        protocol: tcp
   724      - mode: ingress
   725        target: 3004
   726        protocol: tcp
   727      - mode: ingress
   728        target: 3005
   729        protocol: tcp
   730      - mode: ingress
   731        target: 8000
   732        published: 8000
   733        protocol: tcp
   734      - mode: ingress
   735        target: 8080
   736        published: 9090
   737        protocol: tcp
   738      - mode: ingress
   739        target: 8081
   740        published: 9091
   741        protocol: tcp
   742      - mode: ingress
   743        target: 22
   744        published: 49100
   745        protocol: tcp
   746      - mode: ingress
   747        target: 8001
   748        published: 8001
   749        protocol: tcp
   750      - mode: ingress
   751        target: 5000
   752        published: 5000
   753        protocol: tcp
   754      - mode: ingress
   755        target: 5001
   756        published: 5001
   757        protocol: tcp
   758      - mode: ingress
   759        target: 5002
   760        published: 5002
   761        protocol: tcp
   762      - mode: ingress
   763        target: 5003
   764        published: 5003
   765        protocol: tcp
   766      - mode: ingress
   767        target: 5004
   768        published: 5004
   769        protocol: tcp
   770      - mode: ingress
   771        target: 5005
   772        published: 5005
   773        protocol: tcp
   774      - mode: ingress
   775        target: 5006
   776        published: 5006
   777        protocol: tcp
   778      - mode: ingress
   779        target: 5007
   780        published: 5007
   781        protocol: tcp
   782      - mode: ingress
   783        target: 5008
   784        published: 5008
   785        protocol: tcp
   786      - mode: ingress
   787        target: 5009
   788        published: 5009
   789        protocol: tcp
   790      - mode: ingress
   791        target: 5010
   792        published: 5010
   793        protocol: tcp
   794      privileged: true
   795      read_only: true
   796      restart: always
   797      secrets:
   798      - source: secret1
   799      - source: secret2
   800        target: my_secret
   801        uid: "103"
   802        gid: "103"
   803        mode: 288
   804      security_opt:
   805      - label=level:s0:c100,c200
   806      - label=type:svirt_apache_t
   807      stdin_open: true
   808      stop_grace_period: 20s
   809      stop_signal: SIGUSR1
   810      sysctls:
   811        net.core.somaxconn: "1024"
   812        net.ipv4.tcp_syncookies: "0"
   813      tmpfs:
   814      - /run
   815      - /tmp
   816      tty: true
   817      ulimits:
   818        nofile:
   819          soft: 20000
   820          hard: 40000
   821        nproc: 65535
   822      user: someone
   823      volumes:
   824      - type: volume
   825        target: /var/lib/mysql
   826      - type: bind
   827        source: /opt/data
   828        target: /var/lib/mysql
   829      - type: bind
   830        source: /foo
   831        target: /code
   832      - type: bind
   833        source: %s
   834        target: /var/www/html
   835      - type: bind
   836        source: /bar/configs
   837        target: /etc/configs/
   838        read_only: true
   839      - type: volume
   840        source: datavolume
   841        target: /var/lib/mysql
   842      - type: bind
   843        source: %s
   844        target: /opt
   845        consistency: cached
   846      - type: tmpfs
   847        target: /opt
   848        tmpfs:
   849          size: 10000
   850      - type: cluster
   851        source: group:mygroup
   852        target: /srv
   853      working_dir: /code
   854      x-bar: baz
   855      x-foo: bar
   856  networks:
   857    external-network:
   858      name: external-network
   859      external: true
   860    other-external-network:
   861      name: my-cool-network
   862      external: true
   863      x-bar: baz
   864      x-foo: bar
   865    other-network:
   866      driver: overlay
   867      driver_opts:
   868        baz: "1"
   869        foo: bar
   870      ipam:
   871        driver: overlay
   872        config:
   873        - subnet: 172.16.238.0/24
   874        - subnet: 2001:3984:3989::/64
   875      labels:
   876        foo: bar
   877    some-network: {}
   878  volumes:
   879    another-volume:
   880      name: user_specified_name
   881      driver: vsphere
   882      driver_opts:
   883        baz: "1"
   884        foo: bar
   885    cluster-volume:
   886      driver: my-csi-driver
   887      x-cluster-spec:
   888        group: mygroup
   889        access_mode:
   890          scope: single
   891          sharing: none
   892          block_volume: {}
   893        accessibility_requirements:
   894          requisite:
   895          - segments:
   896              region: R1
   897              zone: Z1
   898          - segments:
   899              region: R1
   900              zone: Z2
   901          preferred:
   902          - segments:
   903              region: R1
   904              zone: Z1
   905        capacity_range:
   906          required_bytes: "1073741824"
   907          limit_bytes: "8589934592"
   908        secrets:
   909        - key: mycsisecret
   910          secret: secret1
   911        - key: mycsisecret2
   912          secret: secret4
   913        availability: active
   914    external-volume:
   915      name: external-volume
   916      external: true
   917    external-volume3:
   918      name: this-is-volume3
   919      external: true
   920      x-bar: baz
   921      x-foo: bar
   922    other-external-volume:
   923      name: my-cool-volume
   924      external: true
   925    other-volume:
   926      driver: flocker
   927      driver_opts:
   928        baz: "1"
   929        foo: bar
   930      labels:
   931        foo: bar
   932    some-volume: {}
   933  secrets:
   934    secret1:
   935      file: %s/secret_data
   936      labels:
   937        foo: bar
   938    secret2:
   939      name: my_secret
   940      external: true
   941    secret3:
   942      name: secret3
   943      external: true
   944    secret4:
   945      name: bar
   946      file: %s
   947      x-bar: baz
   948      x-foo: bar
   949  configs:
   950    config1:
   951      file: %s/config_data
   952      labels:
   953        foo: bar
   954    config2:
   955      name: my_config
   956      external: true
   957    config3:
   958      name: config3
   959      external: true
   960    config4:
   961      name: foo
   962      file: %s
   963      x-bar: baz
   964      x-foo: bar
   965  x-bar: baz
   966  x-foo: bar
   967  x-nested:
   968    bar: baz
   969    foo: bar
   970  `,
   971  		filepath.Join(workingDir, "static"),
   972  		filepath.Join(workingDir, "opt"),
   973  		workingDir,
   974  		workingDir,
   975  		workingDir,
   976  		workingDir)
   977  }
   978  
   979  func fullExampleJSON(workingDir string) string {
   980  	return fmt.Sprintf(`{
   981    "configs": {
   982      "config1": {
   983        "file": "%s/config_data",
   984        "external": false,
   985        "labels": {
   986          "foo": "bar"
   987        }
   988      },
   989      "config2": {
   990        "name": "my_config",
   991        "external": true
   992      },
   993      "config3": {
   994        "name": "config3",
   995        "external": true
   996      },
   997      "config4": {
   998        "name": "foo",
   999        "file": "%s",
  1000        "external": false
  1001      }
  1002    },
  1003    "networks": {
  1004      "external-network": {
  1005        "name": "external-network",
  1006        "ipam": {},
  1007        "external": true
  1008      },
  1009      "other-external-network": {
  1010        "name": "my-cool-network",
  1011        "ipam": {},
  1012        "external": true
  1013      },
  1014      "other-network": {
  1015        "driver": "overlay",
  1016        "driver_opts": {
  1017          "baz": "1",
  1018          "foo": "bar"
  1019        },
  1020        "ipam": {
  1021          "driver": "overlay",
  1022          "config": [
  1023            {
  1024              "subnet": "172.16.238.0/24"
  1025            },
  1026            {
  1027              "subnet": "2001:3984:3989::/64"
  1028            }
  1029          ]
  1030        },
  1031        "external": false,
  1032        "labels": {
  1033          "foo": "bar"
  1034        }
  1035      },
  1036      "some-network": {
  1037        "ipam": {},
  1038        "external": false
  1039      }
  1040    },
  1041    "secrets": {
  1042      "secret1": {
  1043        "file": "%s/secret_data",
  1044        "external": false,
  1045        "labels": {
  1046          "foo": "bar"
  1047        }
  1048      },
  1049      "secret2": {
  1050        "name": "my_secret",
  1051        "external": true
  1052      },
  1053      "secret3": {
  1054        "name": "secret3",
  1055        "external": true
  1056      },
  1057      "secret4": {
  1058        "name": "bar",
  1059        "file": "%s",
  1060        "external": false
  1061      }
  1062    },
  1063    "services": {
  1064      "foo": {
  1065        "build": {
  1066          "context": "./dir",
  1067          "dockerfile": "Dockerfile",
  1068          "args": {
  1069            "foo": "bar"
  1070          },
  1071          "labels": {
  1072            "FOO": "BAR"
  1073          },
  1074          "cache_from": [
  1075            "foo",
  1076            "bar"
  1077          ],
  1078          "extra_hosts": [
  1079            "ipv4.example.com:127.0.0.1",
  1080            "ipv6.example.com:::1"
  1081          ],
  1082          "network": "foo",
  1083          "target": "foo"
  1084        },
  1085        "cap_add": [
  1086          "ALL"
  1087        ],
  1088        "cap_drop": [
  1089          "NET_ADMIN",
  1090          "SYS_ADMIN"
  1091        ],
  1092        "cgroup_parent": "m-executor-abcd",
  1093        "command": [
  1094          "bundle",
  1095          "exec",
  1096          "thin",
  1097          "-p",
  1098          "3000"
  1099        ],
  1100        "configs": [
  1101          {
  1102            "source": "config1"
  1103          },
  1104          {
  1105            "source": "config2",
  1106            "target": "/my_config",
  1107            "uid": "103",
  1108            "gid": "103",
  1109            "mode": 288
  1110          }
  1111        ],
  1112        "container_name": "my-web-container",
  1113        "credential_spec": {},
  1114        "depends_on": [
  1115          "db",
  1116          "redis"
  1117        ],
  1118        "deploy": {
  1119          "mode": "replicated",
  1120          "replicas": 6,
  1121          "labels": {
  1122            "FOO": "BAR"
  1123          },
  1124          "update_config": {
  1125            "parallelism": 3,
  1126            "delay": "10s",
  1127            "failure_action": "continue",
  1128            "monitor": "1m0s",
  1129            "max_failure_ratio": 0.3,
  1130            "order": "start-first"
  1131          },
  1132          "rollback_config": {
  1133            "parallelism": 3,
  1134            "delay": "10s",
  1135            "failure_action": "continue",
  1136            "monitor": "1m0s",
  1137            "max_failure_ratio": 0.3,
  1138            "order": "start-first"
  1139          },
  1140          "resources": {
  1141            "limits": {
  1142              "cpus": "0.001",
  1143              "memory": "52428800",
  1144              "pids": 100
  1145            },
  1146            "reservations": {
  1147              "cpus": "0.0001",
  1148              "memory": "20971520",
  1149              "generic_resources": [
  1150                {
  1151                  "discrete_resource_spec": {
  1152                    "kind": "gpu",
  1153                    "value": 2
  1154                  }
  1155                },
  1156                {
  1157                  "discrete_resource_spec": {
  1158                    "kind": "ssd",
  1159                    "value": 1
  1160                  }
  1161                }
  1162              ]
  1163            }
  1164          },
  1165          "restart_policy": {
  1166            "condition": "on-failure",
  1167            "delay": "5s",
  1168            "max_attempts": 3,
  1169            "window": "2m0s"
  1170          },
  1171          "placement": {
  1172            "constraints": [
  1173              "node=foo"
  1174            ],
  1175            "preferences": [
  1176              {
  1177                "spread": "node.labels.az"
  1178              }
  1179            ],
  1180            "max_replicas_per_node": 5
  1181          },
  1182          "endpoint_mode": "dnsrr"
  1183        },
  1184        "devices": [
  1185          "/dev/ttyUSB0:/dev/ttyUSB0"
  1186        ],
  1187        "dns": [
  1188          "8.8.8.8",
  1189          "9.9.9.9"
  1190        ],
  1191        "dns_search": [
  1192          "dc1.example.com",
  1193          "dc2.example.com"
  1194        ],
  1195        "domainname": "foo.com",
  1196        "entrypoint": [
  1197          "/code/entrypoint.sh",
  1198          "-p",
  1199          "3000"
  1200        ],
  1201        "environment": {
  1202          "BAR": "bar_from_env_file_2",
  1203          "BAZ": "baz_from_service_def",
  1204          "FOO": "foo_from_env_file",
  1205          "QUX": "qux_from_environment"
  1206        },
  1207        "env_file": [
  1208          "./example1.env",
  1209          "./example2.env"
  1210        ],
  1211        "expose": [
  1212          "3000",
  1213          "8000"
  1214        ],
  1215        "external_links": [
  1216          "redis_1",
  1217          "project_db_1:mysql",
  1218          "project_db_1:postgresql"
  1219        ],
  1220        "extra_hosts": [
  1221          "somehost:162.242.195.82",
  1222          "otherhost:50.31.209.229",
  1223          "host.docker.internal:host-gateway"
  1224        ],
  1225        "hostname": "foo",
  1226        "healthcheck": {
  1227          "test": [
  1228            "CMD-SHELL",
  1229            "echo \"hello world\""
  1230          ],
  1231          "timeout": "1s",
  1232          "interval": "10s",
  1233          "retries": 5,
  1234          "start_period": "15s"
  1235        },
  1236        "image": "redis",
  1237        "ipc": "host",
  1238        "labels": {
  1239          "com.example.description": "Accounting webapp",
  1240          "com.example.empty-label": "",
  1241          "com.example.number": "42"
  1242        },
  1243        "links": [
  1244          "db",
  1245          "db:database",
  1246          "redis"
  1247        ],
  1248        "logging": {
  1249          "driver": "syslog",
  1250          "options": {
  1251            "syslog-address": "tcp://192.168.0.42:123"
  1252          }
  1253        },
  1254        "mac_address": "02:42:ac:11:65:43",
  1255        "network_mode": "container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b",
  1256        "networks": {
  1257          "other-network": {
  1258            "ipv4_address": "172.16.238.10",
  1259            "ipv6_address": "2001:3984:3989::10"
  1260          },
  1261          "other-other-network": null,
  1262          "some-network": {
  1263            "aliases": [
  1264              "alias1",
  1265              "alias3"
  1266            ]
  1267          }
  1268        },
  1269        "pid": "host",
  1270        "ports": [
  1271          {
  1272            "mode": "ingress",
  1273            "target": 3000,
  1274            "protocol": "tcp"
  1275          },
  1276          {
  1277            "mode": "ingress",
  1278            "target": 3001,
  1279            "protocol": "tcp"
  1280          },
  1281          {
  1282            "mode": "ingress",
  1283            "target": 3002,
  1284            "protocol": "tcp"
  1285          },
  1286          {
  1287            "mode": "ingress",
  1288            "target": 3003,
  1289            "protocol": "tcp"
  1290          },
  1291          {
  1292            "mode": "ingress",
  1293            "target": 3004,
  1294            "protocol": "tcp"
  1295          },
  1296          {
  1297            "mode": "ingress",
  1298            "target": 3005,
  1299            "protocol": "tcp"
  1300          },
  1301          {
  1302            "mode": "ingress",
  1303            "target": 8000,
  1304            "published": 8000,
  1305            "protocol": "tcp"
  1306          },
  1307          {
  1308            "mode": "ingress",
  1309            "target": 8080,
  1310            "published": 9090,
  1311            "protocol": "tcp"
  1312          },
  1313          {
  1314            "mode": "ingress",
  1315            "target": 8081,
  1316            "published": 9091,
  1317            "protocol": "tcp"
  1318          },
  1319          {
  1320            "mode": "ingress",
  1321            "target": 22,
  1322            "published": 49100,
  1323            "protocol": "tcp"
  1324          },
  1325          {
  1326            "mode": "ingress",
  1327            "target": 8001,
  1328            "published": 8001,
  1329            "protocol": "tcp"
  1330          },
  1331          {
  1332            "mode": "ingress",
  1333            "target": 5000,
  1334            "published": 5000,
  1335            "protocol": "tcp"
  1336          },
  1337          {
  1338            "mode": "ingress",
  1339            "target": 5001,
  1340            "published": 5001,
  1341            "protocol": "tcp"
  1342          },
  1343          {
  1344            "mode": "ingress",
  1345            "target": 5002,
  1346            "published": 5002,
  1347            "protocol": "tcp"
  1348          },
  1349          {
  1350            "mode": "ingress",
  1351            "target": 5003,
  1352            "published": 5003,
  1353            "protocol": "tcp"
  1354          },
  1355          {
  1356            "mode": "ingress",
  1357            "target": 5004,
  1358            "published": 5004,
  1359            "protocol": "tcp"
  1360          },
  1361          {
  1362            "mode": "ingress",
  1363            "target": 5005,
  1364            "published": 5005,
  1365            "protocol": "tcp"
  1366          },
  1367          {
  1368            "mode": "ingress",
  1369            "target": 5006,
  1370            "published": 5006,
  1371            "protocol": "tcp"
  1372          },
  1373          {
  1374            "mode": "ingress",
  1375            "target": 5007,
  1376            "published": 5007,
  1377            "protocol": "tcp"
  1378          },
  1379          {
  1380            "mode": "ingress",
  1381            "target": 5008,
  1382            "published": 5008,
  1383            "protocol": "tcp"
  1384          },
  1385          {
  1386            "mode": "ingress",
  1387            "target": 5009,
  1388            "published": 5009,
  1389            "protocol": "tcp"
  1390          },
  1391          {
  1392            "mode": "ingress",
  1393            "target": 5010,
  1394            "published": 5010,
  1395            "protocol": "tcp"
  1396          }
  1397        ],
  1398        "privileged": true,
  1399        "read_only": true,
  1400        "restart": "always",
  1401        "secrets": [
  1402          {
  1403            "source": "secret1"
  1404          },
  1405          {
  1406            "source": "secret2",
  1407            "target": "my_secret",
  1408            "uid": "103",
  1409            "gid": "103",
  1410            "mode": 288
  1411          }
  1412        ],
  1413        "security_opt": [
  1414          "label=level:s0:c100,c200",
  1415          "label=type:svirt_apache_t"
  1416        ],
  1417        "stdin_open": true,
  1418        "stop_grace_period": "20s",
  1419        "stop_signal": "SIGUSR1",
  1420        "sysctls": {
  1421          "net.core.somaxconn": "1024",
  1422          "net.ipv4.tcp_syncookies": "0"
  1423        },
  1424        "tmpfs": [
  1425          "/run",
  1426          "/tmp"
  1427        ],
  1428        "tty": true,
  1429        "ulimits": {
  1430          "nofile": {
  1431            "soft": 20000,
  1432            "hard": 40000
  1433          },
  1434          "nproc": 65535
  1435        },
  1436        "user": "someone",
  1437        "volumes": [
  1438          {
  1439            "type": "volume",
  1440            "target": "/var/lib/mysql"
  1441          },
  1442          {
  1443            "type": "bind",
  1444            "source": "/opt/data",
  1445            "target": "/var/lib/mysql"
  1446          },
  1447          {
  1448            "type": "bind",
  1449            "source": "/foo",
  1450            "target": "/code"
  1451          },
  1452          {
  1453            "type": "bind",
  1454            "source": "%s",
  1455            "target": "/var/www/html"
  1456          },
  1457          {
  1458            "type": "bind",
  1459            "source": "/bar/configs",
  1460            "target": "/etc/configs/",
  1461            "read_only": true
  1462          },
  1463          {
  1464            "type": "volume",
  1465            "source": "datavolume",
  1466            "target": "/var/lib/mysql"
  1467          },
  1468          {
  1469            "type": "bind",
  1470            "source": "%s",
  1471            "target": "/opt",
  1472            "consistency": "cached"
  1473          },
  1474          {
  1475            "type": "tmpfs",
  1476            "target": "/opt",
  1477            "tmpfs": {
  1478              "size": 10000
  1479            }
  1480          },
  1481          {
  1482            "type": "cluster",
  1483            "source": "group:mygroup",
  1484            "target": "/srv"
  1485          }
  1486        ],
  1487        "working_dir": "/code"
  1488      }
  1489    },
  1490    "version": "3.10",
  1491    "volumes": {
  1492      "another-volume": {
  1493        "name": "user_specified_name",
  1494        "driver": "vsphere",
  1495        "driver_opts": {
  1496          "baz": "1",
  1497          "foo": "bar"
  1498        },
  1499        "external": false
  1500      },
  1501      "cluster-volume": {
  1502        "driver": "my-csi-driver",
  1503        "external": false,
  1504        "x-cluster-spec": {
  1505          "group": "mygroup",
  1506          "access_mode": {
  1507            "scope": "single",
  1508            "sharing": "none",
  1509            "block_volume": {}
  1510          },
  1511          "accessibility_requirements": {
  1512            "requisite": [
  1513              {
  1514                "segments": {
  1515                  "region": "R1",
  1516                  "zone": "Z1"
  1517                }
  1518              },
  1519              {
  1520                "segments": {
  1521                  "region": "R1",
  1522                  "zone": "Z2"
  1523                }
  1524              }
  1525            ],
  1526            "preferred": [
  1527              {
  1528                "segments": {
  1529                  "region": "R1",
  1530                  "zone": "Z1"
  1531                }
  1532              }
  1533            ]
  1534          },
  1535          "capacity_range": {
  1536            "required_bytes": "1073741824",
  1537            "limit_bytes": "8589934592"
  1538          },
  1539          "secrets": [
  1540            {
  1541              "key": "mycsisecret",
  1542              "secret": "secret1"
  1543            },
  1544            {
  1545              "key": "mycsisecret2",
  1546              "secret": "secret4"
  1547            }
  1548          ],
  1549          "availability": "active"
  1550        }
  1551      },
  1552      "external-volume": {
  1553        "name": "external-volume",
  1554        "external": true
  1555      },
  1556      "external-volume3": {
  1557        "name": "this-is-volume3",
  1558        "external": true
  1559      },
  1560      "other-external-volume": {
  1561        "name": "my-cool-volume",
  1562        "external": true
  1563      },
  1564      "other-volume": {
  1565        "driver": "flocker",
  1566        "driver_opts": {
  1567          "baz": "1",
  1568          "foo": "bar"
  1569        },
  1570        "external": false,
  1571        "labels": {
  1572          "foo": "bar"
  1573        }
  1574      },
  1575      "some-volume": {
  1576        "external": false
  1577      }
  1578    },
  1579    "x-bar": "baz",
  1580    "x-foo": "bar",
  1581    "x-nested": {
  1582      "bar": "baz",
  1583      "foo": "bar"
  1584    }
  1585  }`,
  1586  		workingDir,
  1587  		workingDir,
  1588  		workingDir,
  1589  		workingDir,
  1590  		filepath.Join(workingDir, "static"),
  1591  		filepath.Join(workingDir, "opt"))
  1592  }