diff --git a/go.mod b/go.mod index e99155fe..5c11b1c4 100644 --- a/go.mod +++ b/go.mod @@ -7,18 +7,23 @@ require ( github.com/fatih/color v1.7.0 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/gobwas/glob v0.2.3 + github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 // indirect github.com/google/go-cmp v0.4.0 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 github.com/mattn/go-colorable v0.1.2 // indirect github.com/mattn/go-isatty v0.0.12 // indirect - github.com/openshift/api v0.0.0-20200930075302-db52bc4ef99f + github.com/openshift/api v3.9.0+incompatible github.com/pkg/errors v0.9.1 github.com/spf13/afero v1.2.2 github.com/stretchr/testify v1.6.1 github.com/xeipuuv/gojsonschema v1.2.0 - k8s.io/api v0.19.0 - k8s.io/apimachinery v0.19.0 + golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect + golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4 // indirect + google.golang.org/protobuf v1.24.0 // indirect + k8s.io/api v0.18.6 + k8s.io/apimachinery v0.18.6 k8s.io/klog v1.0.0 + sigs.k8s.io/controller-runtime v0.6.3 sigs.k8s.io/yaml v1.2.0 ) diff --git a/go.sum b/go.sum index 751a995c..43399c09 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,7 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= @@ -61,6 +62,7 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -76,9 +78,9 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0 github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54= github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= @@ -133,6 +135,7 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -141,7 +144,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -166,14 +168,13 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk= github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= -github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -188,11 +189,13 @@ github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -210,8 +213,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= @@ -234,6 +235,7 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -263,9 +265,8 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/openshift/api v0.0.0-20200930075302-db52bc4ef99f h1:/msM59v15x4DaAZeJnQwkVsCGTEa1mx+nSSMehZVAHs= -github.com/openshift/api v0.0.0-20200930075302-db52bc4ef99f/go.mod h1:Si/I9UGeRR3qzg01YWPmtlr0GeGk2fnuggXJRmjAZ6U= -github.com/openshift/build-machinery-go v0.0.0-20200819073603-48aa266c95f7/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE= +github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs= +github.com/openshift/api v3.9.0+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= @@ -279,14 +280,18 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.11 h1:DhHlBtkHWPYi8O2y31JkK0TF+DGM+51OopZjH/Ia5qI= github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -330,7 +335,6 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/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.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= @@ -338,8 +342,11 @@ go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -348,17 +355,15 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/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/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -376,14 +381,13 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/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-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 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 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -424,6 +428,7 @@ golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -438,16 +443,15 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0= gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -501,35 +505,26 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.18.6 h1:osqrAXbOQjkKIWDTjrqxWQ3w0GkKb1KA1XkUGHHYpeE= k8s.io/api v0.18.6/go.mod h1:eeyxr+cwCjMdLAmr2W3RyDI0VvTawSg/3RFFBEnmZGI= -k8s.io/api v0.19.0 h1:XyrFIJqTYZJ2DU7FBE/bSPz7b1HvbVBuBf07oeo6eTc= -k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= k8s.io/apiextensions-apiserver v0.18.6 h1:vDlk7cyFsDyfwn2rNAO2DbmUbvXy5yT5GE3rrqOzaMo= k8s.io/apiextensions-apiserver v0.18.6/go.mod h1:lv89S7fUysXjLZO7ke783xOwVTm6lKizADfvUM/SS/M= k8s.io/apimachinery v0.18.6 h1:RtFHnfGNfd1N0LeSrKCUznz5xtUP1elRGvHJbL3Ntag= k8s.io/apimachinery v0.18.6/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= -k8s.io/apimachinery v0.19.0 h1:gjKnAda/HZp5k4xQYjL0K/Yb66IvNqjthCb03QlKpaQ= -k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= k8s.io/apiserver v0.18.6/go.mod h1:Zt2XvTHuaZjBz6EFYzpp+X4hTmgWGy8AthNVnTdm3Wg= +k8s.io/client-go v0.18.6 h1:I+oWqJbibLSGsZj8Xs8F0aWVXJVIoUHWaaJV3kUN/Zw= k8s.io/client-go v0.18.6/go.mod h1:/fwtGLjYMS1MaM5oi+eXhKwG+1UHidUEXRh6cNsdO0Q= k8s.io/code-generator v0.18.6/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= -k8s.io/code-generator v0.19.0/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= k8s.io/component-base v0.18.6/go.mod h1:knSVsibPR5K6EW2XOjEHik6sdU5nCvKMrzMt2D4In14= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120 h1:RPscN6KhmG54S33L+lr3GS+oD1jmchIU0ll519K6FA4= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/klog/v2 v2.0.0 h1:Foj74zO6RbjjP4hBEKjnYtjjAhGg4jNynUdYF6fJrok= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0 h1:XRvcwJozkgZ1UQJmfMGpvRthQHOvihEhYtDfAaxMz/A= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY= k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6 h1:+WnxoVtG8TMiudHBSEtrVL1egv36TkkJm+bA8AxicmQ= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200603063816-c1c6865ac451 h1:v8ud2Up6QK1lNOKFgiIVrZdMg7MpmSnvtrOieolJKoE= k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= @@ -539,8 +534,6 @@ sigs.k8s.io/controller-runtime v0.6.3/go.mod h1:WlZNXcM0++oyaQt4B7C2lEE5JYRs8vJU sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E= sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1 h1:YXTMot5Qz/X1iBRJhAt+vI+HVttY0WkSqqhKxQ0xVbA= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/pkg/flatten/annotate.go b/pkg/flatten/annotate.go new file mode 100644 index 00000000..6fa55ab3 --- /dev/null +++ b/pkg/flatten/annotate.go @@ -0,0 +1,39 @@ +package flatten + +import ( + dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "github.com/devfile/api/v2/pkg/attributes" +) + +const ( + ImportSourceAttribute = "library.devfile.io/imported-by" +) + +// AddSourceAttributesForTemplate adds an attribute 'library.devfile.io/imported-by=' to all elements of +// a plugin that support attributes. +func AddSourceAttributesForTemplate(sourceID string, template *dw.DevWorkspaceTemplateSpec) { + for idx, component := range template.Components { + if component.Attributes == nil { + template.Components[idx].Attributes = attributes.Attributes{} + } + template.Components[idx].Attributes.PutString(ImportSourceAttribute, sourceID) + } + for idx, command := range template.Commands { + if command.Attributes == nil { + template.Commands[idx].Attributes = attributes.Attributes{} + } + template.Commands[idx].Attributes.PutString(ImportSourceAttribute, sourceID) + } + for idx, project := range template.Projects { + if project.Attributes == nil { + template.Projects[idx].Attributes = attributes.Attributes{} + } + template.Projects[idx].Attributes.PutString(ImportSourceAttribute, sourceID) + } + for idx, project := range template.StarterProjects { + if project.Attributes == nil { + template.StarterProjects[idx].Attributes = attributes.Attributes{} + } + template.StarterProjects[idx].Attributes.PutString(ImportSourceAttribute, sourceID) + } +} diff --git a/pkg/flatten/common.go b/pkg/flatten/common.go new file mode 100644 index 00000000..7daddb13 --- /dev/null +++ b/pkg/flatten/common.go @@ -0,0 +1,27 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package flatten + +import devfile "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + +func DevWorkspaceIsFlattened(devworkspace *devfile.DevWorkspaceTemplateSpec) bool { + if devworkspace.Parent != nil { + return false + } + for _, component := range devworkspace.Components { + if component.Plugin != nil { + return false + } + } + return true +} diff --git a/pkg/flatten/flatten.go b/pkg/flatten/flatten.go new file mode 100644 index 00000000..19a92957 --- /dev/null +++ b/pkg/flatten/flatten.go @@ -0,0 +1,268 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package flatten + +import ( + "context" + "fmt" + "net/url" + "path" + + devfile "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "github.com/devfile/api/v2/pkg/utils/overriding" + "github.com/devfile/library/pkg/flatten/network" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// ResolverTools contains required structs and data for resolving remote components of a devfile (plugins and parents) +type ResolverTools struct { + // DefaultNamespace is the default namespace to use for resolving Kubernetes ImportReferences that do not include one + DefaultNamespace string + // DefaultRegistryURL is the default registry URL to use when a component specifies an id but not registryURL + DefaultRegistryURL string + // Context is the context used for making Kubernetes or HTTP requests + Context context.Context + // K8sClient is the Kubernetes client instance used for interacting with a cluster + K8sClient client.Client + // HttpClient is the HTTP client used for making network requests when resolving plugins or parents. + HttpClient network.HTTPGetter +} + +// ResolveDevWorkspace takes a DevWorkspaceTemplateSpec and returns a "resolved" version of it -- i.e. one where all plugins and parents +// are inlined as components. +// TODO: +// - Implement flattening for DevWorkspace parents +func ResolveDevWorkspace(workspace *devfile.DevWorkspaceTemplateSpec, tooling ResolverTools) (*devfile.DevWorkspaceTemplateSpec, error) { + resolutionCtx := &resolutionContextTree{} + resolvedDW, err := recursiveResolve(workspace, tooling, resolutionCtx) + if err != nil { + return nil, err + } + return resolvedDW, nil +} + +// recursiveResolve recursively resolves plugins and parents until the result contains no parents or plugin components. +// This is a recursive function, where resolveCtx is used to build a tree of resolved components. This is used to avoid +// plugin or parent import cycles. +func recursiveResolve(workspace *devfile.DevWorkspaceTemplateSpec, tooling ResolverTools, resolveCtx *resolutionContextTree) (*devfile.DevWorkspaceTemplateSpec, error) { + if DevWorkspaceIsFlattened(workspace) { + return workspace.DeepCopy(), nil + } + resolvedParent := &devfile.DevWorkspaceTemplateSpecContent{} + if workspace.Parent != nil { + resolvedParentSpec, err := resolveParentComponent(workspace.Parent, tooling) + if err != nil { + return nil, err + } + if !DevWorkspaceIsFlattened(resolvedParentSpec) { + // TODO: implemenent this + return nil, fmt.Errorf("parents containing plugins or parents are not supported") + } + AddSourceAttributesForTemplate("parent", resolvedParentSpec) + resolvedParent = &resolvedParentSpec.DevWorkspaceTemplateSpecContent + } + + resolvedContent := &devfile.DevWorkspaceTemplateSpecContent{} + resolvedContent.Projects = workspace.Projects + resolvedContent.StarterProjects = workspace.StarterProjects + resolvedContent.Commands = workspace.Commands + resolvedContent.Events = workspace.Events + + var pluginSpecContents []*devfile.DevWorkspaceTemplateSpecContent + for _, component := range workspace.Components { + if component.Plugin == nil { + // No action necessary + resolvedContent.Components = append(resolvedContent.Components, component) + } else { + pluginComponent, err := resolvePluginComponent(component.Name, component.Plugin, tooling) + if err != nil { + return nil, err + } + newCtx := resolveCtx.addPlugin(component.Name, component.Plugin) + if err := newCtx.hasCycle(); err != nil { + return nil, err + } + + resolvedPlugin, err := recursiveResolve(pluginComponent, tooling, newCtx) + if err != nil { + return nil, err + } + + AddSourceAttributesForTemplate(component.Name, resolvedPlugin) + pluginSpecContents = append(pluginSpecContents, &resolvedPlugin.DevWorkspaceTemplateSpecContent) + } + } + + resolvedContent, err := overriding.MergeDevWorkspaceTemplateSpec(resolvedContent, resolvedParent, pluginSpecContents...) + if err != nil { + return nil, fmt.Errorf("failed to merge DevWorkspace parents/plugins: %w", err) + } + + return &devfile.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: *resolvedContent, + }, nil +} + +// resolveParentComponent resolves the parent DevWorkspaceTemplateSpec that a parent reference refers to. +func resolveParentComponent(parent *devfile.Parent, tooling ResolverTools) (resolvedParent *devfile.DevWorkspaceTemplateSpec, err error) { + switch { + case parent.Kubernetes != nil: + // Search in default namespace if namespace ref is unset + if parent.Kubernetes.Namespace == "" { + parent.Kubernetes.Namespace = tooling.DefaultNamespace + } + resolvedParent, err = resolveElementByKubernetesImport("parent", parent.Kubernetes, tooling) + case parent.Uri != "": + resolvedParent, err = resolveElementByURI("parent", parent.Uri, tooling) + case parent.Id != "": + resolvedParent, err = resolveElementById("parent", parent.Id, parent.RegistryUrl, tooling) + default: + err = fmt.Errorf("devfile parent does not define any resources") + } + if err != nil { + return nil, err + } + if parent.Components != nil || parent.Commands != nil || parent.Projects != nil || parent.StarterProjects != nil { + overrideSpec, err := overriding.OverrideDevWorkspaceTemplateSpec(&resolvedParent.DevWorkspaceTemplateSpecContent, parent.ParentOverrides) + + if err != nil { + return nil, err + } + resolvedParent.DevWorkspaceTemplateSpecContent = *overrideSpec + } + return resolvedParent, nil +} + +// resolvePluginComponent resolves the DevWorkspaceTemplateSpec that a plugin component refers to. The name parameter is +// used to construct meaningful error messages (e.g. issue resolving plugin 'name') +func resolvePluginComponent( + name string, + plugin *devfile.PluginComponent, + tooling ResolverTools) (resolvedPlugin *devfile.DevWorkspaceTemplateSpec, err error) { + switch { + case plugin.Kubernetes != nil: + // Search in default namespace if namespace ref is unset + if plugin.Kubernetes.Namespace == "" { + plugin.Kubernetes.Namespace = tooling.DefaultNamespace + } + resolvedPlugin, err = resolveElementByKubernetesImport(name, plugin.Kubernetes, tooling) + case plugin.Uri != "": + resolvedPlugin, err = resolveElementByURI(name, plugin.Uri, tooling) + case plugin.Id != "": + resolvedPlugin, err = resolveElementById(name, plugin.Id, plugin.RegistryUrl, tooling) + default: + err = fmt.Errorf("plugin %s does not define any resources", name) + } + if err != nil { + return nil, err + } + + if plugin.Components != nil || plugin.Commands != nil { + overrideSpec, err := overriding.OverrideDevWorkspaceTemplateSpec(&resolvedPlugin.DevWorkspaceTemplateSpecContent, devfile.PluginOverrides{ + Components: plugin.Components, + Commands: plugin.Commands, + }) + + if err != nil { + return nil, err + } + resolvedPlugin.DevWorkspaceTemplateSpecContent = *overrideSpec + } + return resolvedPlugin, nil +} + +// resolveElementByKubernetesImport resolves a plugin specified by a Kubernetes reference. +// The name parameter is used to construct meaningful error messages (e.g. issue resolving plugin 'name') +func resolveElementByKubernetesImport( + name string, + kubeReference *devfile.KubernetesCustomResourceImportReference, + tools ResolverTools) (resolvedPlugin *devfile.DevWorkspaceTemplateSpec, err error) { + + if tools.K8sClient == nil { + return nil, fmt.Errorf("cannot resolve resources by kubernetes reference: no kubernetes client provided") + } + + namespace := kubeReference.Namespace + if namespace == "" { + if tools.DefaultNamespace == "" { + return nil, fmt.Errorf("'%s' specifies a kubernetes reference without namespace and a default is not provided", name) + } + namespace = tools.DefaultNamespace + } + + var dwTemplate devfile.DevWorkspaceTemplate + namespacedName := types.NamespacedName{ + Name: kubeReference.Name, + Namespace: namespace, + } + err = tools.K8sClient.Get(tools.Context, namespacedName, &dwTemplate) + if err != nil { + if errors.IsNotFound(err) { + return nil, fmt.Errorf("plugin for component %s not found", name) + } + return nil, fmt.Errorf("failed to retrieve plugin referenced by kubernetes name and namespace '%s': %w", name, err) + } + return &dwTemplate.Spec, nil +} + +// resolveElementById resolves a component specified by ID and registry URL. The name parameter is used to +// construct meaningful error messages (e.g. issue resolving plugin 'name'). When registry URL is empty, +// the DefaultRegistryURL from tools is used. +func resolveElementById( + name string, + id string, + registryUrl string, + tools ResolverTools) (resolvedPlugin *devfile.DevWorkspaceTemplateSpec, err error) { + + if tools.HttpClient == nil { + return nil, fmt.Errorf("cannot resolve resources by id: no HTTP client provided") + } + + if registryUrl == "" { + if tools.DefaultRegistryURL == "" { + return nil, fmt.Errorf("'%s' specifies id but has no registryUrl and a default is not provided", name) + } + registryUrl = tools.DefaultRegistryURL + } + pluginURL, err := url.Parse(registryUrl) + if err != nil { + return nil, fmt.Errorf("failed to parse registry URL for component %s: %w", name, err) + } + pluginURL.Path = path.Join(pluginURL.Path, id) + + dwt, err := network.FetchDevWorkspaceTemplate(pluginURL.String(), tools.HttpClient) + if err != nil { + return nil, fmt.Errorf("failed to resolve component %s from registry %s: %w", name, registryUrl, err) + } + return dwt, nil +} + +// resolveElementByURI resolves a plugin defined by URI. The name parameter is used to construct meaningful +// error messages (e.g. issue resolving plugin 'name') +func resolveElementByURI( + name string, + uri string, + tools ResolverTools) (resolvedPlugin *devfile.DevWorkspaceTemplateSpec, err error) { + + if tools.HttpClient == nil { + return nil, fmt.Errorf("cannot resolve resources by URI: no HTTP client provided") + } + + dwt, err := network.FetchDevWorkspaceTemplate(uri, tools.HttpClient) + if err != nil { + return nil, fmt.Errorf("failed to resolve component %s by URI: %w", name, err) + } + return dwt, nil +} diff --git a/pkg/flatten/flatten_test.go b/pkg/flatten/flatten_test.go new file mode 100644 index 00000000..5fc348d3 --- /dev/null +++ b/pkg/flatten/flatten_test.go @@ -0,0 +1,256 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package flatten + +import ( + "context" + "testing" + + "github.com/devfile/library/pkg/flatten/internal/testutil" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" +) + +func TestResolveDevWorkspaceKubernetesReference(t *testing.T) { + tests := testutil.LoadAllTestsOrPanic(t, "testdata/k8s-ref") + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + // sanity check: input defines components + assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") + testClient := &testutil.FakeK8sClient{ + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testResolverTools := ResolverTools{ + Context: context.Background(), + K8sClient: testClient, + DefaultNamespace: "default-namespace", + } + outputWorkspace, err := ResolveDevWorkspace(&tt.Input.Workspace, testResolverTools) + if tt.Output.ErrRegexp != nil && assert.Error(t, err) { + assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match") + } else { + if !assert.NoError(t, err, "Should not return error") { + return + } + assert.Truef(t, cmp.Equal(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts), + "Workspace should match expected output:\n%s", + cmp.Diff(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts)) + } + }) + } +} + +func TestResolveDevWorkspacePluginRegistry(t *testing.T) { + tests := testutil.LoadAllTestsOrPanic(t, "testdata/plugin-id") + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + // sanity check: input defines components + assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") + testHttpGetter := &testutil.FakeHTTPGetter{ + DevfileResources: tt.Input.DevfileResources, + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testResolverTools := ResolverTools{ + Context: context.Background(), + HttpClient: testHttpGetter, + DefaultRegistryURL: "https://default-registry.com", + } + outputWorkspace, err := ResolveDevWorkspace(&tt.Input.Workspace, testResolverTools) + if tt.Output.ErrRegexp != nil && assert.Error(t, err) { + assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match") + } else { + if !assert.NoError(t, err, "Should not return error") { + return + } + assert.Truef(t, cmp.Equal(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts), + "Workspace should match expected output:\n%s", + cmp.Diff(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts)) + } + }) + } +} + +func TestResolveDevWorkspacePluginURI(t *testing.T) { + tests := testutil.LoadAllTestsOrPanic(t, "testdata/plugin-uri") + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + // sanity check: input defines components + assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") + testHttpGetter := &testutil.FakeHTTPGetter{ + DevfileResources: tt.Input.DevfileResources, + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testResolverTools := ResolverTools{ + Context: context.Background(), + HttpClient: testHttpGetter, + } + outputWorkspace, err := ResolveDevWorkspace(&tt.Input.Workspace, testResolverTools) + if tt.Output.ErrRegexp != nil && assert.Error(t, err) { + assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match") + } else { + if !assert.NoError(t, err, "Should not return error") { + return + } + assert.Truef(t, cmp.Equal(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts), + "Workspace should match expected output:\n%s", + cmp.Diff(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts)) + } + }) + } +} + +func TestResolveDevWorkspaceParents(t *testing.T) { + tests := testutil.LoadAllTestsOrPanic(t, "testdata/parent") + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + // sanity check: input defines components + assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") + testHttpGetter := &testutil.FakeHTTPGetter{ + DevfileResources: tt.Input.DevfileResources, + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testK8sClient := &testutil.FakeK8sClient{ + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testResolverTools := ResolverTools{ + Context: context.Background(), + K8sClient: testK8sClient, + HttpClient: testHttpGetter, + DefaultNamespace: "default-namespace", + DefaultRegistryURL: "default-registry-url", + } + outputWorkspace, err := ResolveDevWorkspace(&tt.Input.Workspace, testResolverTools) + if tt.Output.ErrRegexp != nil && assert.Error(t, err) { + assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match") + } else { + if !assert.NoError(t, err, "Should not return error") { + return + } + assert.Truef(t, cmp.Equal(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts), + "Workspace should match expected output:\n%s", + cmp.Diff(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts)) + } + }) + } +} + +func TestResolveDevWorkspaceGeneralAPI(t *testing.T) { + tests := []testutil.TestCase{ + testutil.LoadTestCaseOrPanic(t, "testdata/general/fail-nicely-when-no-http-client-provided_id.yaml"), + testutil.LoadTestCaseOrPanic(t, "testdata/general/fail-nicely-when-no-http-client-provided_uri.yaml"), + testutil.LoadTestCaseOrPanic(t, "testdata/general/fail-nicely-when-no-k8s-client-provided.yaml"), + } + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + // sanity check: input defines components + assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") + testResolverTools := ResolverTools{ + Context: context.Background(), + K8sClient: nil, + HttpClient: nil, + } + outputWorkspace, err := ResolveDevWorkspace(&tt.Input.Workspace, testResolverTools) + if tt.Output.ErrRegexp != nil && assert.Error(t, err) { + assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match") + } else { + if !assert.NoError(t, err, "Should not return error") { + return + } + assert.Truef(t, cmp.Equal(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts), + "Workspace should match expected output:\n%s", + cmp.Diff(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts)) + } + }) + } +} + +func TestResolveDevWorkspaceMissingDefaults(t *testing.T) { + tests := []testutil.TestCase{ + testutil.LoadTestCaseOrPanic(t, "testdata/general/fail-nicely-when-no-registry-url.yaml"), + testutil.LoadTestCaseOrPanic(t, "testdata/general/fail-nicely-when-no-namespace.yaml"), + } + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + // sanity check: input defines components + assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") + testHttpGetter := &testutil.FakeHTTPGetter{ + DevfileResources: tt.Input.DevfileResources, + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testK8sClient := &testutil.FakeK8sClient{ + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testResolverTools := ResolverTools{ + Context: context.Background(), + K8sClient: testK8sClient, + HttpClient: testHttpGetter, + } + outputWorkspace, err := ResolveDevWorkspace(&tt.Input.Workspace, testResolverTools) + if tt.Output.ErrRegexp != nil && assert.Error(t, err) { + assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match") + } else { + if !assert.NoError(t, err, "Should not return error") { + return + } + assert.Truef(t, cmp.Equal(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts), + "Workspace should match expected output:\n%s", + cmp.Diff(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts)) + } + }) + } +} + +func TestResolveDevWorkspaceAnnotations(t *testing.T) { + tests := testutil.LoadAllTestsOrPanic(t, "testdata/annotate") + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + // sanity check: input defines components + assert.True(t, len(tt.Input.Workspace.Components) > 0, "Test case defines workspace with no components") + testHttpGetter := &testutil.FakeHTTPGetter{ + DevfileResources: tt.Input.DevfileResources, + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testK8sClient := &testutil.FakeK8sClient{ + DevWorkspaceResources: tt.Input.DevWorkspaceResources, + Errors: tt.Input.Errors, + } + testResolverTools := ResolverTools{ + Context: context.Background(), + K8sClient: testK8sClient, + HttpClient: testHttpGetter, + DefaultNamespace: "default-namespace", + DefaultRegistryURL: "default-registry-url", + } + outputWorkspace, err := ResolveDevWorkspace(&tt.Input.Workspace, testResolverTools) + if tt.Output.ErrRegexp != nil && assert.Error(t, err) { + assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match") + } else { + if !assert.NoError(t, err, "Should not return error") { + return + } + assert.Truef(t, cmp.Equal(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts), + "Workspace should match expected output:\n%s", + cmp.Diff(tt.Output.Workspace, outputWorkspace, testutil.WorkspaceTemplateDiffOpts)) + } + }) + } +} diff --git a/pkg/flatten/helper.go b/pkg/flatten/helper.go new file mode 100644 index 00000000..d1301478 --- /dev/null +++ b/pkg/flatten/helper.go @@ -0,0 +1,70 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package flatten + +import ( + "fmt" + "reflect" + + devworkspace "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" +) + +// resolutionContextTree is a recursive structure representing information about the devfile that is +// lost when flattening (e.g. plugins, parents) +type resolutionContextTree struct { + componentName string + importReference devworkspace.ImportReference + plugins []*resolutionContextTree + parentNode *resolutionContextTree +} + +// addPlugin adds a plugin component to the resolution context. +func (t *resolutionContextTree) addPlugin(name string, plugin *devworkspace.PluginComponent) *resolutionContextTree { + newNode := &resolutionContextTree{ + componentName: name, + importReference: plugin.ImportReference, + parentNode: t, + } + t.plugins = append(t.plugins, newNode) + return newNode +} + +// hasCycle checks if the current resolutionContextTree has a cycle +func (t *resolutionContextTree) hasCycle() error { + var seenRefs []devworkspace.ImportReference + currNode := t + for currNode.parentNode != nil { + for _, seenRef := range seenRefs { + if reflect.DeepEqual(seenRef, currNode.importReference) { + return fmt.Errorf("DevWorkspace has an cycle in references: %s", formatImportCycle(t)) + } + } + seenRefs = append(seenRefs, currNode.importReference) + currNode = currNode.parentNode + } + return nil +} + +// formatImportCycle is a utility method for formatting a cycle that has been detected. Output is formatted as +// plugin1 -> plugin2 -> plugin3 -> plugin1, where pluginX are component names. +func formatImportCycle(end *resolutionContextTree) string { + cycle := fmt.Sprintf("%s", end.componentName) + for end.parentNode != nil { + end = end.parentNode + if end.parentNode == nil { + end.componentName = "devfile" + } + cycle = fmt.Sprintf("%s -> %s", end.componentName, cycle) + } + return cycle +} diff --git a/pkg/flatten/internal/testutil/common.go b/pkg/flatten/internal/testutil/common.go new file mode 100644 index 00000000..fe0007a5 --- /dev/null +++ b/pkg/flatten/internal/testutil/common.go @@ -0,0 +1,103 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package testutil + +import ( + "io/ioutil" + "path/filepath" + "strings" + "testing" + + dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "sigs.k8s.io/yaml" +) + +// WorkspaceTemplateDiffOpts are used to compare test output against the expected result. +var WorkspaceTemplateDiffOpts = cmp.Options{ + cmpopts.SortSlices(func(a, b dw.Component) bool { + return strings.Compare(a.Key(), b.Key()) > 0 + }), + cmpopts.SortSlices(func(a, b string) bool { + return strings.Compare(a, b) > 0 + }), + // TODO: Devfile overriding results in empty []string instead of nil + cmpopts.IgnoreFields(dw.WorkspaceEvents{}, "PostStart", "PreStop", "PostStop"), +} + +// TestCase describes a single test case for the library. +type TestCase struct { + // Name is a descriptive name of what is being tested + Name string `json:"name"` + // Input describes the test inputs + Input TestInput `json:"input"` + Output TestOutput `json:"output"` +} + +// TestInput defines the inputs required for a test case. +type TestInput struct { + Workspace dw.DevWorkspaceTemplateSpec `json:"workspace,omitempty"` + // DevWorkspaceResources is a map of string keys to devworkspace templates + DevWorkspaceResources map[string]dw.DevWorkspaceTemplate `json:"devworkspaceResources,omitempty"` + // DevfileResources is a map of string keys to devfile resources + DevfileResources map[string]dw.Devfile `json:"devfileResources,omitempty"` + // Errors is a map of plugin name to the error that should be returned when attempting to retrieve it. + Errors map[string]TestPluginError `json:"errors,omitempty"` +} + +// TestPluginError describes an expected error. +type TestPluginError struct { + // IsNotFound marks this error as a kubernetes NotFoundError + IsNotFound bool `json:"isNotFound"` + // StatusCode defines the HTTP response code (if relevant) + StatusCode int `json:"statusCode"` + // Message is the error message returned + Message string `json:"message"` +} + +// TestOutput describes expected test outputs. If errRegexp is not empty, it is compared to the returned error as a regular +// expression. Otherwise, the output Workspace is compared with the output of the function call. +type TestOutput struct { + Workspace *dw.DevWorkspaceTemplateSpec `json:"workspace,omitempty"` + ErrRegexp *string `json:"errRegexp,omitempty"` +} + +// LoadTestCaseOrPanic loads the test file at testFilepath. +func LoadTestCaseOrPanic(t *testing.T, testFilepath string) TestCase { + bytes, err := ioutil.ReadFile(testFilepath) + if err != nil { + t.Fatal(err) + } + var test TestCase + if err := yaml.Unmarshal(bytes, &test); err != nil { + t.Fatal(err) + } + return test +} + +// LoadAllTestsOrPanic loads all yaml files in fromDir as test cases. +func LoadAllTestsOrPanic(t *testing.T, fromDir string) []TestCase { + files, err := ioutil.ReadDir(fromDir) + if err != nil { + t.Fatal(err) + } + var tests []TestCase + for _, file := range files { + if file.IsDir() { + continue + } + tests = append(tests, LoadTestCaseOrPanic(t, filepath.Join(fromDir, file.Name()))) + } + return tests +} diff --git a/pkg/flatten/internal/testutil/http.go b/pkg/flatten/internal/testutil/http.go new file mode 100644 index 00000000..8544134d --- /dev/null +++ b/pkg/flatten/internal/testutil/http.go @@ -0,0 +1,75 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package testutil + +import ( + "bytes" + "errors" + "fmt" + "io" + "net/http" + + "github.com/devfile/library/pkg/flatten/network" + + dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "sigs.k8s.io/yaml" +) + +type FakeHTTPGetter struct { + DevfileResources map[string]dw.Devfile + DevWorkspaceResources map[string]dw.DevWorkspaceTemplate + Errors map[string]TestPluginError +} + +var _ network.HTTPGetter = (*FakeHTTPGetter)(nil) + +type fakeRespBody struct { + io.Reader +} + +func (_ *fakeRespBody) Close() error { return nil } + +func (reg *FakeHTTPGetter) Get(location string) (*http.Response, error) { + if plugin, ok := reg.DevfileResources[location]; ok { + yamlBytes, err := yaml.Marshal(plugin) + if err != nil { + return nil, fmt.Errorf("error marshalling plugin in test: %w", err) + } + resp := &http.Response{ + StatusCode: http.StatusOK, + Body: &fakeRespBody{bytes.NewBuffer(yamlBytes)}, + } + return resp, nil + } + if plugin, ok := reg.DevWorkspaceResources[location]; ok { + yamlBytes, err := yaml.Marshal(plugin) + if err != nil { + return nil, fmt.Errorf("error marshalling plugin in test: %w", err) + } + resp := &http.Response{ + StatusCode: http.StatusOK, + Body: &fakeRespBody{bytes.NewBuffer(yamlBytes)}, + } + return resp, nil + } + if err, ok := reg.Errors[location]; ok { + if err.StatusCode != 0 { + return &http.Response{ + StatusCode: err.StatusCode, + Body: &fakeRespBody{bytes.NewBuffer([]byte{})}, + }, nil + } + return nil, errors.New(err.Message) + } + return nil, fmt.Errorf("test does not define entry for plugin at URL %s", location) +} diff --git a/pkg/flatten/internal/testutil/k8sClient.go b/pkg/flatten/internal/testutil/k8sClient.go new file mode 100644 index 00000000..1acab810 --- /dev/null +++ b/pkg/flatten/internal/testutil/k8sClient.go @@ -0,0 +1,50 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package testutil + +import ( + "context" + "errors" + "fmt" + + "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + k8sErrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type FakeK8sClient struct { + client.Client // To satisfy interface; override all used methods + DevWorkspaceResources map[string]v1alpha2.DevWorkspaceTemplate + Errors map[string]TestPluginError +} + +func (client *FakeK8sClient) Get(_ context.Context, namespacedName client.ObjectKey, obj runtime.Object) error { + template, ok := obj.(*v1alpha2.DevWorkspaceTemplate) + if !ok { + return fmt.Errorf("called Get() in fake client with non-DevWorkspaceTemplate") + } + if plugin, ok := client.DevWorkspaceResources[namespacedName.Name]; ok { + *template = plugin + return nil + } + if err, ok := client.Errors[namespacedName.Name]; ok { + if err.IsNotFound { + return k8sErrors.NewNotFound(schema.GroupResource{}, namespacedName.Name) + } else { + return errors.New(err.Message) + } + } + return fmt.Errorf("test does not define an entry for plugin %s", namespacedName.Name) +} diff --git a/pkg/flatten/network/devfile.go b/pkg/flatten/network/devfile.go new file mode 100644 index 00000000..c866fb56 --- /dev/null +++ b/pkg/flatten/network/devfile.go @@ -0,0 +1,33 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package network + +import ( + "fmt" + "regexp" + + dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" +) + +var SupportedSchemaVersionRegexp = regexp.MustCompile(`^2\..+`) + +func ConvertDevfileToDevWorkspaceTemplate(devfile *dw.Devfile) (*dw.DevWorkspaceTemplate, error) { + if !SupportedSchemaVersionRegexp.MatchString(devfile.SchemaVersion) { + return nil, fmt.Errorf("could not process devfile: unsupported schemaVersion '%s'", devfile.SchemaVersion) + } + dwt := &dw.DevWorkspaceTemplate{} + dwt.Spec = devfile.DevWorkspaceTemplateSpec + dwt.Name = devfile.Metadata.Name // TODO: Handle additional devfile metadata once those changes are pulled in to this repo + + return dwt, nil +} diff --git a/pkg/flatten/network/fetch.go b/pkg/flatten/network/fetch.go new file mode 100644 index 00000000..c0735879 --- /dev/null +++ b/pkg/flatten/network/fetch.go @@ -0,0 +1,74 @@ +// +// Copyright (c) 2019-2021 Red Hat, Inc. +// This program and the accompanying materials are made +// available under the terms of the Eclipse Public License 2.0 +// which is available at https://www.eclipse.org/legal/epl-2.0/ +// +// SPDX-License-Identifier: EPL-2.0 +// +// Contributors: +// Red Hat, Inc. - initial API and implementation +// + +package network + +import ( + "fmt" + "io/ioutil" + "net/http" + + dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "sigs.k8s.io/yaml" +) + +type HTTPGetter interface { + Get(location string) (*http.Response, error) +} + +// FetchDevWorkspaceTemplate retreives a devfile from URL location and extracts the DevWorkspaceTemplateSpec. +func FetchDevWorkspaceTemplate(location string, httpClient HTTPGetter) (*dw.DevWorkspaceTemplateSpec, error) { + resp, err := httpClient.Get(location) + if err != nil { + return nil, fmt.Errorf("failed to fetch file from %s: %w", location, err) + } + defer resp.Body.Close() // ignoring error because what would we even do? + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("could not fetch file from %s: got status %d", location, resp.StatusCode) + } + bytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("could not read data from %s: %w", location, err) + } + + devfile := &dw.Devfile{} + if err := yaml.Unmarshal(bytes, devfile); err != nil { + return nil, fmt.Errorf("could not unmarshal devfile from response: %w", err) + } + if devfile.SchemaVersion != "" { + dwt, err := ConvertDevfileToDevWorkspaceTemplate(devfile) + if err != nil { + return nil, fmt.Errorf("failed to convert devfile to DevWorkspaceTemplate: %s", err) + } + return &dwt.Spec, nil + } + + // Assume we didn't get a devfile, check if content is DevWorkspace + devworkspace := &dw.DevWorkspace{} + if err := yaml.Unmarshal(bytes, devworkspace); err != nil { + return nil, fmt.Errorf("could not unmarshal devworkspace from response: %w", err) + } + if devworkspace.Kind == "DevWorkspace" { + return &devworkspace.Spec.Template, nil + } + + // Check if content is DevWorkspaceTemplate + dwt := &dw.DevWorkspaceTemplate{} + if err := yaml.Unmarshal(bytes, dwt); err != nil { + return nil, fmt.Errorf("could not unmarshal devworkspacetemplate from response: %w", err) + } + if dwt.Kind == "DevWorkspaceTemplate" { + return &dwt.Spec, nil + } + + return nil, fmt.Errorf("could not find devfile or devworkspace object at '%s'", location) +} diff --git a/pkg/flatten/testdata/annotate/annotate-devfile-with-importing-source.yaml b/pkg/flatten/testdata/annotate/annotate-devfile-with-importing-source.yaml new file mode 100644 index 00000000..0a36bf82 --- /dev/null +++ b/pkg/flatten/testdata/annotate/annotate-devfile-with-importing-source.yaml @@ -0,0 +1,68 @@ +name: "Annotates resources merged into devfile with source attribute" + +input: + workspace: + parent: + kubernetes: + name: test-parent-k8s + components: + - name: test-plugin + plugin: + uri: https://test-plugin.io/test-plugin + devworkspaceResources: + test-parent-k8s: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: parent-devworkspacetemplate + spec: + projects: + - name: parent-project + starterProjects: + - name: parent-starter-project + components: + - name: parent-component + container: + image: test-img + commands: + - id: parent-command + devfileResources: + "https://test-plugin.io/test-plugin": + schemaVersion: 2.1.0 + metadata: + name: test-plugin + components: + - name: plugin-component + container: + image: plugin-img + commands: + - id: plugin-command +output: + workspace: + projects: + - name: parent-project + attributes: + library.devfile.io/imported-by: parent + starterProjects: + - name: parent-starter-project + attributes: + library.devfile.io/imported-by: parent + components: + - name: parent-component + attributes: + library.devfile.io/imported-by: parent + container: + image: test-img + - name: plugin-component + attributes: + library.devfile.io/imported-by: test-plugin + container: + image: plugin-img + commands: + - id: parent-command + attributes: + library.devfile.io/imported-by: parent + - id: plugin-command + attributes: + library.devfile.io/imported-by: test-plugin + diff --git a/pkg/flatten/testdata/general/fail-nicely-when-no-http-client-provided_id.yaml b/pkg/flatten/testdata/general/fail-nicely-when-no-http-client-provided_id.yaml new file mode 100644 index 00000000..f65b0428 --- /dev/null +++ b/pkg/flatten/testdata/general/fail-nicely-when-no-http-client-provided_id.yaml @@ -0,0 +1,11 @@ +name: "Fails nicely when no HTTP client provided and id is used" + +input: + workspace: + components: + - name: test-plugin + plugin: + id: test/plugin + +output: + errRegexp: "cannot resolve resources by id: no HTTP client provided" \ No newline at end of file diff --git a/pkg/flatten/testdata/general/fail-nicely-when-no-http-client-provided_uri.yaml b/pkg/flatten/testdata/general/fail-nicely-when-no-http-client-provided_uri.yaml new file mode 100644 index 00000000..ce243305 --- /dev/null +++ b/pkg/flatten/testdata/general/fail-nicely-when-no-http-client-provided_uri.yaml @@ -0,0 +1,11 @@ +name: "Fails nicely when no HTTP client provided and uri is used" + +input: + workspace: + components: + - name: test-plugin + plugin: + uri: https://test-repo.io/my-plugino + +output: + errRegexp: "cannot resolve resources by URI: no HTTP client provided" \ No newline at end of file diff --git a/pkg/flatten/testdata/general/fail-nicely-when-no-k8s-client-provided.yaml b/pkg/flatten/testdata/general/fail-nicely-when-no-k8s-client-provided.yaml new file mode 100644 index 00000000..d97adde8 --- /dev/null +++ b/pkg/flatten/testdata/general/fail-nicely-when-no-k8s-client-provided.yaml @@ -0,0 +1,13 @@ +name: "Fails nicely when no Kubernetes client provided" + +input: + workspace: + components: + - name: test-plugin + plugin: + kubernetes: + name: test-plugin-a + namespace: test-ns + +output: + errRegexp: "cannot resolve resources by kubernetes reference: no kubernetes client provided" \ No newline at end of file diff --git a/pkg/flatten/testdata/general/fail-nicely-when-no-namespace.yaml b/pkg/flatten/testdata/general/fail-nicely-when-no-namespace.yaml new file mode 100644 index 00000000..d1d2fbab --- /dev/null +++ b/pkg/flatten/testdata/general/fail-nicely-when-no-namespace.yaml @@ -0,0 +1,12 @@ +name: "Fails nicely when no Kubernetes namespace is provided and there's no default" + +input: + workspace: + components: + - name: test-plugin + plugin: + kubernetes: + name: test-plugin-a + +output: + errRegexp: "specifies a kubernetes reference without namespace and a default is not provided" \ No newline at end of file diff --git a/pkg/flatten/testdata/general/fail-nicely-when-no-registry-url.yaml b/pkg/flatten/testdata/general/fail-nicely-when-no-registry-url.yaml new file mode 100644 index 00000000..7fdf5204 --- /dev/null +++ b/pkg/flatten/testdata/general/fail-nicely-when-no-registry-url.yaml @@ -0,0 +1,11 @@ +name: "Fails nicely when no registry URL is provided and there's no default" + +input: + workspace: + components: + - name: test-plugin + plugin: + id: test/plugin + +output: + errRegexp: "specifies id but has no registryUrl and a default is not provided" \ No newline at end of file diff --git a/pkg/flatten/testdata/k8s-ref/already-flattened.yaml b/pkg/flatten/testdata/k8s-ref/already-flattened.yaml new file mode 100644 index 00000000..ea405d54 --- /dev/null +++ b/pkg/flatten/testdata/k8s-ref/already-flattened.yaml @@ -0,0 +1,69 @@ +name: "Already flattened workspace" + +input: + workspace: + components: + - name: dev + container: + image: quay.io/wto/web-terminal-tooling:latest + mountSources: false + memoryLimit: 256Mi + args: ["tail", "-f", "/dev/null"] + env: + - value: '\[\e[34m\]>\[\e[m\]\[\e[33m\]>\[\e[m\]' + name: PS1 + - name: web-terminal + container: + image: quay.io/eclipse/che-machine-exec:nightly + mountSources: false + command: ["/go/bin/che-machine-exec", + "--authenticated-user-id", "$(DEVWORKSPACE_CREATOR)", + "--idle-timeout", "$(DEVWORKSPACE_IDLE_TIMEOUT)", + "--pod-selector", "controller.devfile.io/devworkspace_id=$(DEVWORKSPACE_ID)", + "--use-bearer-token", + "--use-tls"] + endpoints: + - name: web-terminal + targetPort: 4444 + attributes: + protocol: http + type: ide + discoverable: "false" + secure: "true" + env: + - name: USE_BEARER_TOKEN + value: "true" + +output: + workspace: + components: + - name: dev + container: + image: quay.io/wto/web-terminal-tooling:latest + mountSources: false + memoryLimit: 256Mi + args: ["tail", "-f", "/dev/null"] + env: + - value: '\[\e[34m\]>\[\e[m\]\[\e[33m\]>\[\e[m\]' + name: PS1 + - name: web-terminal + container: + image: quay.io/eclipse/che-machine-exec:nightly + mountSources: false + command: ["/go/bin/che-machine-exec", + "--authenticated-user-id", "$(DEVWORKSPACE_CREATOR)", + "--idle-timeout", "$(DEVWORKSPACE_IDLE_TIMEOUT)", + "--pod-selector", "controller.devfile.io/devworkspace_id=$(DEVWORKSPACE_ID)", + "--use-bearer-token", + "--use-tls"] + endpoints: + - name: web-terminal + targetPort: 4444 + attributes: + protocol: http + type: ide + discoverable: "false" + secure: "true" + env: + - name: USE_BEARER_TOKEN + value: "true" diff --git a/pkg/flatten/testdata/k8s-ref/error_bad-plugin-merge.yaml b/pkg/flatten/testdata/k8s-ref/error_bad-plugin-merge.yaml new file mode 100644 index 00000000..f5fdb20c --- /dev/null +++ b/pkg/flatten/testdata/k8s-ref/error_bad-plugin-merge.yaml @@ -0,0 +1,23 @@ +name: "Attempting to override undefined plugin component" + +input: + workspace: + components: + - name: "bad-override" + plugin: + kubernetes: + name: override + components: + - name: non-existent + container: + memoryLimit: 512Mi + devworkspaceResources: + override: + spec: + components: + - name: my-component + container: + image: test-image + +output: + errRegexp: "Some Components do not override any existing element: non-existent.*" diff --git a/pkg/flatten/testdata/k8s-ref/error_conflicting-merge.yaml b/pkg/flatten/testdata/k8s-ref/error_conflicting-merge.yaml new file mode 100644 index 00000000..0c096bca --- /dev/null +++ b/pkg/flatten/testdata/k8s-ref/error_conflicting-merge.yaml @@ -0,0 +1,22 @@ +name: "Component conflicts with plugin component" + +input: + workspace: + components: + - name: "component-conflict" + plugin: + kubernetes: + name: test-plugin + - name: my-component + container: + image: test-image + devworkspaceResources: + test-plugin: + spec: + components: + - name: my-component + container: + image: test-image + +output: + errRegexp: "Some Components are already defined in plugin '.*': my-component.*" diff --git a/pkg/flatten/testdata/k8s-ref/error_error-when-retrieving-plugin.yaml b/pkg/flatten/testdata/k8s-ref/error_error-when-retrieving-plugin.yaml new file mode 100644 index 00000000..ef4551bf --- /dev/null +++ b/pkg/flatten/testdata/k8s-ref/error_error-when-retrieving-plugin.yaml @@ -0,0 +1,15 @@ +name: "Error retrieving plugin" + +input: + workspace: + components: + - name: "bad-plugin" + plugin: + kubernetes: + name: test-plugin + errors: + test-plugin: + message: "Internal k8s error" + +output: + errRegexp: ".*failed to retrieve.*bad-plugin.*Internal k8s error.*" diff --git a/pkg/flatten/testdata/k8s-ref/error_plugin-not-found.yaml b/pkg/flatten/testdata/k8s-ref/error_plugin-not-found.yaml new file mode 100644 index 00000000..77aef485 --- /dev/null +++ b/pkg/flatten/testdata/k8s-ref/error_plugin-not-found.yaml @@ -0,0 +1,16 @@ +name: "Referenced plugin cannot be found" + +input: + workspace: + components: + - name: "bad-plugin" + plugin: + kubernetes: + name: test-plugin + errors: + test-plugin: + isNotFound: true + message: "Plugin not found" + +output: + errRegexp: "plugin for component bad-plugin not found.*" diff --git a/pkg/flatten/testdata/k8s-ref/error_plugin-references-self.yml b/pkg/flatten/testdata/k8s-ref/error_plugin-references-self.yml new file mode 100644 index 00000000..61084f17 --- /dev/null +++ b/pkg/flatten/testdata/k8s-ref/error_plugin-references-self.yml @@ -0,0 +1,25 @@ +name: "Plugin references self" + +input: + workspace: + components: + - name: "plugin-a" + plugin: + kubernetes: + name: plugin-a + devworkspaceResources: + plugin-a: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: plugin-a + spec: + components: + - name: plugin-a + plugin: + kubernetes: + name: plugin-a + namespace: devworkspace-plugins + +output: + errRegexp: "DevWorkspace has an cycle in references.*" diff --git a/pkg/flatten/testdata/k8s-ref/error_plugins-have-cycle.yml b/pkg/flatten/testdata/k8s-ref/error_plugins-have-cycle.yml new file mode 100644 index 00000000..f6fcfff1 --- /dev/null +++ b/pkg/flatten/testdata/k8s-ref/error_plugins-have-cycle.yml @@ -0,0 +1,37 @@ +name: "Plugins have reference cycle" + +input: + workspace: + components: + - name: "plugin-a" + plugin: + kubernetes: + name: plugin-a + devworkspaceResources: + plugin-a: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: plugin-a + spec: + components: + - name: plugin-b + plugin: + kubernetes: + name: plugin-b + namespace: devworkspace-plugins + plugin-b: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: plugin-b + spec: + components: + - name: plugin-a + plugin: + kubernetes: + name: plugin-a + namespace: devworkspace-plugins + +output: + errRegexp: "DevWorkspace has an cycle in references.*" diff --git a/pkg/flatten/testdata/k8s-ref/nested-plugins-annotation.yaml b/pkg/flatten/testdata/k8s-ref/nested-plugins-annotation.yaml new file mode 100644 index 00000000..e058343c --- /dev/null +++ b/pkg/flatten/testdata/k8s-ref/nested-plugins-annotation.yaml @@ -0,0 +1,44 @@ +name: "DevWorkspace annotates nested plugins with the first plugin" + +input: + workspace: + components: + - name: root-plugin + plugin: + kubernetes: + name: test-plugin-a + namespace: test-ns + devworkspaceResources: + test-plugin-a: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: plugin-a + spec: + components: + - name: plugin-b + plugin: + kubernetes: + name: test-plugin-b + namespace: test-ns + test-plugin-b: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: plugin-b + spec: + components: + - name: plugin-b-container + container: + name: test-container + image: test-img + +output: + workspace: + components: + - name: plugin-b-container + attributes: + library.devfile.io/imported-by: "root-plugin" + container: + name: test-container + image: test-img diff --git a/pkg/flatten/testdata/k8s-ref/nodejs-workspace.yaml b/pkg/flatten/testdata/k8s-ref/nodejs-workspace.yaml new file mode 100644 index 00000000..18cd0740 --- /dev/null +++ b/pkg/flatten/testdata/k8s-ref/nodejs-workspace.yaml @@ -0,0 +1,521 @@ +name: "Theia and NodeJS plugin workspace" + +input: + workspace: + projects: + - name: web-nodejs-sample + git: + remotes: + origin: "https://github.com/che-samples/web-nodejs-sample.git" + components: + - name: che-theia + plugin: + kubernetes: + name: theia-next + namespace: devworkspace-plugins + - name: machine-exec + plugin: + kubernetes: + name: machine-exec + namespace: devworkspace-plugins + - name: typescript + plugin: + kubernetes: + name: vscode-typescript + namespace: devworkspace-plugins + components: + - name: sidecar-typescript + container: + memoryLimit: 512Mi + - name: nodejs + container: + image: quay.io/eclipse/che-nodejs10-ubi:nightly + memoryLimit: 512Mi + endpoints: + - name: nodejs + protocol: http + targetPort: 3000 + mountSources: true + commands: + - id: download-dependencies + exec: + component: nodejs + commandLine: npm install + workingDir: ${PROJECTS_ROOT}/project/app + - id: run-the-app + exec: + component: nodejs + commandLine: nodemon app.js + workingDir: ${PROJECTS_ROOT}/project/app + - id: run-the-app-with-debugging-enabled + exec: + component: nodejs + commandLine: nodemon --inspect app.js + workingDir: ${PROJECTS_ROOT}/project/app + - id: stop-the-app + exec: + component: nodejs + commandLine: >- + node_server_pids=$(pgrep -fx '.*nodemon (--inspect )?app.js' | tr "\\n" " ") && + echo "Stopping node server with PIDs: ${node_server_pids}" && + kill -15 ${node_server_pids} &>/dev/null && echo 'Done.' + - id: attach-remote-debugger + vscodeLaunch: + inlined: | + { + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "attach", + "name": "Attach to Remote", + "address": "localhost", + "port": 9229, + "localRoot": "${workspaceFolder}", + "remoteRoot": "${workspaceFolder}" + } + ] + } + devworkspaceResources: + theia-next: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: theia-next + labels: + "devworkspace.devfile.io/editor-name": "che-theia" + spec: + components: + - name: plugins + volume: {} + - name: remote-endpoint + volume: {} # TODO: Fix this once ephemeral volumes are supported + - name: vsx-installer # Mainly reads the container objects and searches for those + # with che-theia.eclipse.org/vscode-extensions attributes to get VSX urls + # Those found in the dedicated containers components are with a sidecar, + # Those found in the che-theia container are without a sidecar. + attributes: + "app.kubernetes.io/part-of": che-theia.eclipse.org + "app.kubernetes.io/component": bootstrapper + container: + args: + - /bin/sh + - '-c' + - | + KUBE_API_ENDPOINT="https://kubernetes.default.svc/apis/workspace.devfile.io/v1alpha2/namespaces/${CHE_WORKSPACE_NAMESPACE}/devworkspaces/${CHE_WORKSPACE_NAME}" &&\ + TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) &&\ + WORKSPACE=$(curl -fsS --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -H "Authorization: Bearer ${TOKEN}" $KUBE_API_ENDPOINT) &&\ + IFS=$'\n' &&\ + for container in $(echo $WORKSPACE | sed -e 's|[[,]\({"attributes":{"app.kubernetes.io\)|\n\1|g' | grep '"che-theia.eclipse.org/vscode-extensions":' | grep -e '^{"attributes".*'); do \ + dest=$(echo "$container" | sed 's|.*{"name":"THEIA_PLUGINS","value":"local-dir://\([^"][^"]*\)"}.*|\1|' - ) ;\ + urls=$(echo "$container" | sed 's|.*"che-theia.eclipse.org/vscode-extensions":\[\([^]][^]]*\)\]}.*|\1|' - ) ;\ + mkdir -p $dest ;\ + unset IFS &&\ + for url in $(echo $urls | sed 's/[",]/ /g' - ); do \ + echo; echo downloading $urls to $dest; curl -L $url > $dest/$(basename $url) ;\ + done \ + done \ + image: 'quay.io/samsahai/curl:latest' + volumeMounts: + - path: "/plugins" + name: plugins + - name: remote-runtime-injector + attributes: + "app.kubernetes.io/part-of": che-theia.eclipse.org + "app.kubernetes.io/component": bootstrapper + container: #### corresponds to `initContainer` definition in old meta.yaml. + image: "quay.io/eclipse/che-theia-endpoint-runtime-binary:7.20.0" + volumeMounts: + - path: "/remote-endpoint" + name: remote-endpoint + env: + - name: PLUGIN_REMOTE_ENDPOINT_EXECUTABLE + value: /remote-endpoint/plugin-remote-endpoint + - name: REMOTE_ENDPOINT_VOLUME_NAME + value: remote-endpoint + - name: theia-ide + attributes: + "app.kubernetes.io/name": che-theia.eclipse.org + "app.kubernetes.io/part-of": che.eclipse.org + "app.kubernetes.io/component": editor + + # Added by Che-theia at start when detecting, after cloning, that the extensions.json in the repo + # contains the vscode-pull-request-github vscode plugin. + "che-theia.eclipse.org/vscode-extensions": + - https://github.com/microsoft/vscode-pull-request-github/releases/download/v0.8.0/vscode-pull-request-github-0.8.0.vsix + container: + image: "quay.io/eclipse/che-theia:next" + env: + - name: THEIA_PLUGINS + value: local-dir:///plugins + - name: HOSTED_PLUGIN_HOSTNAME + value: 0.0.0.0 + - name: HOSTED_PLUGIN_PORT + value: "3130" + - name: THEIA_HOST + value: 0.0.0.0 + volumeMounts: + - path: "/plugins" + name: plugins + mountSources: true + memoryLimit: "512M" + endpoints: + - name: "theia" + exposure: public + targetPort: 3100 + secure: true + protocol: http + attributes: + type: ide + - name: "webviews" + exposure: public + targetPort: 3100 + protocol: http + secure: true + attributes: + type: webview + unique: "true" + - name: "theia-dev" + exposure: public + targetPort: 3130 + protocol: http + attributes: + type: ide-dev + - name: "theia-redir-1" + exposure: public + targetPort: 13131 + protocol: http + - name: "theia-redir-2" + exposure: public + targetPort: 13132 + protocol: http + - name: "theia-redir-3" + exposure: public + targetPort: 13133 + protocol: http + commands: + # Commands coming from plugin editor + - id: inject-theia-in-remote-sidecar + apply: + component: remote-runtime-injector + - id: copy-vsx + apply: + component: vsx-installer + events: + preStart: + - inject-theia-in-remote-sidecar + - copy-vsx + + machine-exec: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: machine-exec + labels: + "devworkspace.devfile.io/editor-compatibility": "che-theia" + spec: + components: + - name: che-machine-exec + attributes: + "app.kubernetes.io/name": che-terminal.eclipse.org + "app.kubernetes.io/part-of": che.eclipse.org + "app.kubernetes.io/component": terminal + container: + image: "quay.io/eclipse/che-machine-exec:7.20.0" + command: ['/go/bin/che-machine-exec'] + args: + - '--url' + - '0.0.0.0:4444' + - '--pod-selector' + - controller.devfile.io/devworkspace_id=$(DEVWORKSPACE_ID) + endpoints: + - name: "che-mach-exec" + exposure: public + targetPort: 4444 + protocol: ws + secure: true + attributes: + type: terminal + + vscode-typescript: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: vscode-typescript + labels: + "devworkspace.devfile.io/editor-compatibility": "che-theia" + spec: + components: + - name: sidecar-typescript + attributes: + "app.kubernetes.io/part-of": che-theia.eclipse.org + "app.kubernetes.io/component": vscode-plugin + + # Added by Che-theia at start when detecting, after cloning, that the extensions.json in the repo + # contains the typescript vscode plugin. + "che-theia.eclipse.org/vscode-extensions": + - https://download.jboss.org/jbosstools/vscode/3rdparty/ms-code.typescript/che-typescript-language-1.35.1.vsix + + container: + image: "quay.io/eclipse/che-sidecar-node:10-0cb5d78" + memoryLimit: '512Mi' + env: + - name: PLUGIN_REMOTE_ENDPOINT_EXECUTABLE + value: /remote-endpoint/plugin-remote-endpoint + - name: THEIA_PLUGINS + value: local-dir:///plugins/sidecars/vscode-typescript + volumeMounts: + - path: "/remote-endpoint" + name: remote-endpoint + - name: plugins + path: /plugins + + +output: + workspace: + projects: + - name: web-nodejs-sample + git: + remotes: + origin: "https://github.com/che-samples/web-nodejs-sample.git" + + components: + + - name: theia-ide + attributes: + "app.kubernetes.io/name": che-theia.eclipse.org + "app.kubernetes.io/part-of": che.eclipse.org + "app.kubernetes.io/component": editor + + # Added by Che-theia at start when detecting, after cloning, that the extensions.json in the repo + # contains the vscode-pull-request-github vscode plugin. + "che-theia.eclipse.org/vscode-extensions": + - https://github.com/microsoft/vscode-pull-request-github/releases/download/v0.8.0/vscode-pull-request-github-0.8.0.vsix + library.devfile.io/imported-by: "che-theia" + container: + image: "quay.io/eclipse/che-theia:next" + env: + - name: THEIA_PLUGINS + value: local-dir:///plugins + - name: HOSTED_PLUGIN_HOSTNAME + value: 0.0.0.0 + - name: HOSTED_PLUGIN_PORT + value: "3130" + - name: THEIA_HOST + value: 0.0.0.0 + volumeMounts: + - path: "/plugins" + name: plugins + mountSources: true + memoryLimit: "512M" + endpoints: + - name: "theia" + exposure: public + targetPort: 3100 + secure: true + protocol: http + attributes: + type: ide + - name: "webviews" + exposure: public + targetPort: 3100 + protocol: http + secure: true + attributes: + type: webview + unique: "true" + - name: "theia-dev" + exposure: public + targetPort: 3130 + protocol: http + attributes: + type: ide-dev + - name: "theia-redir-1" + exposure: public + targetPort: 13131 + protocol: http + - name: "theia-redir-2" + exposure: public + targetPort: 13132 + protocol: http + - name: "theia-redir-3" + exposure: public + targetPort: 13133 + protocol: http + + - name: plugins + attributes: + library.devfile.io/imported-by: "che-theia" + volume: {} + + - name: vsx-installer # Mainly reads the container objects and searches for those + # with che-theia.eclipse.org/vscode-extensions attributes to get VSX urls + # Those found in the dedicated containers components are with a sidecar, + # Those found in the che-theia container are without a sidecar. + attributes: + "app.kubernetes.io/part-of": che-theia.eclipse.org + "app.kubernetes.io/component": bootstrapper + library.devfile.io/imported-by: "che-theia" + container: + args: + - /bin/sh + - '-c' + - | + KUBE_API_ENDPOINT="https://kubernetes.default.svc/apis/workspace.devfile.io/v1alpha2/namespaces/${CHE_WORKSPACE_NAMESPACE}/devworkspaces/${CHE_WORKSPACE_NAME}" &&\ + TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) &&\ + WORKSPACE=$(curl -fsS --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -H "Authorization: Bearer ${TOKEN}" $KUBE_API_ENDPOINT) &&\ + IFS=$'\n' &&\ + for container in $(echo $WORKSPACE | sed -e 's|[[,]\({"attributes":{"app.kubernetes.io\)|\n\1|g' | grep '"che-theia.eclipse.org/vscode-extensions":' | grep -e '^{"attributes".*'); do \ + dest=$(echo "$container" | sed 's|.*{"name":"THEIA_PLUGINS","value":"local-dir://\([^"][^"]*\)"}.*|\1|' - ) ;\ + urls=$(echo "$container" | sed 's|.*"che-theia.eclipse.org/vscode-extensions":\[\([^]][^]]*\)\]}.*|\1|' - ) ;\ + mkdir -p $dest ;\ + unset IFS &&\ + for url in $(echo $urls | sed 's/[",]/ /g' - ); do \ + echo; echo downloading $urls to $dest; curl -L $url > $dest/$(basename $url) ;\ + done \ + done \ + image: 'quay.io/samsahai/curl:latest' + volumeMounts: + - path: "/plugins" + name: plugins + + - name: remote-endpoint + attributes: + library.devfile.io/imported-by: "che-theia" + volume: {} + # ephemeral: true #### We should add it in the Devfile 2.0 spec ! Not critical to implement at start though + + - name: remote-runtime-injector + attributes: + "app.kubernetes.io/part-of": che-theia.eclipse.org + "app.kubernetes.io/component": bootstrapper + library.devfile.io/imported-by: "che-theia" + container: #### corresponds to `initContainer` definition in old meta.yaml. + image: "quay.io/eclipse/che-theia-endpoint-runtime-binary:7.20.0" + volumeMounts: + - path: "/remote-endpoint" + name: remote-endpoint + env: + - name: PLUGIN_REMOTE_ENDPOINT_EXECUTABLE + value: /remote-endpoint/plugin-remote-endpoint + - name: REMOTE_ENDPOINT_VOLUME_NAME + value: remote-endpoint + + - name: che-machine-exec + attributes: + "app.kubernetes.io/name": che-terminal.eclipse.org + "app.kubernetes.io/part-of": che.eclipse.org + "app.kubernetes.io/component": terminal + library.devfile.io/imported-by: "machine-exec" + container: + image: "quay.io/eclipse/che-machine-exec:7.20.0" + command: ['/go/bin/che-machine-exec'] + args: + - '--url' + - '0.0.0.0:4444' + - '--pod-selector' + - controller.devfile.io/devworkspace_id=$(DEVWORKSPACE_ID) + endpoints: + - name: "che-mach-exec" + exposure: public + targetPort: 4444 + protocol: ws + secure: true + attributes: + type: terminal + - name: sidecar-typescript + attributes: + "app.kubernetes.io/part-of": che-theia.eclipse.org + "app.kubernetes.io/component": vscode-plugin + + # Added by Che-theia at start when detecting, after cloning, that the extensions.json in the repo + # contains the typescript vscode plugin. + "che-theia.eclipse.org/vscode-extensions": + - https://download.jboss.org/jbosstools/vscode/3rdparty/ms-code.typescript/che-typescript-language-1.35.1.vsix + + library.devfile.io/imported-by: "typescript" + + container: + image: "quay.io/eclipse/che-sidecar-node:10-0cb5d78" + memoryLimit: '512Mi' + env: + - name: PLUGIN_REMOTE_ENDPOINT_EXECUTABLE + value: /remote-endpoint/plugin-remote-endpoint + - name: THEIA_PLUGINS + value: local-dir:///plugins/sidecars/vscode-typescript + volumeMounts: + - path: "/remote-endpoint" + name: remote-endpoint + - name: plugins + path: /plugins + + # User runtime container + - name: nodejs + container: + image: quay.io/eclipse/che-nodejs10-ubi:nightly + memoryLimit: 512Mi + endpoints: + - name: nodejs + protocol: http + targetPort: 3000 + mountSources: true + + commands: + + # Commands coming from plugin editor + - id: inject-theia-in-remote-sidecar + attributes: + library.devfile.io/imported-by: "che-theia" + apply: + component: remote-runtime-injector + - id: copy-vsx + attributes: + library.devfile.io/imported-by: "che-theia" + apply: + component: vsx-installer + + # User commands + - id: download-dependencies + exec: + component: nodejs + commandLine: npm install + workingDir: ${PROJECTS_ROOT}/project/app + - id: run-the-app + exec: + component: nodejs + commandLine: nodemon app.js + workingDir: ${PROJECTS_ROOT}/project/app + - id: run-the-app-with-debugging-enabled + exec: + component: nodejs + commandLine: nodemon --inspect app.js + workingDir: ${PROJECTS_ROOT}/project/app + - id: stop-the-app + exec: + component: nodejs + commandLine: >- + node_server_pids=$(pgrep -fx '.*nodemon (--inspect )?app.js' | tr "\\n" " ") && + echo "Stopping node server with PIDs: ${node_server_pids}" && + kill -15 ${node_server_pids} &>/dev/null && echo 'Done.' + - id: attach-remote-debugger + vscodeLaunch: + inlined: | + { + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "attach", + "name": "Attach to Remote", + "address": "localhost", + "port": 9229, + "localRoot": "${workspaceFolder}", + "remoteRoot": "${workspaceFolder}" + } + ] + } + + events: + preStart: + - inject-theia-in-remote-sidecar + - copy-vsx diff --git a/pkg/flatten/testdata/k8s-ref/web-terminal-with-plugin.yaml b/pkg/flatten/testdata/k8s-ref/web-terminal-with-plugin.yaml new file mode 100644 index 00000000..4298264d --- /dev/null +++ b/pkg/flatten/testdata/k8s-ref/web-terminal-with-plugin.yaml @@ -0,0 +1,82 @@ +name: "Web terminal default" + +input: + workspace: + components: + - name: dev + container: + memoryLimit: "256Mi" + image: quay.io/wto/web-terminal-tooling:latest + args: ["tail", "-f", "/dev/null"] + env: + - name: PS1 + value: \[\e[34m\]>\[\e[m\]\[\e[33m\]>\[\e[m\] + - name: web-terminal + plugin: + kubernetes: + name: web-terminal + namespace: devworkspace-plugins + devworkspaceResources: + web-terminal: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: web-terminal + labels: + "devworkspace.devfile.io/editor-name": "web-terminal" + spec: + components: + - name: web-terminal + container: + image: quay.io/eclipse/che-machine-exec:nightly + command: ["/go/bin/che-machine-exec", + "--authenticated-user-id", "$(DEVWORKSPACE_CREATOR)", + "--idle-timeout", "$(DEVWORKSPACE_IDLE_TIMEOUT)", + "--pod-selector", "controller.devfile.io/devworkspace_id=$(DEVWORKSPACE_ID)", + "--use-bearer-token", + "--use-tls"] + endpoints: + - name: web-terminal + targetPort: 4444 + attributes: + protocol: http + type: ide + discoverable: "false" + secure: "true" + env: + - name: USE_BEARER_TOKEN + value: "true" + +output: + workspace: + components: + - name: dev + container: + image: quay.io/wto/web-terminal-tooling:latest + memoryLimit: 256Mi + args: ["tail", "-f", "/dev/null"] + env: + - value: '\[\e[34m\]>\[\e[m\]\[\e[33m\]>\[\e[m\]' + name: PS1 + - name: web-terminal + attributes: + library.devfile.io/imported-by: "web-terminal" + container: + image: quay.io/eclipse/che-machine-exec:nightly + command: ["/go/bin/che-machine-exec", + "--authenticated-user-id", "$(DEVWORKSPACE_CREATOR)", + "--idle-timeout", "$(DEVWORKSPACE_IDLE_TIMEOUT)", + "--pod-selector", "controller.devfile.io/devworkspace_id=$(DEVWORKSPACE_ID)", + "--use-bearer-token", + "--use-tls"] + endpoints: + - name: web-terminal + targetPort: 4444 + attributes: + protocol: http + type: ide + discoverable: "false" + secure: "true" + env: + - name: USE_BEARER_TOKEN + value: "true" diff --git a/pkg/flatten/testdata/parent/error_parent-has-parent.yaml b/pkg/flatten/testdata/parent/error_parent-has-parent.yaml new file mode 100644 index 00000000..4de009da --- /dev/null +++ b/pkg/flatten/testdata/parent/error_parent-has-parent.yaml @@ -0,0 +1,31 @@ +name: "Fails when parent is not flattened (has parent)" + +input: + workspace: + parent: + kubernetes: + name: test-parent-k8s + components: + - name: regular-component + container: + image: regular-test-image + name: regular-container + devworkspaceResources: + test-parent-k8s: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: parent-devworkspacetemplate + spec: + parent: + id: another-parent + components: + - name: parent-component + container: + image: test-img + env: + - name: test-env + value: original-value + +output: + errRegexp: "parents containing plugins or parents are not supported" diff --git a/pkg/flatten/testdata/parent/error_parent-has-plugins.yaml b/pkg/flatten/testdata/parent/error_parent-has-plugins.yaml new file mode 100644 index 00000000..b8805106 --- /dev/null +++ b/pkg/flatten/testdata/parent/error_parent-has-plugins.yaml @@ -0,0 +1,26 @@ +name: "Fails when parent is not flattened (has plugin)" + +input: + workspace: + parent: + kubernetes: + name: test-parent-k8s + components: + - name: regular-component + container: + image: regular-test-image + name: regular-container + devworkspaceResources: + test-parent-k8s: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: parent-devworkspacetemplate + spec: + components: + - name: parent-component + plugin: + id: parent-plugin + +output: + errRegexp: "parents containing plugins or parents are not supported" diff --git a/pkg/flatten/testdata/parent/resolve-parent-and-plugins.yaml b/pkg/flatten/testdata/parent/resolve-parent-and-plugins.yaml new file mode 100644 index 00000000..328c6984 --- /dev/null +++ b/pkg/flatten/testdata/parent/resolve-parent-and-plugins.yaml @@ -0,0 +1,70 @@ +name: "Resolve parent and plugins" + +input: + workspace: + parent: + kubernetes: + name: test-parent-k8s + components: + - name: parent-component + container: + env: + - name: test-env + value: test-value + components: + - name: regular-component + container: + image: regular-test-image + name: regular-container + - name: test-plugin + plugin: + uri: https://test-plugin.io/test-plugin + devworkspaceResources: + test-parent-k8s: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: parent-devworkspacetemplate + spec: + components: + - name: parent-component + container: + image: test-img + env: + - name: test-env + value: original-value + devfileResources: + "https://test-plugin.io/test-plugin": + schemaVersion: 2.1.0 + metadata: + name: test-plugin + components: + - name: plugin-component + container: + image: plugin-img + env: + - name: plugin-env + value: original-value +output: + workspace: + components: + - name: parent-component + attributes: + library.devfile.io/imported-by: parent + container: + image: test-img + env: + - name: test-env + value: test-value + - name: regular-component + container: + image: regular-test-image + name: regular-container + - name: plugin-component + attributes: + library.devfile.io/imported-by: test-plugin + container: + image: plugin-img + env: + - name: plugin-env + value: original-value diff --git a/pkg/flatten/testdata/parent/resolve-parent-by-id.yaml b/pkg/flatten/testdata/parent/resolve-parent-by-id.yaml new file mode 100644 index 00000000..f9f00b62 --- /dev/null +++ b/pkg/flatten/testdata/parent/resolve-parent-by-id.yaml @@ -0,0 +1,46 @@ +name: "Resolve parent by ID" + +input: + workspace: + parent: + id: test/parent/id + registryUrl: https://test-registry.io/ + components: + - name: parent-component + container: + env: + - name: test-env + value: test-value + components: + - name: regular-component + container: + image: regular-test-image + name: regular-container + devfileResources: + "https://test-registry.io/test/parent/id": + schemaVersion: 2.1.0 + metadata: + name: parent-devfile + components: + - name: parent-component + container: + image: test-img + env: + - name: test-env + value: original-value + +output: + workspace: + components: + - name: parent-component + attributes: + library.devfile.io/imported-by: parent + container: + image: test-img + env: + - name: test-env + value: test-value + - name: regular-component + container: + image: regular-test-image + name: regular-container diff --git a/pkg/flatten/testdata/parent/resolve-parent-by-k8s-reference.yaml b/pkg/flatten/testdata/parent/resolve-parent-by-k8s-reference.yaml new file mode 100644 index 00000000..c9595e4e --- /dev/null +++ b/pkg/flatten/testdata/parent/resolve-parent-by-k8s-reference.yaml @@ -0,0 +1,48 @@ +name: "Resolve parent by Kubernetes reference" + +input: + workspace: + parent: + kubernetes: + name: test-parent-k8s + components: + - name: parent-component + container: + env: + - name: test-env + value: test-value + components: + - name: regular-component + container: + image: regular-test-image + name: regular-container + devworkspaceResources: + test-parent-k8s: + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: parent-devworkspacetemplate + spec: + components: + - name: parent-component + container: + image: test-img + env: + - name: test-env + value: original-value + +output: + workspace: + components: + - name: parent-component + attributes: + library.devfile.io/imported-by: parent + container: + image: test-img + env: + - name: test-env + value: test-value + - name: regular-component + container: + image: regular-test-image + name: regular-container diff --git a/pkg/flatten/testdata/parent/resolve-parent-by-uri.yaml b/pkg/flatten/testdata/parent/resolve-parent-by-uri.yaml new file mode 100644 index 00000000..c1bb5a7b --- /dev/null +++ b/pkg/flatten/testdata/parent/resolve-parent-by-uri.yaml @@ -0,0 +1,45 @@ +name: "Resolve parent by URI" + +input: + workspace: + parent: + uri: https://test.io/path/to/parent + components: + - name: parent-component + container: + env: + - name: test-env + value: test-value + components: + - name: regular-component + container: + image: regular-test-image + name: regular-container + devfileResources: + "https://test.io/path/to/parent": + schemaVersion: 2.1.0 + metadata: + name: parent-devfile + components: + - name: parent-component + container: + image: test-img + env: + - name: test-env + value: original-value + +output: + workspace: + components: + - name: parent-component + attributes: + library.devfile.io/imported-by: parent + container: + image: test-img + env: + - name: test-env + value: test-value + - name: regular-component + container: + image: regular-test-image + name: regular-container diff --git a/pkg/flatten/testdata/plugin-id/error_fetch-unparseable-file.yaml b/pkg/flatten/testdata/plugin-id/error_fetch-unparseable-file.yaml new file mode 100644 index 00000000..c2b0ed51 --- /dev/null +++ b/pkg/flatten/testdata/plugin-id/error_fetch-unparseable-file.yaml @@ -0,0 +1,23 @@ +name: "DevWorkspace registry contains non-devfile type content" + +input: + workspace: + components: + - name: test-plugin + plugin: + id: my/test/plugin + registryUrl: "https://test-registry.io/subpath" + devworkspaceResources: + "https://test-registry.io/subpath/my/test/plugin": + metadata: + name: test-plugin + spec: + components: + - name: plugin-a + container: + name: test-container + image: test-image + + +output: + errRegexp: "could not find devfile or devworkspace object at 'https://test-registry.io/subpath/my/test/plugin'" diff --git a/pkg/flatten/testdata/plugin-id/error_invalid-schema-version.yaml b/pkg/flatten/testdata/plugin-id/error_invalid-schema-version.yaml new file mode 100644 index 00000000..ceb400f1 --- /dev/null +++ b/pkg/flatten/testdata/plugin-id/error_invalid-schema-version.yaml @@ -0,0 +1,22 @@ +name: "DevWorkspace references plugin with invalid schemaVersion" + +input: + workspace: + components: + - name: test-plugin + plugin: + id: my/test/plugin + registryUrl: "https://test-registry.io/subpath" + devfileResources: + "https://test-registry.io/subpath/my/test/plugin": + schemaVersion: 1.0.0 + metadata: + name: "plugin-a" + components: + - name: plugin-a + container: + name: test-container + image: test-image + +output: + errRegexp: "could not process devfile: unsupported schemaVersion '1.0.0'" diff --git a/pkg/flatten/testdata/plugin-id/error_on-fetch.yaml b/pkg/flatten/testdata/plugin-id/error_on-fetch.yaml new file mode 100644 index 00000000..6267d350 --- /dev/null +++ b/pkg/flatten/testdata/plugin-id/error_on-fetch.yaml @@ -0,0 +1,15 @@ +name: "Error when fetching plugin" + +input: + workspace: + components: + - name: test-plugin + plugin: + id: my/test/plugin + registryUrl: "https://test-registry.io/subpath" + errors: + "https://test-registry.io/subpath/my/test/plugin": + message: "testing error" + +output: + errRegexp: "failed to fetch file from.*testing error" diff --git a/pkg/flatten/testdata/plugin-id/error_plugin-not-found.yaml b/pkg/flatten/testdata/plugin-id/error_plugin-not-found.yaml new file mode 100644 index 00000000..eccc3583 --- /dev/null +++ b/pkg/flatten/testdata/plugin-id/error_plugin-not-found.yaml @@ -0,0 +1,15 @@ +name: "Plugin not found in registry" + +input: + workspace: + components: + - name: test-plugin + plugin: + id: my/test/plugin + registryUrl: "https://test-registry.io/subpath" + errors: + "https://test-registry.io/subpath/my/test/plugin": + statusCode: 404 + +output: + errRegexp: "could not fetch file from.*got status 404" diff --git a/pkg/flatten/testdata/plugin-id/error_unparseable-url.yaml b/pkg/flatten/testdata/plugin-id/error_unparseable-url.yaml new file mode 100644 index 00000000..4669ce02 --- /dev/null +++ b/pkg/flatten/testdata/plugin-id/error_unparseable-url.yaml @@ -0,0 +1,12 @@ +name: "Error when parsing registryURL" + +input: + workspace: + components: + - name: test-plugin + plugin: + id: my/test/plugin + registryUrl: ":/test-registry.io/subpath" + +output: + errRegexp: "failed to parse registry URL for component test-plugin" diff --git a/pkg/flatten/testdata/plugin-id/resolve-devworkspace-instead-of-devfile.yaml b/pkg/flatten/testdata/plugin-id/resolve-devworkspace-instead-of-devfile.yaml new file mode 100644 index 00000000..669964d6 --- /dev/null +++ b/pkg/flatten/testdata/plugin-id/resolve-devworkspace-instead-of-devfile.yaml @@ -0,0 +1,32 @@ +name: "DevWorkspace references DevWorkspaceTemplate plugin from registry" + +input: + workspace: + components: + - name: test-plugin + plugin: + id: my/test/plugin + registryUrl: "https://test-registry.io/subpath" + devworkspaceResources: + "https://test-registry.io/subpath/my/test/plugin": + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: test-plugin + spec: + components: + - name: plugin-a + container: + name: test-container + image: test-image + + +output: + workspace: + components: + - name: plugin-a + attributes: + library.devfile.io/imported-by: "test-plugin" + container: + name: test-container + image: test-image diff --git a/pkg/flatten/testdata/plugin-id/resolve-plugin-by-id.yaml b/pkg/flatten/testdata/plugin-id/resolve-plugin-by-id.yaml new file mode 100644 index 00000000..6cb7a9fb --- /dev/null +++ b/pkg/flatten/testdata/plugin-id/resolve-plugin-by-id.yaml @@ -0,0 +1,29 @@ +name: "DevWorkspace references plugin from plugin registry" + +input: + workspace: + components: + - name: test-plugin + plugin: + id: my/test/plugin + registryUrl: "https://test-registry.io/subpath" + devfileResources: + "https://test-registry.io/subpath/my/test/plugin": + schemaVersion: 2.0.0 + metadata: + name: "plugin-a" + components: + - name: plugin-a + container: + name: test-container + image: test-image + +output: + workspace: + components: + - name: plugin-a + attributes: + library.devfile.io/imported-by: "test-plugin" + container: + name: test-container + image: test-image diff --git a/pkg/flatten/testdata/plugin-id/resolve-plugin-multiple-registries.yaml b/pkg/flatten/testdata/plugin-id/resolve-plugin-multiple-registries.yaml new file mode 100644 index 00000000..0503c009 --- /dev/null +++ b/pkg/flatten/testdata/plugin-id/resolve-plugin-multiple-registries.yaml @@ -0,0 +1,48 @@ +name: "DevWorkspace references plugins from multiple plugin registries" + +input: + workspace: + components: + - name: test-plugin + plugin: + id: my/test/plugin + registryUrl: "https://test-registry.io/subpath" + - name: test-plugin-2 + plugin: + id: my/test/plugin-2 + registryUrl: "https://test-registry-2.io/subpath" + devfileResources: + "https://test-registry.io/subpath/my/test/plugin": + schemaVersion: 2.0.0 + metadata: + name: "plugin-a" + components: + - name: plugin-a + container: + name: test-container + image: test-image + "https://test-registry-2.io/subpath/my/test/plugin-2": + schemaVersion: 2.0.0 + metadata: + name: "plugin-b" + components: + - name: plugin-b + container: + name: test-container-b + image: test-image + +output: + workspace: + components: + - name: plugin-a + attributes: + library.devfile.io/imported-by: "test-plugin" + container: + name: test-container + image: test-image + - name: plugin-b + attributes: + library.devfile.io/imported-by: "test-plugin-2" + container: + name: test-container-b + image: test-image diff --git a/pkg/flatten/testdata/plugin-uri/error_fetch-unparseable-file.yaml b/pkg/flatten/testdata/plugin-uri/error_fetch-unparseable-file.yaml new file mode 100644 index 00000000..c4aa2e2e --- /dev/null +++ b/pkg/flatten/testdata/plugin-uri/error_fetch-unparseable-file.yaml @@ -0,0 +1,22 @@ +name: "DevWorkspace reference URI containing non-devfile type content" + +input: + workspace: + components: + - name: test-plugin + plugin: + uri: "https://my-plugin.io/test" + devworkspaceResources: + "https://my-plugin.io/test": + metadata: + name: test-plugin + spec: + components: + - name: plugin-a + container: + name: test-container + image: test-image + + +output: + errRegexp: "could not find devfile or devworkspace object at 'https://my-plugin.io/test'" diff --git a/pkg/flatten/testdata/plugin-uri/error_invalid-schema-version.yaml b/pkg/flatten/testdata/plugin-uri/error_invalid-schema-version.yaml new file mode 100644 index 00000000..9a92e17d --- /dev/null +++ b/pkg/flatten/testdata/plugin-uri/error_invalid-schema-version.yaml @@ -0,0 +1,21 @@ +name: "DevWorkspace references plugin with invalid schemaVersion" + +input: + workspace: + components: + - name: test-plugin + plugin: + uri: https://test-registry.io/old-devfiles + devfileResources: + "https://test-registry.io/old-devfiles": + schemaVersion: 1.0.0 + metadata: + name: "plugin-a" + components: + - name: plugin-a + container: + name: test-container + image: test-image + +output: + errRegexp: "could not process devfile: unsupported schemaVersion '1.0.0'" diff --git a/pkg/flatten/testdata/plugin-uri/error_on-fetch.yaml b/pkg/flatten/testdata/plugin-uri/error_on-fetch.yaml new file mode 100644 index 00000000..45bfd6d7 --- /dev/null +++ b/pkg/flatten/testdata/plugin-uri/error_on-fetch.yaml @@ -0,0 +1,14 @@ +name: "Error when fetching plugin" + +input: + workspace: + components: + - name: test-plugin + plugin: + uri: https://test-registry.io/error + errors: + "https://test-registry.io/error": + message: "testing error" + +output: + errRegexp: "failed to fetch file from.*testing error" diff --git a/pkg/flatten/testdata/plugin-uri/error_plugin-not-found.yaml b/pkg/flatten/testdata/plugin-uri/error_plugin-not-found.yaml new file mode 100644 index 00000000..5a1da1af --- /dev/null +++ b/pkg/flatten/testdata/plugin-uri/error_plugin-not-found.yaml @@ -0,0 +1,14 @@ +name: "Plugin not found in at URI" + +input: + workspace: + components: + - name: test-plugin + plugin: + uri: "https://test-registry.io/notfound" + errors: + "https://test-registry.io/notfound": + statusCode: 404 + +output: + errRegexp: "could not fetch file from.*got status 404" diff --git a/pkg/flatten/testdata/plugin-uri/resolve-devworkspace-instead-of-devfile.yaml b/pkg/flatten/testdata/plugin-uri/resolve-devworkspace-instead-of-devfile.yaml new file mode 100644 index 00000000..025d1753 --- /dev/null +++ b/pkg/flatten/testdata/plugin-uri/resolve-devworkspace-instead-of-devfile.yaml @@ -0,0 +1,31 @@ +name: "DevWorkspace references DevWorkspaceTemplate plugin from registry" + +input: + workspace: + components: + - name: test-plugin + plugin: + uri: "https://my-plugin.io/test" + devworkspaceResources: + "https://my-plugin.io/test": + kind: DevWorkspaceTemplate + apiVersion: workspace.devfile.io/v1alpha2 + metadata: + name: test-plugin + spec: + components: + - name: plugin-a + container: + name: test-container + image: test-image + + +output: + workspace: + components: + - name: plugin-a + attributes: + library.devfile.io/imported-by: "test-plugin" + container: + name: test-container + image: test-image diff --git a/pkg/flatten/testdata/plugin-uri/resolve-multiple-plugins-by-uri.yaml b/pkg/flatten/testdata/plugin-uri/resolve-multiple-plugins-by-uri.yaml new file mode 100644 index 00000000..741873fd --- /dev/null +++ b/pkg/flatten/testdata/plugin-uri/resolve-multiple-plugins-by-uri.yaml @@ -0,0 +1,46 @@ +name: "DevWorkspace references plugins from multiple plugin registries" + +input: + workspace: + components: + - name: test-plugin + plugin: + uri: "https://my-plugin.io/test" + - name: test-plugin-2 + plugin: + uri: "https://my-plugin-alt.io/test" + devfileResources: + "https://my-plugin.io/test": + schemaVersion: 2.0.0 + metadata: + name: "plugin-a" + components: + - name: plugin-a + container: + name: test-container + image: test-image + "https://my-plugin-alt.io/test": + schemaVersion: 2.0.0 + metadata: + name: "plugin-b" + components: + - name: plugin-b + container: + name: test-container-b + image: test-image + +output: + workspace: + components: + - name: plugin-a + attributes: + library.devfile.io/imported-by: "test-plugin" + container: + name: test-container + image: test-image + - name: plugin-b + attributes: + library.devfile.io/imported-by: "test-plugin-2" + container: + name: test-container-b + image: test-image diff --git a/pkg/flatten/testdata/plugin-uri/resolve-plugin-by-uri.yaml b/pkg/flatten/testdata/plugin-uri/resolve-plugin-by-uri.yaml new file mode 100644 index 00000000..a1784cd4 --- /dev/null +++ b/pkg/flatten/testdata/plugin-uri/resolve-plugin-by-uri.yaml @@ -0,0 +1,28 @@ +name: "DevWorkspace references plugin by URI" + +input: + workspace: + components: + - name: test-plugin + plugin: + uri: "https://my-plugin.io/test" + devfileResources: + "https://my-plugin.io/test": + schemaVersion: 2.0.0 + metadata: + name: "plugin-a" + components: + - name: plugin-a + container: + name: test-container + image: test-image + +output: + workspace: + components: + - name: plugin-a + attributes: + library.devfile.io/imported-by: "test-plugin" + container: + name: test-container + image: test-image