From 44b6192c4c82c1ff505c7864c104c80e7dc3f992 Mon Sep 17 00:00:00 2001 From: Maysun J Faisal Date: Mon, 8 Aug 2022 16:36:22 -0400 Subject: [PATCH 1/3] Add Kube YAML parser/reader Signed-off-by: Maysun J Faisal --- go.mod | 1 + go.sum | 3 +- pkg/devfile/generator/generators.go | 1 + pkg/devfile/parser/reader.go | 107 +++++++++++++++++++++ pkg/devfile/parser/reader_test.go | 142 ++++++++++++++++++++++++++++ tests/yamls/resources.yaml | 120 +++++++++++++++++++++++ 6 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 pkg/devfile/parser/reader.go create mode 100644 pkg/devfile/parser/reader_test.go create mode 100644 tests/yamls/resources.yaml diff --git a/go.mod b/go.mod index 2f88954f..27e01500 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/spf13/afero v1.2.2 github.com/stretchr/testify v1.7.0 github.com/xeipuuv/gojsonschema v1.2.0 + gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.21.3 k8s.io/apimachinery v0.21.3 k8s.io/client-go v0.21.3 diff --git a/go.sum b/go.sum index a1db5810..2bf4f198 100644 --- a/go.sum +++ b/go.sum @@ -748,8 +748,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/devfile/generator/generators.go b/pkg/devfile/generator/generators.go index 7ceafc19..f5767d2d 100644 --- a/pkg/devfile/generator/generators.go +++ b/pkg/devfile/generator/generators.go @@ -2,6 +2,7 @@ package generator import ( "fmt" + v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" "github.com/devfile/library/pkg/devfile/parser" "github.com/devfile/library/pkg/devfile/parser/data/v2/common" diff --git a/pkg/devfile/parser/reader.go b/pkg/devfile/parser/reader.go new file mode 100644 index 00000000..5bda46e8 --- /dev/null +++ b/pkg/devfile/parser/reader.go @@ -0,0 +1,107 @@ +package parser + +import ( + "bytes" + "io" + + "github.com/devfile/library/pkg/testingutil/filesystem" + "github.com/devfile/library/pkg/util" + "github.com/pkg/errors" + "gopkg.in/yaml.v3" + k8yaml "sigs.k8s.io/yaml" + + routev1 "github.com/openshift/api/route/v1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" +) + +// YamlSrc specifies the src of the yaml in either Path, URL or Data format +type YamlSrc struct { + // Path is a relative or absolute yaml path. + Path string + // URL is the URL address of the specific yaml. + URL string + // Data is the yaml content in []byte format. + Data []byte +} + +// ReadKubernetesYaml reads a yaml Kubernetes file from either the Path, URL or Data provided. +// It returns Deployments, Services, Routes resources as the primary Kubernetes resources. +// Other Kubernetes resources are returned as []byte type. Consumers interested in other Kubernetes resources +// are expected to Unmarshal it to the struct of the respective resource. +func ReadKubernetesYaml(src YamlSrc, fs filesystem.Filesystem) ([]appsv1.Deployment, []corev1.Service, []routev1.Route, [][]byte, error) { + + var data []byte + var err error + + if src.URL != "" { + data, err = util.DownloadFileInMemory(src.URL) + if err != nil { + return nil, nil, nil, nil, errors.Wrapf(err, "failed to download file %q", src.URL) + } + } else if src.Path != "" { + absPath, err := util.GetAbsPath(src.Path) + if err != nil { + return nil, nil, nil, nil, err + } + data, err = fs.ReadFile(absPath) + if err != nil { + return nil, nil, nil, nil, errors.Wrapf(err, "failed to read yaml from path %q", src.Path) + } + } else if len(src.Data) > 0 { + data = src.Data + } + + var values []interface{} + dec := yaml.NewDecoder(bytes.NewReader(data)) + for { + var value interface{} + err = dec.Decode(&value) + if err != nil { + if err == io.EOF { + break + } + return nil, nil, nil, nil, err + } + values = append(values, value) + } + + var deployments []appsv1.Deployment + var services []corev1.Service + var routes []routev1.Route + var otherResources [][]byte + + for _, value := range values { + var deployment appsv1.Deployment + var service corev1.Service + var route routev1.Route + + byteData, err := k8yaml.Marshal(value) + if err != nil { + return nil, nil, nil, nil, err + } + + kubernetesMap := value.(map[string]interface{}) + kind := kubernetesMap["kind"] + + switch kind { + case "Deployment": + err = k8yaml.Unmarshal(byteData, &deployment) + deployments = append(deployments, deployment) + case "Service": + err = k8yaml.Unmarshal(byteData, &service) + services = append(services, service) + case "Route": + err = k8yaml.Unmarshal(byteData, &route) + routes = append(routes, route) + default: + otherResources = append(otherResources, byteData) + } + + if err != nil { + return nil, nil, nil, nil, err + } + } + + return deployments, services, routes, otherResources, nil +} diff --git a/pkg/devfile/parser/reader_test.go b/pkg/devfile/parser/reader_test.go new file mode 100644 index 00000000..853c19ae --- /dev/null +++ b/pkg/devfile/parser/reader_test.go @@ -0,0 +1,142 @@ +package parser + +import ( + "net" + "net/http" + "net/http/httptest" + "testing" + + "github.com/devfile/library/pkg/testingutil/filesystem" + "github.com/devfile/library/pkg/util" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + k8yaml "sigs.k8s.io/yaml" +) + +func TestReadKubernetesYaml(t *testing.T) { + const serverIP = "127.0.0.1:9080" + var data []byte + + fs := filesystem.DefaultFs{} + absPath, err := util.GetAbsPath("../../../tests/yamls/resources.yaml") + if err != nil { + t.Error(err) + return + } + + data, err = fs.ReadFile(absPath) + if err != nil { + t.Error(err) + return + } + + // Mocking the YAML file endpoint on a very basic level + testServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, err = w.Write(data) + if err != nil { + t.Errorf("Unexpected error while writing data: %v", err) + } + })) + // create a listener with the desired port. + l, err := net.Listen("tcp", serverIP) + if err != nil { + t.Errorf("Unexpected error while creating listener: %v", err) + return + } + + // NewUnstartedServer creates a listener. Close that listener and replace + // with the one we created. + testServer.Listener.Close() + testServer.Listener = l + + testServer.Start() + defer testServer.Close() + + tests := []struct { + name string + src YamlSrc + fs filesystem.Filesystem + wantErr bool + wantDeploymentNames []string + wantServiceNames []string + wantRouteNames []string + wantOtherNames []string + }{ + { + name: "Read the YAML from the URL", + src: YamlSrc{ + URL: "http://" + serverIP, + }, + fs: filesystem.DefaultFs{}, + wantDeploymentNames: []string{"deploy-sample"}, + wantServiceNames: []string{"service-sample"}, + wantRouteNames: []string{"route-sample"}, + wantOtherNames: []string{"pvc-sample"}, + }, + { + name: "Read the YAML from the Path", + src: YamlSrc{ + Path: "../../../tests/yamls/resources.yaml", + }, + fs: filesystem.DefaultFs{}, + wantDeploymentNames: []string{"deploy-sample"}, + wantServiceNames: []string{"service-sample"}, + wantRouteNames: []string{"route-sample"}, + wantOtherNames: []string{"pvc-sample"}, + }, + { + name: "Read the YAML from the Data", + src: YamlSrc{ + Data: data, + }, + fs: filesystem.DefaultFs{}, + wantDeploymentNames: []string{"deploy-sample"}, + wantServiceNames: []string{"service-sample"}, + wantRouteNames: []string{"route-sample"}, + wantOtherNames: []string{"pvc-sample"}, + }, + { + name: "Bad URL", + src: YamlSrc{ + URL: "http://badurl", + }, + fs: filesystem.DefaultFs{}, + wantErr: true, + }, + { + name: "Bad Path", + src: YamlSrc{ + Path: "$%^&", + }, + fs: filesystem.DefaultFs{}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + deployments, services, routes, others, err := ReadKubernetesYaml(tt.src, tt.fs) + if (err != nil) != tt.wantErr { + t.Errorf("unexpected error: %v", err) + return + } + for _, deploy := range deployments { + assert.Contains(t, tt.wantDeploymentNames, deploy.Name) + } + for _, svc := range services { + assert.Contains(t, tt.wantServiceNames, svc.Name) + } + for _, route := range routes { + assert.Contains(t, tt.wantRouteNames, route.Name) + } + for _, other := range others { + pvc := corev1.PersistentVolumeClaim{} + err = k8yaml.Unmarshal(other, &pvc) + if err != nil { + t.Error(err) + } + assert.Contains(t, tt.wantOtherNames, pvc.Name) + } + }) + } +} diff --git a/tests/yamls/resources.yaml b/tests/yamls/resources.yaml new file mode 100644 index 00000000..267b8f11 --- /dev/null +++ b/tests/yamls/resources.yaml @@ -0,0 +1,120 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + name: deploy-sample + namespace: application-service-system +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: component-sample + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/instance: component-sample + spec: + containers: + - env: + - name: FOO + value: foo1 + - name: BAR + value: bar1 + image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + imagePullPolicy: Always + livenessProbe: + httpGet: + path: / + port: 1111 + initialDelaySeconds: 10 + periodSeconds: 10 + name: container-image + ports: + - containerPort: 1111 + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: 1111 + resources: + limits: + cpu: "2" + memory: 500Mi + storage: 400Mi + requests: + cpu: 700m + memory: 400Mi + storage: 200Mi +status: {} +--- +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + name: service-sample + namespace: application-service-system +spec: + ports: + - port: 1111 + targetPort: 1111 + selector: + app.kubernetes.io/instance: component-sample +status: + loadBalancer: {} +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + name: route-sample + namespace: application-service-system +spec: + host: route111 + port: + targetPort: 1111 + tls: + insecureEdgeTerminationPolicy: Redirect + termination: edge + to: + kind: Service + name: component-sample + weight: 100 +status: {} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: pvc-sample +spec: + accessModes: + - ReadWriteOnce + volumeMode: Filesystem + resources: + requests: + storage: 8Gi + storageClassName: slow + selector: + matchLabels: + release: "stable" + matchExpressions: + - {key: environment, operator: In, values: [dev]} From 2822e30455e52cfacb98480420378464e13ee476 Mon Sep 17 00:00:00 2001 From: Maysun J Faisal Date: Mon, 8 Aug 2022 16:50:15 -0400 Subject: [PATCH 2/3] Add test case Signed-off-by: Maysun J Faisal --- pkg/devfile/parser/reader_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/devfile/parser/reader_test.go b/pkg/devfile/parser/reader_test.go index 853c19ae..fe82c235 100644 --- a/pkg/devfile/parser/reader_test.go +++ b/pkg/devfile/parser/reader_test.go @@ -52,6 +52,8 @@ func TestReadKubernetesYaml(t *testing.T) { testServer.Start() defer testServer.Close() + badData := append(data, 59) + tests := []struct { name string src YamlSrc @@ -111,6 +113,14 @@ func TestReadKubernetesYaml(t *testing.T) { fs: filesystem.DefaultFs{}, wantErr: true, }, + { + name: "Bad Data", + src: YamlSrc{ + Data: badData, + }, + fs: filesystem.DefaultFs{}, + wantErr: true, + }, } for _, tt := range tests { From 4d8ed7593d2f5312565ba70ddff4ec1660a56c3d Mon Sep 17 00:00:00 2001 From: Maysun J Faisal Date: Thu, 11 Aug 2022 13:20:46 -0400 Subject: [PATCH 3/3] Address review feedback Signed-off-by: Maysun J Faisal --- README.md | 8 ++ pkg/devfile/parser/reader.go | 64 ++++++++---- pkg/devfile/parser/reader_test.go | 93 +++++++++++------ tests/yamls/resources.yaml | 159 ++++++++++++++++++++++++++++++ 4 files changed, 275 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index f1173485..8d3c43b2 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,14 @@ The function documentation can be accessed via [pkg.go.dev](https://pkg.go.dev/g // write to the devfile on disk err = devfile.WriteYamlDevfile() ``` +6. To parse the outerloop Kubernetes/OpenShift component's uri or inline content, call the read and parse functions + ```go + // Read the YAML content + values, err := ReadKubernetesYaml(src, fs) + + // Get the Kubernetes resources + resources, err := ParseKubernetesYaml(values) + ``` ## Projects using devfile/library diff --git a/pkg/devfile/parser/reader.go b/pkg/devfile/parser/reader.go index 5bda46e8..5c3ba815 100644 --- a/pkg/devfile/parser/reader.go +++ b/pkg/devfile/parser/reader.go @@ -13,23 +13,33 @@ import ( routev1 "github.com/openshift/api/route/v1" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + extensionsv1 "k8s.io/api/extensions/v1beta1" ) // YamlSrc specifies the src of the yaml in either Path, URL or Data format type YamlSrc struct { - // Path is a relative or absolute yaml path. + // Path to the yaml file Path string - // URL is the URL address of the specific yaml. + // URL of the yaml file URL string - // Data is the yaml content in []byte format. + // Data is the yaml content in []byte format Data []byte } +// KubernetesResources struct contains the Deployments, Services, +// Routes and Ingresses resources +type KubernetesResources struct { + Deployments []appsv1.Deployment + Services []corev1.Service + Routes []routev1.Route + Ingresses []extensionsv1.Ingress +} + // ReadKubernetesYaml reads a yaml Kubernetes file from either the Path, URL or Data provided. -// It returns Deployments, Services, Routes resources as the primary Kubernetes resources. -// Other Kubernetes resources are returned as []byte type. Consumers interested in other Kubernetes resources -// are expected to Unmarshal it to the struct of the respective resource. -func ReadKubernetesYaml(src YamlSrc, fs filesystem.Filesystem) ([]appsv1.Deployment, []corev1.Service, []routev1.Route, [][]byte, error) { +// It returns all the parsed Kubernetes objects as an array of interface. +// Consumers interested in the Kubernetes resources are expected to Unmarshal +// it to the struct of the respective Kubernetes resource. +func ReadKubernetesYaml(src YamlSrc, fs filesystem.Filesystem) ([]interface{}, error) { var data []byte var err error @@ -37,16 +47,12 @@ func ReadKubernetesYaml(src YamlSrc, fs filesystem.Filesystem) ([]appsv1.Deploym if src.URL != "" { data, err = util.DownloadFileInMemory(src.URL) if err != nil { - return nil, nil, nil, nil, errors.Wrapf(err, "failed to download file %q", src.URL) + return nil, errors.Wrapf(err, "failed to download file %q", src.URL) } } else if src.Path != "" { - absPath, err := util.GetAbsPath(src.Path) - if err != nil { - return nil, nil, nil, nil, err - } - data, err = fs.ReadFile(absPath) + data, err = fs.ReadFile(src.Path) if err != nil { - return nil, nil, nil, nil, errors.Wrapf(err, "failed to read yaml from path %q", src.Path) + return nil, errors.Wrapf(err, "failed to read yaml from path %q", src.Path) } } else if len(src.Data) > 0 { data = src.Data @@ -61,24 +67,34 @@ func ReadKubernetesYaml(src YamlSrc, fs filesystem.Filesystem) ([]appsv1.Deploym if err == io.EOF { break } - return nil, nil, nil, nil, err + return nil, err } values = append(values, value) } + return values, nil +} + +// ParseKubernetesYaml Unmarshals the interface array of the Kubernetes resources +// and returns it as a KubernetesResources struct. Only Deployment, Service, Route +// and Ingress are processed. Consumers interested in other Kubernetes resources +// are expected to parse the values interface array an Unmarshal it to their +// desired Kuberenetes struct +func ParseKubernetesYaml(values []interface{}) (KubernetesResources, error) { var deployments []appsv1.Deployment var services []corev1.Service var routes []routev1.Route - var otherResources [][]byte + var ingresses []extensionsv1.Ingress for _, value := range values { var deployment appsv1.Deployment var service corev1.Service var route routev1.Route + var ingress extensionsv1.Ingress byteData, err := k8yaml.Marshal(value) if err != nil { - return nil, nil, nil, nil, err + return KubernetesResources{}, err } kubernetesMap := value.(map[string]interface{}) @@ -94,14 +110,20 @@ func ReadKubernetesYaml(src YamlSrc, fs filesystem.Filesystem) ([]appsv1.Deploym case "Route": err = k8yaml.Unmarshal(byteData, &route) routes = append(routes, route) - default: - otherResources = append(otherResources, byteData) + case "Ingress": + err = k8yaml.Unmarshal(byteData, &ingress) + ingresses = append(ingresses, ingress) } if err != nil { - return nil, nil, nil, nil, err + return KubernetesResources{}, err } } - return deployments, services, routes, otherResources, nil + return KubernetesResources{ + Deployments: deployments, + Services: services, + Routes: routes, + Ingresses: ingresses, + }, nil } diff --git a/pkg/devfile/parser/reader_test.go b/pkg/devfile/parser/reader_test.go index fe82c235..5291a984 100644 --- a/pkg/devfile/parser/reader_test.go +++ b/pkg/devfile/parser/reader_test.go @@ -4,16 +4,15 @@ import ( "net" "net/http" "net/http/httptest" + "reflect" "testing" "github.com/devfile/library/pkg/testingutil/filesystem" "github.com/devfile/library/pkg/util" "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" - k8yaml "sigs.k8s.io/yaml" ) -func TestReadKubernetesYaml(t *testing.T) { +func TestReadAndParseKubernetesYaml(t *testing.T) { const serverIP = "127.0.0.1:9080" var data []byte @@ -62,6 +61,7 @@ func TestReadKubernetesYaml(t *testing.T) { wantDeploymentNames []string wantServiceNames []string wantRouteNames []string + wantIngressNames []string wantOtherNames []string }{ { @@ -70,10 +70,11 @@ func TestReadKubernetesYaml(t *testing.T) { URL: "http://" + serverIP, }, fs: filesystem.DefaultFs{}, - wantDeploymentNames: []string{"deploy-sample"}, - wantServiceNames: []string{"service-sample"}, - wantRouteNames: []string{"route-sample"}, - wantOtherNames: []string{"pvc-sample"}, + wantDeploymentNames: []string{"deploy-sample", "deploy-sample-2"}, + wantServiceNames: []string{"service-sample", "service-sample-2"}, + wantRouteNames: []string{"route-sample", "route-sample-2"}, + wantIngressNames: []string{"ingress-sample", "ingress-sample-2"}, + wantOtherNames: []string{"pvc-sample", "pvc-sample-2"}, }, { name: "Read the YAML from the Path", @@ -81,10 +82,11 @@ func TestReadKubernetesYaml(t *testing.T) { Path: "../../../tests/yamls/resources.yaml", }, fs: filesystem.DefaultFs{}, - wantDeploymentNames: []string{"deploy-sample"}, - wantServiceNames: []string{"service-sample"}, - wantRouteNames: []string{"route-sample"}, - wantOtherNames: []string{"pvc-sample"}, + wantDeploymentNames: []string{"deploy-sample", "deploy-sample-2"}, + wantServiceNames: []string{"service-sample", "service-sample-2"}, + wantRouteNames: []string{"route-sample", "route-sample-2"}, + wantIngressNames: []string{"ingress-sample", "ingress-sample-2"}, + wantOtherNames: []string{"pvc-sample", "pvc-sample-2"}, }, { name: "Read the YAML from the Data", @@ -92,10 +94,11 @@ func TestReadKubernetesYaml(t *testing.T) { Data: data, }, fs: filesystem.DefaultFs{}, - wantDeploymentNames: []string{"deploy-sample"}, - wantServiceNames: []string{"service-sample"}, - wantRouteNames: []string{"route-sample"}, - wantOtherNames: []string{"pvc-sample"}, + wantDeploymentNames: []string{"deploy-sample", "deploy-sample-2"}, + wantServiceNames: []string{"service-sample", "service-sample-2"}, + wantRouteNames: []string{"route-sample", "route-sample-2"}, + wantIngressNames: []string{"ingress-sample", "ingress-sample-2"}, + wantOtherNames: []string{"pvc-sample", "pvc-sample-2"}, }, { name: "Bad URL", @@ -125,27 +128,61 @@ func TestReadKubernetesYaml(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - deployments, services, routes, others, err := ReadKubernetesYaml(tt.src, tt.fs) + values, err := ReadKubernetesYaml(tt.src, tt.fs) if (err != nil) != tt.wantErr { t.Errorf("unexpected error: %v", err) return } - for _, deploy := range deployments { - assert.Contains(t, tt.wantDeploymentNames, deploy.Name) - } - for _, svc := range services { - assert.Contains(t, tt.wantServiceNames, svc.Name) - } - for _, route := range routes { - assert.Contains(t, tt.wantRouteNames, route.Name) + + for _, value := range values { + kubernetesMap := value.(map[string]interface{}) + + kind := kubernetesMap["kind"] + metadataMap := kubernetesMap["metadata"].(map[string]interface{}) + name := metadataMap["name"] + + switch kind { + case "Deployment": + assert.Contains(t, tt.wantDeploymentNames, name) + case "Service": + assert.Contains(t, tt.wantServiceNames, name) + case "Route": + assert.Contains(t, tt.wantRouteNames, name) + case "Ingress": + assert.Contains(t, tt.wantIngressNames, name) + default: + assert.Contains(t, tt.wantOtherNames, name) + } } - for _, other := range others { - pvc := corev1.PersistentVolumeClaim{} - err = k8yaml.Unmarshal(other, &pvc) + + if len(values) > 0 { + resources, err := ParseKubernetesYaml(values) if err != nil { t.Error(err) + return + } + + if reflect.DeepEqual(resources, KubernetesResources{}) { + t.Error("Kubernetes resources is empty, expected to contain some resources") + } else { + deployments := resources.Deployments + services := resources.Services + routes := resources.Routes + ingresses := resources.Ingresses + + for _, deploy := range deployments { + assert.Contains(t, tt.wantDeploymentNames, deploy.Name) + } + for _, svc := range services { + assert.Contains(t, tt.wantServiceNames, svc.Name) + } + for _, route := range routes { + assert.Contains(t, tt.wantRouteNames, route.Name) + } + for _, ingress := range ingresses { + assert.Contains(t, tt.wantIngressNames, ingress.Name) + } } - assert.Contains(t, tt.wantOtherNames, pvc.Name) } }) } diff --git a/tests/yamls/resources.yaml b/tests/yamls/resources.yaml index 267b8f11..1ae81d86 100644 --- a/tests/yamls/resources.yaml +++ b/tests/yamls/resources.yaml @@ -55,6 +55,63 @@ spec: storage: 200Mi status: {} --- +apiVersion: apps/v1 +kind: Deployment +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + name: deploy-sample-2 + namespace: application-service-system +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: component-sample + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app.kubernetes.io/instance: component-sample + spec: + containers: + - env: + - name: FOO + value: foo1 + - name: BAR + value: bar1 + image: quay.io/redhat-appstudio/user-workload:application-service-system-component-sample + imagePullPolicy: Always + livenessProbe: + httpGet: + path: / + port: 1111 + initialDelaySeconds: 10 + periodSeconds: 10 + name: container-image + ports: + - containerPort: 1111 + readinessProbe: + initialDelaySeconds: 10 + periodSeconds: 10 + tcpSocket: + port: 1111 + resources: + limits: + cpu: "2" + memory: 500Mi + storage: 400Mi + requests: + cpu: 700m + memory: 400Mi + storage: 200Mi +status: {} +--- apiVersion: v1 kind: Service metadata: @@ -76,6 +133,27 @@ spec: status: loadBalancer: {} --- +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + name: service-sample-2 + namespace: application-service-system +spec: + ports: + - port: 1111 + targetPort: 1111 + selector: + app.kubernetes.io/instance: component-sample +status: + loadBalancer: {} +--- apiVersion: route.openshift.io/v1 kind: Route metadata: @@ -101,6 +179,69 @@ spec: weight: 100 status: {} --- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/created-by: application-service + app.kubernetes.io/instance: component-sample + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: backend + app.kubernetes.io/part-of: application-sample + name: route-sample-2 + namespace: application-service-system +spec: + host: route111 + port: + targetPort: 1111 + tls: + insecureEdgeTerminationPolicy: Redirect + termination: edge + to: + kind: Service + name: component-sample + weight: 100 +status: {} +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress-sample + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + ingressClassName: nginx-example + rules: + - http: + paths: + - path: /testpath + pathType: Prefix + backend: + service: + name: test + port: + number: 80 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress-sample-2 + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + ingressClassName: nginx-example + rules: + - http: + paths: + - path: /testpath + pathType: Prefix + backend: + service: + name: test + port: + number: 80 +--- apiVersion: v1 kind: PersistentVolumeClaim metadata: @@ -118,3 +259,21 @@ spec: release: "stable" matchExpressions: - {key: environment, operator: In, values: [dev]} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: pvc-sample-2 +spec: + accessModes: + - ReadWriteOnce + volumeMode: Filesystem + resources: + requests: + storage: 8Gi + storageClassName: slow + selector: + matchLabels: + release: "stable" + matchExpressions: + - {key: environment, operator: In, values: [dev]}