support multiple containers in a pod (#1394)

This commit is contained in:
tk42 2021-07-08 10:48:05 +09:00 committed by GitHub
parent c51d59566b
commit deb00f3407
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 801 additions and 87 deletions

View File

@ -54,6 +54,10 @@ var (
// WithKomposeAnnotation decides if we will add metadata about this convert to resource's annotation.
// default is true.
WithKomposeAnnotation bool
// MultipleContainerMode which enables creating multi containers in a single pod is a developping function.
// default is false
MultipleContainerMode bool
)
var convertCmd = &cobra.Command{
@ -95,6 +99,7 @@ var convertCmd = &cobra.Command{
IsDeploymentConfigFlag: cmd.Flags().Lookup("deployment-config").Changed,
YAMLIndent: ConvertYAMLIndent,
WithKomposeAnnotation: WithKomposeAnnotation,
MultipleContainerMode: MultipleContainerMode,
}
// Validate before doing anything else. Use "bundle" if passed in.
@ -124,6 +129,7 @@ func init() {
convertCmd.Flags().MarkHidden("daemon-set")
convertCmd.Flags().MarkHidden("replication-controller")
convertCmd.Flags().MarkHidden("deployment")
convertCmd.Flags().BoolVar(&MultipleContainerMode, "multiple-container-mode", false, "Create multiple containers grouped by 'kompose.service.group' label")
// OpenShift only
convertCmd.Flags().BoolVar(&ConvertDeploymentConfig, "deployment-config", true, "Generate an OpenShift deploymentconfig object")

View File

@ -158,6 +158,7 @@ The currently supported options are:
| Key | Value |
|----------------------|-------------------------------------|
| kompose.service.type | nodeport / clusterip / loadbalancer / headless |
| kompose.service.group | name to group the containers contained in a single pod |
| kompose.service.expose | true / hostnames (separated by comma) |
| kompose.service.nodeport.port | port value (string) |
| kompose.service.expose.tls-secret | secret name |
@ -195,6 +196,28 @@ services:
kompose.service.type: nodeport
```
- `kompose.service.group` defines the group of containers included in a single pod.
For example:
```yaml
version: "3"
services:
nginx:
image: nginx
depends_on:
- logs
labels:
- kompose.service.group=sidecar
logs:
image: busybox
command: ["tail -f /var/log/nginx/access.log"]
labels:
- kompose.service.group=sidecar
```
- `kompose.service.expose` defines if the service needs to be made accessible from outside the cluster or not. If the value is set to "true", the provider sets the endpoint automatically, and for any other value, the value is set as the hostname. If multiple ports are defined in a service, the first one is chosen to be the exposed.
- For the Kubernetes provider, an ingress resource is created and it is assumed that an ingress controller has already been configured. If the value is set to a comma sepatated list, multiple hostnames are supported.Hostname with path is also supported.
- For the OpenShift provider, a route is created.

10
go.mod
View File

@ -17,6 +17,7 @@ replace github.com/containerd/containerd => github.com/containerd/containerd v1.
replace golang.org/x/sys => golang.org/x/sys v0.0.0-20201029080932-201ba4db2418
require (
github.com/deckarep/golang-set v1.7.1
github.com/docker/cli v0.0.0-20190711175710-5b38d82aa076
github.com/docker/go-connections v0.4.0
github.com/docker/libcompose v0.4.0
@ -26,8 +27,11 @@ require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/imdario/mergo v0.3.10 // indirect
github.com/joho/godotenv v1.3.0
github.com/mattn/goveralls v0.0.9 // indirect
github.com/mitchellh/gox v1.0.1 // indirect
github.com/moby/sys/mount v0.1.1 // indirect
github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2 // indirect
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5 // indirect
github.com/novln/docker-parser v1.0.0
github.com/openshift/api v0.0.0-20200803131051-87466835fcc0
github.com/pkg/errors v0.9.1
@ -35,7 +39,11 @@ require (
github.com/spf13/cast v1.3.1
github.com/spf13/cobra v1.0.0
github.com/spf13/viper v1.7.1
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/yaml.v2 v2.3.0
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
gotest.tools/v3 v3.0.3 // indirect

36
go.sum
View File

@ -70,6 +70,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/cli v20.10.0-beta1.0.20201029214301-1d20b15adc38+incompatible h1:r99CiNpN5pxrSuSH36suYxrbLxFOhBvQ0sEH6624MHs=
@ -194,6 +196,8 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8=
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
@ -244,6 +248,8 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/goveralls v0.0.9 h1:XmIwwrO9a9pqSW6IpI89BSCShzQxx0j/oKnnvELQNME=
github.com/mattn/goveralls v0.0.9/go.mod h1:FRbM1PS8oVsOe9JtdzAAXM+DsvDMMHcM1C7drGJD8HY=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@ -251,6 +257,8 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI=
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@ -268,6 +276,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5 h1:8Q0qkMVC/MmWkpIdlvZgcv2o2jrlF6zqVOh7W5YHdMA=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
@ -385,6 +395,7 @@ github.com/xeipuuv/gojsonschema v1.2.1-0.20201027075954-b076d39a02e5 h1:ImnGIsrc
github.com/xeipuuv/gojsonschema v1.2.1-0.20201027075954-b076d39a02e5/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
@ -399,6 +410,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -414,12 +426,16 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -438,8 +454,9 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -447,12 +464,12 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM=
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@ -482,11 +499,16 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200602230032-c00d67ef29d0 h1:6txNFSnY+tteYoO+hf01EpdYcYZiurdC9MDIrcUzEu4=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200602230032-c00d67ef29d0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=

View File

@ -77,6 +77,8 @@ type ConvertOptions struct {
YAMLIndent int
WithKomposeAnnotation bool
MultipleContainerMode bool
}
// IsPodController indicate if the user want to use a controller
@ -84,8 +86,11 @@ func (opt *ConvertOptions) IsPodController() bool {
return opt.IsDeploymentFlag || opt.IsDaemonSetFlag || opt.IsReplicationControllerFlag || opt.Controller != ""
}
type ServiceConfigGroup []ServiceConfig
// ServiceConfig holds the basic struct of a container
type ServiceConfig struct {
Name string
ContainerName string
Image string `compose:"image"`
Environment []EnvVar `compose:"environment"`

View File

@ -32,6 +32,8 @@ import (
const (
// LabelServiceType defines the type of service to be created
LabelServiceType = "kompose.service.type"
// LabelServiceGroup defines the group of services in a single pod
LabelServiceGroup = "kompose.service.group"
// LabelNodePortPort defines the port value for NodePort service
LabelNodePortPort = "kompose.service.nodeport.port"
// LabelServiceExpose defines if the service needs to be made accessible from outside the cluster or not

View File

@ -178,6 +178,7 @@ func libComposeToKomposeMapping(composeObject *project.Project) (kobject.Kompose
// all relevant information as well as avoid the unsupported keys as well.
for name, composeServiceConfig := range composeObject.ServiceConfigs.All() {
serviceConfig := kobject.ServiceConfig{}
serviceConfig.Name = name
serviceConfig.Image = composeServiceConfig.Image
serviceConfig.Build = composeServiceConfig.Build.Context
newName := normalizeContainerNames(composeServiceConfig.ContainerName)

View File

@ -364,6 +364,7 @@ func dockerComposeToKomposeMapping(composeObject *types.Config) (kobject.Kompose
// No need to modify before importation
name := composeServiceConfig.Name
serviceConfig := kobject.ServiceConfig{}
serviceConfig.Name = name
serviceConfig.Image = composeServiceConfig.Image
serviceConfig.WorkingDir = composeServiceConfig.WorkingDir
serviceConfig.Annotations = map[string]string(composeServiceConfig.Labels)

View File

@ -33,6 +33,7 @@ import (
"github.com/joho/godotenv"
"github.com/kubernetes/kompose/pkg/kobject"
"github.com/kubernetes/kompose/pkg/loader/compose"
"github.com/kubernetes/kompose/pkg/transformer"
deployapi "github.com/openshift/api/apps/v1"
"github.com/pkg/errors"
@ -440,11 +441,44 @@ func (k *Kubernetes) CreateHeadlessService(name string, service kobject.ServiceC
return svc
}
func (k *Kubernetes) UpdateKubernetesObjectsMultipleContainers(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions, objects *[]runtime.Object, podSpec PodSpec) error {
// Configure annotations
annotations := transformer.ConfigAnnotations(service)
// fillTemplate fills the pod template with the value calculated from config
fillTemplate := func(template *api.PodTemplateSpec) error {
template.ObjectMeta.Labels = transformer.ConfigLabelsWithNetwork(name, service.Network)
template.Spec = podSpec.Get()
return nil
}
// fillObjectMeta fills the metadata with the value calculated from config
fillObjectMeta := func(meta *metav1.ObjectMeta) {
meta.Annotations = annotations
}
// update supported controller
for _, obj := range *objects {
err := k.UpdateController(obj, fillTemplate, fillObjectMeta)
if err != nil {
return errors.Wrap(err, "k.UpdateController failed")
}
if len(service.Volumes) > 0 {
switch objType := obj.(type) {
case *appsv1.Deployment:
objType.Spec.Strategy.Type = appsv1.RecreateDeploymentStrategyType
case *deployapi.DeploymentConfig:
objType.Spec.Strategy.Type = deployapi.DeploymentStrategyTypeRecreate
}
}
}
return nil
}
// UpdateKubernetesObjects loads configurations to k8s objects
func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions, objects *[]runtime.Object) error {
// Configure the environment variables.
envs, err := k.ConfigEnvs(name, service, opt)
envs, err := ConfigEnvs(name, service, opt)
if err != nil {
return errors.Wrap(err, "Unable to load env variables")
}
@ -479,10 +513,10 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
}
// Configure the container ports.
ports := k.ConfigPorts(name, service)
ports := ConfigPorts(name, service)
// Configure capabilities
capabilities := k.ConfigCapabilities(service)
capabilities := ConfigCapabilities(service)
// Configure annotations
annotations := transformer.ConfigAnnotations(service)
@ -662,6 +696,20 @@ func (k *Kubernetes) UpdateKubernetesObjects(name string, service kobject.Servic
return nil
}
// KomposeObjectToServiceConfigGroupMapping returns the service config group by name
func KomposeObjectToServiceConfigGroupMapping(komposeObject kobject.KomposeObject) map[string]kobject.ServiceConfigGroup {
serviceConfigGroup := make(map[string]kobject.ServiceConfigGroup)
for name, service := range komposeObject.ServiceConfigs {
if groupID, ok := service.Labels[compose.LabelServiceGroup]; ok {
service.Name = name
serviceConfigGroup[groupID] = append(serviceConfigGroup[groupID], service)
} else {
serviceConfigGroup[name] = append(serviceConfigGroup[name], service)
}
}
return serviceConfigGroup
}
// TranslatePodResource config pod resources
func TranslatePodResource(service *kobject.ServiceConfig, template *api.PodTemplateSpec) {
// Configure the resource limits

View File

@ -536,7 +536,7 @@ func (k *Kubernetes) CreatePVC(name string, mode string, size string, selectorVa
}
// ConfigPorts configures the container ports.
func (k *Kubernetes) ConfigPorts(name string, service kobject.ServiceConfig) []api.ContainerPort {
func ConfigPorts(name string, service kobject.ServiceConfig) []api.ContainerPort {
ports := []api.ContainerPort{}
exist := map[string]bool{}
for _, port := range service.Port {
@ -641,7 +641,7 @@ func (k *Kubernetes) ConfigServicePorts(name string, service kobject.ServiceConf
}
//ConfigCapabilities configure POSIX capabilities that can be added or removed to a container
func (k *Kubernetes) ConfigCapabilities(service kobject.ServiceConfig) *api.Capabilities {
func ConfigCapabilities(service kobject.ServiceConfig) *api.Capabilities {
capsAdd := []api.Capability{}
capsDrop := []api.Capability{}
for _, capAdd := range service.CapAdd {
@ -947,7 +947,7 @@ func (k *Kubernetes) ConfigPVCVolumeSource(name string, readonly bool) *api.Volu
}
// ConfigEnvs configures the environment variables.
func (k *Kubernetes) ConfigEnvs(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions) ([]api.EnvVar, error) {
func ConfigEnvs(name string, service kobject.ServiceConfig, opt kobject.ConvertOptions) ([]api.EnvVar, error) {
envs := transformer.EnvSort{}
keysFromEnvFile := make(map[string]bool)
@ -1132,98 +1132,249 @@ func (k *Kubernetes) Transform(komposeObject kobject.KomposeObject, opt kobject.
}
}
sortedKeys := SortedKeys(komposeObject)
for _, name := range sortedKeys {
service := komposeObject.ServiceConfigs[name]
var objects []runtime.Object
if opt.MultipleContainerMode {
komposeObjectToServiceConfigGroupMapping := KomposeObjectToServiceConfigGroupMapping(komposeObject)
for name, group := range komposeObjectToServiceConfigGroupMapping {
service := komposeObject.ServiceConfigs[name]
var objects []runtime.Object
service.WithKomposeAnnotation = opt.WithKomposeAnnotation
service.WithKomposeAnnotation = opt.WithKomposeAnnotation
// Must build the images before conversion (got to add service.Image in case 'image' key isn't provided
// Check that --build is set to true
// Check to see if there is an InputFile (required!) before we build the container
// Check that there's actually a Build key
// Lastly, we must have an Image name to continue
if opt.Build == "local" && opt.InputFiles != nil && service.Build != "" {
// If there's no "image" key, use the name of the container that's built
if service.Image == "" {
service.Image = name
}
// Must build the images before conversion (got to add service.Image in case 'image' key isn't provided
// Check that --build is set to true
// Check to see if there is an InputFile (required!) before we build the container
// Check that there's actually a Build key
// Lastly, we must have an Image name to continue
if opt.Build == "local" && opt.InputFiles != nil && service.Build != "" {
// If there's no "image" key, use the name of the container that's built
if service.Image == "" {
service.Image = name
}
if service.Image == "" {
return nil, fmt.Errorf("image key required within build parameters in order to build and push service '%s'", name)
}
if service.Image == "" {
return nil, fmt.Errorf("image key required within build parameters in order to build and push service '%s'", name)
}
log.Infof("Build key detected. Attempting to build image '%s'", service.Image)
log.Infof("Build key detected. Attempting to build image '%s'", service.Image)
// Build the image!
err := transformer.BuildDockerImage(service, name)
if err != nil {
return nil, errors.Wrapf(err, "Unable to build Docker image for service %v", name)
}
// Push the built image to the repo!
if opt.PushImage {
log.Infof("Push image enabled. Attempting to push image '%s'", service.Image)
err = transformer.PushDockerImage(service, name)
// Build the image!
err := transformer.BuildDockerImage(service, name)
if err != nil {
return nil, errors.Wrapf(err, "Unable to push Docker image for service %v", name)
return nil, errors.Wrapf(err, "Unable to build Docker image for service %v", name)
}
// Push the built image to the repo!
if opt.PushImage {
log.Infof("Push image enabled. Attempting to push image '%s'", service.Image)
err = transformer.PushDockerImage(service, name)
if err != nil {
return nil, errors.Wrapf(err, "Unable to push Docker image for service %v", name)
}
}
}
}
// Generate pod only and nothing more
if (service.Restart == "no" || service.Restart == "on-failure") && !opt.IsPodController() {
log.Infof("Create kubernetes pod instead of pod controller due to restart policy: %s", service.Restart)
pod := k.InitPod(name, service)
objects = append(objects, pod)
} else {
objects = k.CreateKubernetesObjects(name, service, opt)
}
podSpec := PodSpec{}
if k.PortsExist(service) {
if service.ServiceType == "LoadBalancer" {
svcs := k.CreateLBService(name, service, objects)
for _, svc := range svcs {
// added a container
for _, service := range group {
podSpec.Append(AddContainer(service, opt))
// Generate pod only and nothing more
if (service.Restart == "no" || service.Restart == "on-failure") && !opt.IsPodController() {
log.Infof("Create kubernetes pod instead of pod controller due to restart policy: %s", service.Restart)
pod := k.InitPod(name, service)
objects = append(objects, pod)
} else {
objects = k.CreateKubernetesObjects(name, service, opt)
}
if k.PortsExist(service) {
if service.ServiceType == "LoadBalancer" {
svcs := k.CreateLBService(name, service, objects)
for _, svc := range svcs {
objects = append(objects, svc)
}
if len(svcs) > 1 {
log.Warningf("Create multiple service to avoid using mixed protocol in the same service when it's loadbalander type")
}
} else {
svc := k.CreateService(name, service, objects)
objects = append(objects, svc)
if service.ExposeService != "" {
objects = append(objects, k.initIngress(name, service, svc.Spec.Ports[0].Port))
}
}
} else {
if service.ServiceType == "Headless" {
svc := k.CreateHeadlessService(name, service, objects)
objects = append(objects, svc)
} else {
log.Warnf("Service %q won't be created because 'ports' is not specified", name)
}
}
// Configure the container volumes.
volumesMount, volumes, pvc, cms, err := k.ConfigVolumes(name, service)
if err != nil {
return nil, errors.Wrap(err, "k.ConfigVolumes failed")
}
podSpec.Append(
SetVolumeMounts(volumesMount),
SetVolumes(volumes),
)
// Configure Tmpfs
if len(service.TmpFs) > 0 {
TmpVolumesMount, TmpVolumes := k.ConfigTmpfs(name, service)
volumes = append(volumes, TmpVolumes...)
volumesMount = append(volumesMount, TmpVolumesMount...)
}
if pvc != nil {
// Looping on the slice pvc instead of `*objects = append(*objects, pvc...)`
// because the type of objects and pvc is different, but when doing append
// one element at a time it gets converted to runtime.Object for objects slice
for _, p := range pvc {
objects = append(objects, p)
}
}
if cms != nil {
for _, c := range cms {
objects = append(objects, c)
}
}
podSpec.Append(
SetPorts(name, service),
ImagePullPolicy(name, service),
RestartPolicy(name, service),
SecurityContext(name, service),
LivenessProbe(service),
ReadinessProbe(service),
HostName(service),
DomainName(service),
ResourcesLimits(service),
ResourcesRequests(service),
TerminationGracePeriodSeconds(name, service),
)
err = k.UpdateKubernetesObjectsMultipleContainers(name, service, opt, &objects, podSpec)
if err != nil {
return nil, errors.Wrap(err, "Error transforming Kubernetes objects")
}
}
if len(service.Network) > 0 {
for _, net := range service.Network {
log.Infof("Network %s is detected at Source, shall be converted to equivalent NetworkPolicy at Destination", net)
np, err := k.CreateNetworkPolicy(name, net)
if err != nil {
return nil, errors.Wrapf(err, "Unable to create Network Policy for network %v for service %v", net, name)
}
objects = append(objects, np)
}
}
allobjects = append(allobjects, objects...)
}
} else {
sortedKeys := SortedKeys(komposeObject)
for _, name := range sortedKeys {
service := komposeObject.ServiceConfigs[name]
var objects []runtime.Object
service.WithKomposeAnnotation = opt.WithKomposeAnnotation
// Must build the images before conversion (got to add service.Image in case 'image' key isn't provided
// Check that --build is set to true
// Check to see if there is an InputFile (required!) before we build the container
// Check that there's actually a Build key
// Lastly, we must have an Image name to continue
if opt.Build == "local" && opt.InputFiles != nil && service.Build != "" {
// If there's no "image" key, use the name of the container that's built
if service.Image == "" {
service.Image = name
}
if service.Image == "" {
return nil, fmt.Errorf("image key required within build parameters in order to build and push service '%s'", name)
}
log.Infof("Build key detected. Attempting to build image '%s'", service.Image)
// Build the image!
err := transformer.BuildDockerImage(service, name)
if err != nil {
return nil, errors.Wrapf(err, "Unable to build Docker image for service %v", name)
}
// Push the built image to the repo!
if opt.PushImage {
log.Infof("Push image enabled. Attempting to push image '%s'", service.Image)
err = transformer.PushDockerImage(service, name)
if err != nil {
return nil, errors.Wrapf(err, "Unable to push Docker image for service %v", name)
}
}
}
// Generate pod only and nothing more
if (service.Restart == "no" || service.Restart == "on-failure") && !opt.IsPodController() {
log.Infof("Create kubernetes pod instead of pod controller due to restart policy: %s", service.Restart)
pod := k.InitPod(name, service)
objects = append(objects, pod)
} else {
objects = k.CreateKubernetesObjects(name, service, opt)
}
if k.PortsExist(service) {
if service.ServiceType == "LoadBalancer" {
svcs := k.CreateLBService(name, service, objects)
for _, svc := range svcs {
objects = append(objects, svc)
}
if len(svcs) > 1 {
log.Warningf("Create multiple service to avoid using mixed protocol in the same service when it's loadbalander type")
}
} else {
svc := k.CreateService(name, service, objects)
objects = append(objects, svc)
}
if len(svcs) > 1 {
log.Warningf("Create multiple service to avoid using mixed protocol in the same service when it's loadbalander type")
if service.ExposeService != "" {
objects = append(objects, k.initIngress(name, service, svc.Spec.Ports[0].Port))
}
}
} else {
svc := k.CreateService(name, service, objects)
objects = append(objects, svc)
if service.ExposeService != "" {
objects = append(objects, k.initIngress(name, service, svc.Spec.Ports[0].Port))
if service.ServiceType == "Headless" {
svc := k.CreateHeadlessService(name, service, objects)
objects = append(objects, svc)
} else {
log.Warnf("Service %q won't be created because 'ports' is not specified", name)
}
}
} else {
if service.ServiceType == "Headless" {
svc := k.CreateHeadlessService(name, service, objects)
objects = append(objects, svc)
} else {
log.Warnf("Service %q won't be created because 'ports' is not specified", name)
err := k.UpdateKubernetesObjects(name, service, opt, &objects)
if err != nil {
return nil, errors.Wrap(err, "Error transforming Kubernetes objects")
}
}
err := k.UpdateKubernetesObjects(name, service, opt, &objects)
if err != nil {
return nil, errors.Wrap(err, "Error transforming Kubernetes objects")
}
if len(service.Network) > 0 {
for _, net := range service.Network {
log.Infof("Network %s is detected at Source, shall be converted to equivalent NetworkPolicy at Destination", net)
np, err := k.CreateNetworkPolicy(name, net)
if len(service.Network) > 0 {
for _, net := range service.Network {
log.Infof("Network %s is detected at Source, shall be converted to equivalent NetworkPolicy at Destination", net)
np, err := k.CreateNetworkPolicy(name, net)
if err != nil {
return nil, errors.Wrapf(err, "Unable to create Network Policy for network %v for service %v", net, name)
if err != nil {
return nil, errors.Wrapf(err, "Unable to create Network Policy for network %v for service %v", net, name)
}
objects = append(objects, np)
}
objects = append(objects, np)
}
}
allobjects = append(allobjects, objects...)
allobjects = append(allobjects, objects...)
}
}
// sort all object so Services are first

View File

@ -24,6 +24,7 @@ import (
"testing"
"github.com/kubernetes/kompose/pkg/kobject"
"github.com/kubernetes/kompose/pkg/loader/compose"
"github.com/kubernetes/kompose/pkg/transformer"
deployapi "github.com/openshift/api/apps/v1"
"github.com/pkg/errors"
@ -535,12 +536,106 @@ func TestConfigCapabilities(t *testing.T) {
"ConfigCapsNoAddDrop": {kobject.ServiceConfig{CapAdd: nil, CapDrop: nil}, api.Capabilities{Add: []api.Capability{}, Drop: []api.Capability{}}},
}
k := Kubernetes{}
for name, test := range testCases {
t.Log("Test case:", name)
result := k.ConfigCapabilities(test.service)
result := ConfigCapabilities(test.service)
if !reflect.DeepEqual(result.Add, test.result.Add) || !reflect.DeepEqual(result.Drop, test.result.Drop) {
t.Errorf("Not expected result for ConfigCapabilities")
}
}
}
func TestMultipleContainersInPod(t *testing.T) {
groupName := "pod_group"
containerName := ""
createConfig := func(name string, containerName *string) kobject.ServiceConfig {
config := newServiceConfig()
config.Labels = map[string]string{compose.LabelServiceGroup: groupName}
config.Name = name
if containerName != nil {
config.ContainerName = *containerName
}
config.Volumes = []kobject.Volumes{
{
VolumeName: "mountVolume",
MountPath: "/data",
},
}
return config
}
testCases := map[string]struct {
komposeObject kobject.KomposeObject
opt kobject.ConvertOptions
expectedNumObjs int
expectedNames []string
}{
"Converted multiple containers": {
kobject.KomposeObject{
ServiceConfigs: map[string]kobject.ServiceConfig{
"app1": createConfig("app1", &containerName),
"app2": createConfig("app2", &containerName),
},
}, kobject.ConvertOptions{MultipleContainerMode: true}, 2, []string{"app1", "app2"}},
"Converted multiple containers to Deployments (D)": {
kobject.KomposeObject{
ServiceConfigs: map[string]kobject.ServiceConfig{
"app1": createConfig("app1", &containerName),
"app2": createConfig("app2", &containerName),
},
}, kobject.ConvertOptions{MultipleContainerMode: true, CreateD: true}, 3, []string{"app1", "app2"}},
"Converted multiple containers (ContainerName are nil) to Deployments (D)": {
kobject.KomposeObject{
ServiceConfigs: map[string]kobject.ServiceConfig{
"app1": createConfig("app1", nil),
"app2": createConfig("app2", nil),
},
}, kobject.ConvertOptions{MultipleContainerMode: true, CreateD: true}, 3, []string{"name", "name"}},
// TODO: add more tests
}
for name, test := range testCases {
t.Log("Test case:", name)
k := Kubernetes{}
// Run Transform
objs, err := k.Transform(test.komposeObject, test.opt)
if err != nil {
t.Error(errors.Wrap(err, "k.Transform failed"))
}
if len(objs) != test.expectedNumObjs {
t.Errorf("Expected %d objects returned, got %d", test.expectedNumObjs, len(objs))
}
// Check results
for _, obj := range objs {
if svc, ok := obj.(*api.Service); ok {
if svc.Name != groupName {
t.Errorf("Expected %v returned, got %v", groupName, svc.Name)
}
}
if deployment, ok := obj.(*appsv1.Deployment); ok {
if deployment.Name != groupName {
t.Errorf("Expected %v returned, got %v", groupName, deployment.Name)
}
if len(deployment.Spec.Template.Spec.Containers) != 2 {
t.Errorf("Expected %d returned, got %d", 2, len(deployment.Spec.Template.Spec.Containers))
}
nameSet := make(map[string]api.Container)
for _, container := range deployment.Spec.Template.Spec.Containers {
nameSet[container.Name] = container
}
if container, ok := nameSet[test.expectedNames[0]]; !ok {
t.Errorf("Expected %v returned, got %v", test.expectedNames[0], container.Name)
} else if len(container.VolumeMounts) != 1 {
t.Errorf("Expected %v returned, got %v", 1, len(container.VolumeMounts))
}
if container, ok := nameSet[test.expectedNames[1]]; !ok {
t.Errorf("Expected %v returned, got %v", test.expectedNames[1], container.Name)
} else if len(container.VolumeMounts) != 1 {
t.Errorf("Expected %v returned, got %v", 1, len(container.VolumeMounts))
}
}
}
}
}

View File

@ -0,0 +1,352 @@
package kubernetes
import (
"reflect"
"strconv"
mapset "github.com/deckarep/golang-set"
"github.com/kubernetes/kompose/pkg/kobject"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
api "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/util/intstr"
)
type PodSpec struct {
api.PodSpec
}
type PodSpecOption func(*PodSpec)
func AddContainer(service kobject.ServiceConfig, opt kobject.ConvertOptions) PodSpecOption {
return func(podSpec *PodSpec) {
name := service.Name
image := service.Image
if image == "" {
image = name
}
// do not override in openshift case?
if len(service.ContainerName) > 0 {
name = FormatContainerName(service.ContainerName)
}
envs, err := ConfigEnvs(name, service, opt)
if err != nil {
panic("Unable to load env variables")
}
podSpec.Containers = append(podSpec.Containers, api.Container{
Name: name,
Image: image,
Env: envs,
Command: service.Command,
Args: service.Args,
WorkingDir: service.WorkingDir,
Stdin: service.Stdin,
TTY: service.Tty,
})
podSpec.NodeSelector = service.Placement
}
}
func ImagePullSecrets(pullSecret string) PodSpecOption {
return func(podSpec *PodSpec) {
podSpec.ImagePullSecrets = append(podSpec.ImagePullSecrets,
api.LocalObjectReference{
Name: pullSecret,
},
)
}
}
func TerminationGracePeriodSeconds(name string, service kobject.ServiceConfig) PodSpecOption {
return func(podSpec *PodSpec) {
var err error
if service.StopGracePeriod != "" {
podSpec.TerminationGracePeriodSeconds, err = DurationStrToSecondsInt(service.StopGracePeriod)
if err != nil {
log.Warningf("Failed to parse duration \"%v\" for service \"%v\"", service.StopGracePeriod, name)
}
}
}
}
// Configure the resource limits
func ResourcesLimits(service kobject.ServiceConfig) PodSpecOption {
return func(podSpec *PodSpec) {
if service.MemLimit != 0 || service.CPULimit != 0 {
resourceLimit := api.ResourceList{}
if service.MemLimit != 0 {
resourceLimit[api.ResourceMemory] = *resource.NewQuantity(int64(service.MemLimit), "RandomStringForFormat")
}
if service.CPULimit != 0 {
resourceLimit[api.ResourceCPU] = *resource.NewMilliQuantity(service.CPULimit, resource.DecimalSI)
}
for i := range podSpec.Containers {
podSpec.Containers[i].Resources.Limits = resourceLimit
}
}
}
}
// Configure the resource requests
func ResourcesRequests(service kobject.ServiceConfig) PodSpecOption {
return func(podSpec *PodSpec) {
if service.MemReservation != 0 || service.CPUReservation != 0 {
resourceRequests := api.ResourceList{}
if service.MemReservation != 0 {
resourceRequests[api.ResourceMemory] = *resource.NewQuantity(int64(service.MemReservation), "RandomStringForFormat")
}
if service.CPUReservation != 0 {
resourceRequests[api.ResourceCPU] = *resource.NewMilliQuantity(service.CPUReservation, resource.DecimalSI)
}
for i := range podSpec.Containers {
podSpec.Containers[i].Resources.Requests = resourceRequests
}
}
}
}
// Configure SecurityContext
func SecurityContext(name string, service kobject.ServiceConfig) PodSpecOption {
return func(podSpec *PodSpec) {
// Configure resource reservations
podSecurityContext := &api.PodSecurityContext{}
//set pid namespace mode
if service.Pid != "" {
if service.Pid == "host" {
// podSecurityContext.HostPID = true
} else {
log.Warningf("Ignoring PID key for service \"%v\". Invalid value \"%v\".", name, service.Pid)
}
}
//set supplementalGroups
if service.GroupAdd != nil {
podSecurityContext.SupplementalGroups = service.GroupAdd
}
// Setup security context
securityContext := &api.SecurityContext{}
if service.Privileged {
securityContext.Privileged = &service.Privileged
}
if service.User != "" {
uid, err := strconv.ParseInt(service.User, 10, 64)
if err != nil {
log.Warn("Ignoring user directive. User to be specified as a UID (numeric).")
} else {
securityContext.RunAsUser = &uid
}
}
// Configure capabilities
capabilities := ConfigCapabilities(service)
//set capabilities if it is not empty
if len(capabilities.Add) > 0 || len(capabilities.Drop) > 0 {
securityContext.Capabilities = capabilities
}
// update template only if securityContext is not empty
if *securityContext != (api.SecurityContext{}) {
podSpec.Containers[0].SecurityContext = securityContext
}
if !reflect.DeepEqual(*podSecurityContext, api.PodSecurityContext{}) {
podSpec.SecurityContext = podSecurityContext
}
}
}
func SetVolumeNames(volumes []api.Volume) mapset.Set {
set := mapset.NewSet()
for _, volume := range volumes {
set.Add(volume.Name)
}
return set
}
func SetVolumes(volumes []api.Volume) PodSpecOption {
return func(podSpec *PodSpec) {
volumesSet := SetVolumeNames(volumes)
containerVolumesSet := SetVolumeNames(podSpec.Volumes)
for diffVolumeName := range volumesSet.Difference(containerVolumesSet).Iter() {
for _, volume := range volumes {
if volume.Name == diffVolumeName {
podSpec.Volumes = append(podSpec.Volumes, volume)
break
}
}
}
}
}
func SetVolumeMountPaths(volumesMount []api.VolumeMount) mapset.Set {
set := mapset.NewSet()
for _, volumeMount := range volumesMount {
set.Add(volumeMount.MountPath)
}
return set
}
func SetVolumeMounts(volumesMount []api.VolumeMount) PodSpecOption {
return func(podSpec *PodSpec) {
volumesMountSet := SetVolumeMountPaths(volumesMount)
for i := range podSpec.Containers {
containerVolumeMountsSet := SetVolumeMountPaths(podSpec.Containers[i].VolumeMounts)
for diffVolumeMountPath := range volumesMountSet.Difference(containerVolumeMountsSet).Iter() {
for _, volumeMount := range volumesMount {
if volumeMount.MountPath == diffVolumeMountPath {
podSpec.Containers[i].VolumeMounts = append(podSpec.Containers[i].VolumeMounts, volumeMount)
break
}
}
}
}
}
}
// Configure ports
func SetPorts(name string, service kobject.ServiceConfig) PodSpecOption {
return func(podSpec *PodSpec) {
// Configure the container ports.
ports := ConfigPorts(name, service)
for i := range podSpec.Containers {
podSpec.Containers[i].Ports = ports
}
}
}
// Configure the image pull policy
func ImagePullPolicy(name string, service kobject.ServiceConfig) PodSpecOption {
return func(podSpec *PodSpec) {
if policy, err := GetImagePullPolicy(name, service.ImagePullPolicy); err != nil {
panic(err)
} else {
for i := range podSpec.Containers {
podSpec.Containers[i].ImagePullPolicy = policy
}
}
}
}
// Configure the container restart policy.
func RestartPolicy(name string, service kobject.ServiceConfig) PodSpecOption {
return func(podSpec *PodSpec) {
if restart, err := GetRestartPolicy(name, service.Restart); err != nil {
panic(err)
} else {
podSpec.RestartPolicy = restart
}
}
}
func HostName(service kobject.ServiceConfig) PodSpecOption {
return func(podSpec *PodSpec) {
// Configure hostname/domain_name settings
if service.HostName != "" {
podSpec.Hostname = service.HostName
}
}
}
func DomainName(service kobject.ServiceConfig) PodSpecOption {
return func(podSpec *PodSpec) {
if service.DomainName != "" {
podSpec.Subdomain = service.DomainName
}
}
}
func LivenessProbe(service kobject.ServiceConfig) PodSpecOption {
return func(podSpec *PodSpec) {
// Configure the HealthCheck
// We check to see if it's blank
if !reflect.DeepEqual(service.HealthChecks.Liveness, kobject.HealthCheck{}) {
probe := api.Probe{}
if len(service.HealthChecks.Liveness.Test) > 0 {
probe.Handler = api.Handler{
Exec: &api.ExecAction{
Command: service.HealthChecks.Liveness.Test,
},
}
} else if !reflect.ValueOf(service.HealthChecks.Liveness.HTTPPath).IsZero() &&
!reflect.ValueOf(service.HealthChecks.Liveness.HTTPPort).IsZero() {
probe.Handler = api.Handler{
HTTPGet: &api.HTTPGetAction{
Path: service.HealthChecks.Liveness.HTTPPath,
Port: intstr.FromInt(int(service.HealthChecks.Liveness.HTTPPort)),
},
}
} else {
panic(errors.New("Health check must contain a command"))
}
probe.TimeoutSeconds = service.HealthChecks.Liveness.Timeout
probe.PeriodSeconds = service.HealthChecks.Liveness.Interval
probe.FailureThreshold = service.HealthChecks.Liveness.Retries
// See issue: https://github.com/docker/cli/issues/116
// StartPeriod has been added to docker/cli however, it is not yet added
// to compose. Once the feature has been implemented, this will automatically work
probe.InitialDelaySeconds = service.HealthChecks.Liveness.StartPeriod
for i := range podSpec.Containers {
podSpec.Containers[i].LivenessProbe = &probe
}
}
}
}
func ReadinessProbe(service kobject.ServiceConfig) PodSpecOption {
return func(podSpec *PodSpec) {
if !reflect.DeepEqual(service.HealthChecks.Readiness, kobject.HealthCheck{}) {
probeHealthCheckReadiness := api.Probe{}
if len(service.HealthChecks.Readiness.Test) > 0 {
probeHealthCheckReadiness.Handler = api.Handler{
Exec: &api.ExecAction{
Command: service.HealthChecks.Readiness.Test,
},
}
} else {
panic(errors.New("Health check must contain a command"))
}
probeHealthCheckReadiness.TimeoutSeconds = service.HealthChecks.Readiness.Timeout
probeHealthCheckReadiness.PeriodSeconds = service.HealthChecks.Readiness.Interval
probeHealthCheckReadiness.FailureThreshold = service.HealthChecks.Readiness.Retries
// See issue: https://github.com/docker/cli/issues/116
// StartPeriod has been added to docker/cli however, it is not yet added
// to compose. Once the feature has been implemented, this will automatically work
probeHealthCheckReadiness.InitialDelaySeconds = service.HealthChecks.Readiness.StartPeriod
for i := range podSpec.Containers {
podSpec.Containers[i].ReadinessProbe = &probeHealthCheckReadiness
}
}
}
}
func (podSpec *PodSpec) Append(ops ...PodSpecOption) *PodSpec {
for _, option := range ops {
option(podSpec)
}
return podSpec
}
func (podSpec *PodSpec) Get() api.PodSpec {
return podSpec.PodSpec
}