From 7ade1153922ee9655669cad98cfd194970ced6c3 Mon Sep 17 00:00:00 2001 From: Nootan Singh Date: Tue, 31 Jan 2023 16:20:14 +0530 Subject: [PATCH 1/6] Add governor api call provision to send cluster telemetry data Signed-off-by: Nootan Singh --- src/api/v1alpha1/inspectionpolicy_types.go | 18 ++++++ src/api/v1alpha1/workload.go | 3 + ...oharbor.goharbor.io_assessmentreports.yaml | 17 +++++ ...harbor.goharbor.io_inspectionpolicies.yaml | 17 +++++ src/config/samples/policy.yaml | 5 ++ src/go.mod | 1 + src/go.sum | 3 + src/pkg/data/consumers/governor/exporter.go | 62 +++++++++++++++++++ src/pkg/inspection/controller.go | 15 +++++ src/pkg/inspection/scanner.go | 31 +++++++--- 10 files changed, 163 insertions(+), 9 deletions(-) create mode 100644 src/pkg/data/consumers/governor/exporter.go diff --git a/src/api/v1alpha1/inspectionpolicy_types.go b/src/api/v1alpha1/inspectionpolicy_types.go index 4c875ef2..6ed909cb 100644 --- a/src/api/v1alpha1/inspectionpolicy_types.go +++ b/src/api/v1alpha1/inspectionpolicy_types.go @@ -114,6 +114,24 @@ type Assessment struct { // ElasticSearch certificate for the client // +kubebuilder:validation:Optional OpenSearchCert string `json:"openSearchCert"` + // Indicate whether to config of governor + Governor Governor `json:"governor"` +} + +// Governor contains policies for governor to send report +type Governor struct { + // Indicate whether to send the reports to governor + // +kubebuilder:default:=false + Enabled bool `json:"enabled"` + // Unique identifier of the cluster + // +kubebuilder:validation:Optional + ClusterID string `json:"clusterId"` + // Api url to send telemetry data + // +kubebuilder:validation:Optional + URL string `json:"url"` + // Api token for user authentication + // +kubebuilder:validation:Optional + APIToken string `json:"apiToken"` } // FollowupAction defines what actions should be applied when security expectations are matched. diff --git a/src/api/v1alpha1/workload.go b/src/api/v1alpha1/workload.go index 765c5e07..464fbe0b 100644 --- a/src/api/v1alpha1/workload.go +++ b/src/api/v1alpha1/workload.go @@ -3,6 +3,7 @@ package v1alpha1 import ( + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" ) @@ -39,6 +40,8 @@ var AllWorkloads = []string{ type Workload struct { // For pure Pod, no object reference. corev1.ObjectReference `json:"metadata"` + // Replicas of this workload. + Replicas appsv1.ReplicaSet `json:"replicas"` // Pods of this workload. Pods []*Pod `json:"pods"` } diff --git a/src/config/crd/bases/goharbor.goharbor.io_assessmentreports.yaml b/src/config/crd/bases/goharbor.goharbor.io_assessmentreports.yaml index b742bff2..98cb163b 100644 --- a/src/config/crd/bases/goharbor.goharbor.io_assessmentreports.yaml +++ b/src/config/crd/bases/goharbor.goharbor.io_assessmentreports.yaml @@ -128,6 +128,23 @@ spec: elasticSearchUser: description: ElasticSearch username for the client type: string + governor: + description: Indicate whether to config of governor + properties: + enabled: + default: false + description: Indicate whether to send the reports to governor + type: boolean + clusterId: + description: Unique identifier of the cluster + type: string + url: + description: Api url to send telemetry data + type: string + apiToken: + description: Api token for user authentication + type: string + type: object format: default: YAML description: Format of the assessment report data. diff --git a/src/config/crd/bases/goharbor.goharbor.io_inspectionpolicies.yaml b/src/config/crd/bases/goharbor.goharbor.io_inspectionpolicies.yaml index 5fb1b240..8e824cda 100644 --- a/src/config/crd/bases/goharbor.goharbor.io_inspectionpolicies.yaml +++ b/src/config/crd/bases/goharbor.goharbor.io_inspectionpolicies.yaml @@ -179,6 +179,23 @@ spec: openSearchUser: description: ElasticSearch username for the client type: string + governor: + description: Indicate whether to config of governor + properties: + enabled: + default: false + description: Indicate whether to send the reports to governor + type: boolean + clusterId: + description: Unique identifier of the cluster + type: string + url: + description: Api url to send telemetry data + type: string + apiToken: + description: Api token for user authentication + type: string + type: object required: - elasticSearchEnabled - format diff --git a/src/config/samples/policy.yaml b/src/config/samples/policy.yaml index 2644f0f4..b53c7615 100644 --- a/src/config/samples/policy.yaml +++ b/src/config/samples/policy.yaml @@ -57,6 +57,11 @@ spec: openSearchAddr: "https://opensearch-cluster-master.default:9200" openSearchUser: "admin" openSearchPasswd: "admin" + governor: + enabled: true + clusterId: "65a03970-c53a-4ba1-8d1f-42c9f95d2761" + url: "app-catalog.vmware.com/api" + apiToken: "n6yDMkMEghPUYJDGsn39I_GYNnPh4Vi-LnaH2URjpgwcXbFIVYzMU-n8LRzTJGKO" baselines: - kind: "vulnerability" baseline: "High" diff --git a/src/go.mod b/src/go.mod index 17e9c122..0e890c19 100644 --- a/src/go.mod +++ b/src/go.mod @@ -21,6 +21,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/spf13/viper v1.14.0 github.com/stretchr/testify v1.8.1 + gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client v0.0.0-20230213153333-158da97aac53 k8s.io/api v0.25.4 k8s.io/apimachinery v0.25.4 k8s.io/apiserver v0.25.4 diff --git a/src/go.sum b/src/go.sum index 6c0a0854..83cd7c57 100644 --- a/src/go.sum +++ b/src/go.sum @@ -638,6 +638,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client v0.0.0-20230213153333-158da97aac53 h1:a1Xa7i9FOnW72qQBC7pPTLrcmUcu4/dS0fpxqAGxQDs= +gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client v0.0.0-20230213153333-158da97aac53/go.mod h1:Z3g5cIHSpGMDg7W8dXek1xsxZn4ZsOqNCu5vBXY0DXM= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= @@ -809,6 +811,7 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= diff --git a/src/pkg/data/consumers/governor/exporter.go b/src/pkg/data/consumers/governor/exporter.go new file mode 100644 index 00000000..ad6c626a --- /dev/null +++ b/src/pkg/data/consumers/governor/exporter.go @@ -0,0 +1,62 @@ +package consumers + +import ( + "context" + "errors" + api "github.com/vmware-tanzu/cloud-native-security-inspector/src/api/v1alpha1" + openapi "gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client" + "net/http" +) + +type GovernorExporter struct { + Report *api.AssessmentReport + ClusterID string + ApiURL string + ApiToken string +} + +// SendReportToGovernor is used to send report to governor url http end point. +func (g GovernorExporter) SendReportToGovernor() error { + // Get api request model from assessment report. + kubernetesCluster := getAPIRequest(*g.Report) + + // Create api client to governor api. + apiClient := openapi.NewAPIClient(openapi.NewConfiguration()) + apiSaveClusterRequest := apiClient.ClustersApi.UpdateTelemetry(context.Background(), g.ClusterID) + + // Call api cluster to send telemetry data and get response. + response, err := apiSaveClusterRequest.KubernetesTelemetryRequest(kubernetesCluster).Execute() + if err != nil { + return err + } + if response.StatusCode != http.StatusOK { + return errors.New(response.Status) + } + + return nil +} + +// getAPIRequest is used to map assessment report to client model. +func getAPIRequest(doc api.AssessmentReport) openapi.KubernetesTelemetryRequest { + kubernetesCluster := openapi.NewKubernetesTelemetryRequestWithDefaults() + for _, nsa := range doc.Spec.NamespaceAssessments { + for _, workloadAssessment := range nsa.WorkloadAssessments { + kubernetesWorkloads := openapi.NewKubernetesWorkloadWithDefaults() + kubernetesWorkloads.Name = workloadAssessment.Workload.Name + kubernetesWorkloads.Kind = workloadAssessment.Workload.Kind + kubernetesWorkloads.Namespace = nsa.Namespace.Name + kubernetesWorkloads.Replicas = *workloadAssessment.Workload.Replicas.Spec.Replicas + for _, pod := range workloadAssessment.Workload.Pods { + containerData := openapi.NewContainerWithDefaults() + for _, container := range pod.Containers { + containerData.Name = container.Name + containerData.ImageId = container.ImageID + containerData.Image = container.Image + kubernetesWorkloads.Containers = append(kubernetesWorkloads.Containers, *containerData) + } + } + kubernetesCluster.Workloads = append(kubernetesCluster.Workloads, *kubernetesWorkloads) + } + } + return *kubernetesCluster +} diff --git a/src/pkg/inspection/controller.go b/src/pkg/inspection/controller.go index b2254838..fedb7f49 100644 --- a/src/pkg/inspection/controller.go +++ b/src/pkg/inspection/controller.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/vmware-tanzu/cloud-native-security-inspector/src/lib/log" es "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/es" + governor "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/governor" osearch "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/opensearch" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" @@ -331,6 +332,20 @@ func (c *controller) Run(ctx context.Context, policy *v1alpha1.InspectionPolicy) } } + // Read config from InspectionPolicy, send assessment reports to Governor api if governor enabled. + if policy.Spec.Inspection.Assessment.Governor.Enabled { + governorConfig := policy.Spec.Inspection.Assessment.Governor + exporter := governor.GovernorExporter{ + Report: report, + ClusterID: governorConfig.ClusterID, + ApiURL: governorConfig.URL, + ApiToken: governorConfig.APIToken, + } + if err := exporter.SendReportToGovernor(); err != nil { + return err + } + } + // Create report CR if necessary. if policy.Spec.Inspection.Assessment.Generate { if err := c.kc.Create(ctx, report); err != nil { diff --git a/src/pkg/inspection/scanner.go b/src/pkg/inspection/scanner.go index cc417482..04270cad 100644 --- a/src/pkg/inspection/scanner.go +++ b/src/pkg/inspection/scanner.go @@ -130,7 +130,7 @@ func (ds *DefaultScanner) processPod(ctx context.Context, pod corev1.Pod, indexe return errors.Wrap(err, "get pod reference") } - owner, err := ds.getOwner(ctx, pod) + owner, replicaSet, err := ds.getOwner(ctx, pod) if err != nil { return errors.Wrap(err, "get pod owner") } @@ -142,6 +142,7 @@ func (ds *DefaultScanner) processPod(ctx context.Context, pod corev1.Pod, indexe if _, ok := indexer[uid]; !ok { indexer[uid] = &v1alpha1.Workload{} + indexer[uid].Replicas = *replicaSet } // Set owner reference. @@ -160,33 +161,35 @@ func (ds *DefaultScanner) processPod(ctx context.Context, pod corev1.Pod, indexe return nil } -func (ds *DefaultScanner) getOwner(ctx context.Context, pod corev1.Pod) (*corev1.ObjectReference, error) { +func (ds *DefaultScanner) getOwner(ctx context.Context, pod corev1.Pod) (*corev1.ObjectReference, *appsv1.ReplicaSet, error) { refs := pod.GetOwnerReferences() if ref, yes := hasReplicaSet(refs); yes { - rsOwnerRef, err := ds.getReplicaSetOwnerRef(ctx, types.NamespacedName{ + rsOwnerRef, replicaSetData, err := ds.getReplicaSetOwnerRef(ctx, types.NamespacedName{ Namespace: pod.Namespace, Name: ref.Name, }) if err != nil { - return nil, errors.Wrap(err, "get replicaSet owner ref") + return nil, nil, errors.Wrap(err, "get replicaSet owner ref") } // Deployment? if rsOwnerRef != nil { - return rsOwnerRef, nil + return rsOwnerRef, replicaSetData, nil } } - return extractOwnerRef(pod.Namespace, refs), nil + return extractOwnerRef(pod.Namespace, refs), nil, nil } -func (ds *DefaultScanner) getReplicaSetOwnerRef(ctx context.Context, namespacedName types.NamespacedName) (*corev1.ObjectReference, error) { +func (ds *DefaultScanner) getReplicaSetOwnerRef(ctx context.Context, namespacedName types.NamespacedName) (*corev1.ObjectReference, *appsv1.ReplicaSet, error) { var rps appsv1.ReplicaSet if err := ds.Get(ctx, namespacedName, &rps); err != nil { - return nil, err + return nil, nil, err } - return extractOwnerRef(namespacedName.Namespace, rps.GetOwnerReferences()), nil + ownerRef := rps.GetOwnerReferences() + + return extractOwnerRef(namespacedName.Namespace, ownerRef), extractReplicaSet(ownerRef, rps), nil } // ScanOtherResource implements inspection.Scanner. @@ -268,6 +271,16 @@ func extractOwnerRef(ns string, ownerRefs []metav1.OwnerReference) *corev1.Objec return nil } +func extractReplicaSet(ownerRefs []metav1.OwnerReference, rps appsv1.ReplicaSet) *appsv1.ReplicaSet { + for _, or := range ownerRefs { + if isWorkload(or.Kind) { + return &appsv1.ReplicaSet{Spec: appsv1.ReplicaSetSpec{Replicas: rps.Spec.Replicas}} + } + } + + return nil +} + func hasReplicaSet(ownerRefs []metav1.OwnerReference) (metav1.OwnerReference, bool) { for _, or := range ownerRefs { if or.Kind == v1alpha1.WorkloadKindReplicaSet { From 95c58a200b63b25c397a25a42e8e06554515f17d Mon Sep 17 00:00:00 2001 From: Nootan Singh Date: Wed, 22 Feb 2023 09:37:12 +0530 Subject: [PATCH 2/6] Added UTs and addressed comments Signed-off-by: Nootan Singh --- src/api/v1alpha1/inspectionpolicy_types.go | 1 + src/api/v1alpha1/workload.go | 3 +- ...oharbor.goharbor.io_assessmentreports.yaml | 41 +++++----- ...harbor.goharbor.io_inspectionpolicies.yaml | 36 ++++----- src/go.mod | 2 +- src/go.sum | 4 +- src/pkg/data/consumers/governor/exporter.go | 28 ++++--- .../data/consumers/governor/exporter_test.go | 64 ++++++++++++++++ .../data/consumers/governor/mock_client.go | 74 +++++++++++++++++++ src/pkg/inspection/controller.go | 39 ++++++++-- src/pkg/inspection/scanner.go | 24 +++--- 11 files changed, 246 insertions(+), 70 deletions(-) create mode 100644 src/pkg/data/consumers/governor/exporter_test.go create mode 100644 src/pkg/data/consumers/governor/mock_client.go diff --git a/src/api/v1alpha1/inspectionpolicy_types.go b/src/api/v1alpha1/inspectionpolicy_types.go index 6ed909cb..7a902f2b 100644 --- a/src/api/v1alpha1/inspectionpolicy_types.go +++ b/src/api/v1alpha1/inspectionpolicy_types.go @@ -115,6 +115,7 @@ type Assessment struct { // +kubebuilder:validation:Optional OpenSearchCert string `json:"openSearchCert"` // Indicate whether to config of governor + // +kubebuilder:validation:Optional Governor Governor `json:"governor"` } diff --git a/src/api/v1alpha1/workload.go b/src/api/v1alpha1/workload.go index 464fbe0b..e4915e0d 100644 --- a/src/api/v1alpha1/workload.go +++ b/src/api/v1alpha1/workload.go @@ -3,7 +3,6 @@ package v1alpha1 import ( - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" ) @@ -41,7 +40,7 @@ type Workload struct { // For pure Pod, no object reference. corev1.ObjectReference `json:"metadata"` // Replicas of this workload. - Replicas appsv1.ReplicaSet `json:"replicas"` + Replicas int32 `json:"replicas"` // Pods of this workload. Pods []*Pod `json:"pods"` } diff --git a/src/config/crd/bases/goharbor.goharbor.io_assessmentreports.yaml b/src/config/crd/bases/goharbor.goharbor.io_assessmentreports.yaml index 98cb163b..c7eed68a 100644 --- a/src/config/crd/bases/goharbor.goharbor.io_assessmentreports.yaml +++ b/src/config/crd/bases/goharbor.goharbor.io_assessmentreports.yaml @@ -128,23 +128,6 @@ spec: elasticSearchUser: description: ElasticSearch username for the client type: string - governor: - description: Indicate whether to config of governor - properties: - enabled: - default: false - description: Indicate whether to send the reports to governor - type: boolean - clusterId: - description: Unique identifier of the cluster - type: string - url: - description: Api url to send telemetry data - type: string - apiToken: - description: Api token for user authentication - type: string - type: object format: default: YAML description: Format of the assessment report data. @@ -157,6 +140,25 @@ spec: description: Generate indicates whether generate the assessment report or not. type: boolean + governor: + description: Indicate whether to config of governor + properties: + apiToken: + description: Api token for user authentication + type: string + clusterId: + description: Unique identifier of the cluster + type: string + enabled: + default: false + description: Indicate whether to send the reports to governor + type: boolean + url: + description: Api url to send telemetry data + type: string + required: + - enabled + type: object liveTime: default: 86400 description: Live time of the generated report. Unit is second. @@ -693,9 +695,14 @@ spec: - metadata type: object type: array + replicas: + description: Replicas of this workload. + format: int32 + type: integer required: - metadata - pods + - replicas type: object required: - passed diff --git a/src/config/crd/bases/goharbor.goharbor.io_inspectionpolicies.yaml b/src/config/crd/bases/goharbor.goharbor.io_inspectionpolicies.yaml index 8e824cda..760aab35 100644 --- a/src/config/crd/bases/goharbor.goharbor.io_inspectionpolicies.yaml +++ b/src/config/crd/bases/goharbor.goharbor.io_inspectionpolicies.yaml @@ -152,6 +152,25 @@ spec: description: Generate indicates whether generate the assessment report or not. type: boolean + governor: + description: Indicate whether to config of governor + properties: + apiToken: + description: Api token for user authentication + type: string + clusterId: + description: Unique identifier of the cluster + type: string + enabled: + default: false + description: Indicate whether to send the reports to governor + type: boolean + url: + description: Api url to send telemetry data + type: string + required: + - enabled + type: object liveTime: default: 86400 description: Live time of the generated report. Unit is second. @@ -179,23 +198,6 @@ spec: openSearchUser: description: ElasticSearch username for the client type: string - governor: - description: Indicate whether to config of governor - properties: - enabled: - default: false - description: Indicate whether to send the reports to governor - type: boolean - clusterId: - description: Unique identifier of the cluster - type: string - url: - description: Api url to send telemetry data - type: string - apiToken: - description: Api token for user authentication - type: string - type: object required: - elasticSearchEnabled - format diff --git a/src/go.mod b/src/go.mod index 0e890c19..30564bb1 100644 --- a/src/go.mod +++ b/src/go.mod @@ -21,7 +21,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/spf13/viper v1.14.0 github.com/stretchr/testify v1.8.1 - gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client v0.0.0-20230213153333-158da97aac53 + gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client v0.0.0-20230217132810-674c08a7361b k8s.io/api v0.25.4 k8s.io/apimachinery v0.25.4 k8s.io/apiserver v0.25.4 diff --git a/src/go.sum b/src/go.sum index 83cd7c57..4607634d 100644 --- a/src/go.sum +++ b/src/go.sum @@ -638,8 +638,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client v0.0.0-20230213153333-158da97aac53 h1:a1Xa7i9FOnW72qQBC7pPTLrcmUcu4/dS0fpxqAGxQDs= -gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client v0.0.0-20230213153333-158da97aac53/go.mod h1:Z3g5cIHSpGMDg7W8dXek1xsxZn4ZsOqNCu5vBXY0DXM= +gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client v0.0.0-20230217132810-674c08a7361b h1:jW9yVVi1meezoQI1h12LVDKu6QgIbZTOtKEBNEwD08A= +gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client v0.0.0-20230217132810-674c08a7361b/go.mod h1:Z3g5cIHSpGMDg7W8dXek1xsxZn4ZsOqNCu5vBXY0DXM= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= diff --git a/src/pkg/data/consumers/governor/exporter.go b/src/pkg/data/consumers/governor/exporter.go index ad6c626a..b2c60846 100644 --- a/src/pkg/data/consumers/governor/exporter.go +++ b/src/pkg/data/consumers/governor/exporter.go @@ -3,7 +3,9 @@ package consumers import ( "context" "errors" + "fmt" api "github.com/vmware-tanzu/cloud-native-security-inspector/src/api/v1alpha1" + "github.com/vmware-tanzu/cloud-native-security-inspector/src/lib/log" openapi "gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client" "net/http" ) @@ -11,41 +13,43 @@ import ( type GovernorExporter struct { Report *api.AssessmentReport ClusterID string - ApiURL string ApiToken string + ApiClient *openapi.APIClient } // SendReportToGovernor is used to send report to governor url http end point. func (g GovernorExporter) SendReportToGovernor() error { - // Get api request model from assessment report. - kubernetesCluster := getAPIRequest(*g.Report) + // Get governor api request model from assessment report. + kubernetesCluster := getGovernorAPIPayload(*g.Report) - // Create api client to governor api. - apiClient := openapi.NewAPIClient(openapi.NewConfiguration()) - apiSaveClusterRequest := apiClient.ClustersApi.UpdateTelemetry(context.Background(), g.ClusterID) + apiSaveClusterRequest := g.ApiClient.ClustersApi.UpdateTelemetry(context.Background(), g.ClusterID).KubernetesTelemetryRequest(kubernetesCluster) // Call api cluster to send telemetry data and get response. - response, err := apiSaveClusterRequest.KubernetesTelemetryRequest(kubernetesCluster).Execute() + response, err := g.ApiClient.ClustersApi.UpdateTelemetryExecute(apiSaveClusterRequest) if err != nil { + log.Errorf("Governor api response error: %v", err) return err } if response.StatusCode != http.StatusOK { - return errors.New(response.Status) + log.Errorf("Governor api response status: %v", response.StatusCode) + return errors.New(fmt.Sprintf("Governor api response status: %s", response.Status)) } return nil } -// getAPIRequest is used to map assessment report to client model. -func getAPIRequest(doc api.AssessmentReport) openapi.KubernetesTelemetryRequest { +// getGovernorAPIPayload is used to map assessment report to client model. +func getGovernorAPIPayload(doc api.AssessmentReport) openapi.KubernetesTelemetryRequest { kubernetesCluster := openapi.NewKubernetesTelemetryRequestWithDefaults() for _, nsa := range doc.Spec.NamespaceAssessments { for _, workloadAssessment := range nsa.WorkloadAssessments { kubernetesWorkloads := openapi.NewKubernetesWorkloadWithDefaults() kubernetesWorkloads.Name = workloadAssessment.Workload.Name kubernetesWorkloads.Kind = workloadAssessment.Workload.Kind - kubernetesWorkloads.Namespace = nsa.Namespace.Name - kubernetesWorkloads.Replicas = *workloadAssessment.Workload.Replicas.Spec.Replicas + if nsa.Namespace.Name != "" { + kubernetesWorkloads.Namespace = nsa.Namespace.Name + } + kubernetesWorkloads.Replicas = workloadAssessment.Workload.Replicas for _, pod := range workloadAssessment.Workload.Pods { containerData := openapi.NewContainerWithDefaults() for _, container := range pod.Containers { diff --git a/src/pkg/data/consumers/governor/exporter_test.go b/src/pkg/data/consumers/governor/exporter_test.go new file mode 100644 index 00000000..c6741387 --- /dev/null +++ b/src/pkg/data/consumers/governor/exporter_test.go @@ -0,0 +1,64 @@ +package consumers + +import ( + api "github.com/vmware-tanzu/cloud-native-security-inspector/src/api/v1alpha1" + openapi "gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client" + v1 "k8s.io/api/core/v1" + "os" + "testing" +) + +var ( + mockClient *openapi.APIClient + clusterID = "testingId" + apiToken = "apiToken" + namespace = "testingNamespace" + name = "name" + image = "image" + imageID = "imageId" + replicaCount = 2 +) + +const ( + testHost = "clusterapi.swagger.io:80" + testScheme = "http" +) + +func TestMain(m *testing.M) { + cfg := openapi.NewConfiguration() + cfg.AddDefaultHeader("testheader", "testvalue") + cfg.Host = testHost + cfg.Scheme = testScheme + mockClient = openapi.NewAPIClient(cfg) + retCode := m.Run() + os.Exit(retCode) +} + +func TestSendReportToGovernorSuccess(t *testing.T) { + actualApi := mockClient.ClustersApi + mockApi := NewMockClustersApi() + mockClient.ClustersApi = mockApi + + g := GovernorExporter{ + Report: &api.AssessmentReport{ + Spec: api.AssessmentReportSpec{NamespaceAssessments: []*api.NamespaceAssessment{{Namespace: v1.LocalObjectReference{ + Name: namespace, + }, + WorkloadAssessments: []*api.WorkloadAssessment{{Workload: api.Workload{Replicas: int32(replicaCount), + Pods: []*api.Pod{{Containers: []*api.Container{{ + Name: name, + Image: image, + ImageID: imageID, + }}}}}}}}}}}, + ClusterID: clusterID, + ApiToken: apiToken, + ApiClient: mockClient, + } + + errFromSendReportToGovernor := g.SendReportToGovernor() + if errFromSendReportToGovernor != nil { + t.Fatalf("Error while updating telemetry data of workloads in cluster: %v", errFromSendReportToGovernor) + } + mockClient.ClustersApi = actualApi + +} diff --git a/src/pkg/data/consumers/governor/mock_client.go b/src/pkg/data/consumers/governor/mock_client.go new file mode 100644 index 00000000..b3570d8e --- /dev/null +++ b/src/pkg/data/consumers/governor/mock_client.go @@ -0,0 +1,74 @@ +package consumers + +import ( + "context" + openapi "gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client" + "net/http" +) + +// MockClustersApi is a mock of the ClustersApi interface +type MockClustersApi struct { +} + +// NewMockClustersApi creates a new mock instance +func NewMockClustersApi() *MockClustersApi { + return &MockClustersApi{} +} + +func (m *MockClustersApi) FetchAgentConfig(ctx context.Context, clusterId string) openapi.ApiFetchAgentConfigRequest { + //TODO implement me + panic("implement me") +} + +func (m *MockClustersApi) FetchAgentConfigExecute(r openapi.ApiFetchAgentConfigRequest) (string, *http.Response, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockClustersApi) GetCluster(ctx context.Context, clusterId string) openapi.ApiGetClusterRequest { + //TODO implement me + panic("implement me") +} + +func (m *MockClustersApi) GetClusterExecute(r openapi.ApiGetClusterRequest) (*openapi.KubernetesClusterDetailedResponse, *http.Response, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockClustersApi) GetClusters(ctx context.Context) openapi.ApiGetClustersRequest { + //TODO implement me + panic("implement me") +} + +func (m *MockClustersApi) GetClustersExecute(r openapi.ApiGetClustersRequest) ([]openapi.KubernetesClusterResponse, *http.Response, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockClustersApi) RegisterCluster(ctx context.Context) openapi.ApiRegisterClusterRequest { + //TODO implement me + panic("implement me") +} + +func (m *MockClustersApi) RegisterClusterExecute(r openapi.ApiRegisterClusterRequest) (*http.Response, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockClustersApi) UnregisterCluster(ctx context.Context, clusterId string) openapi.ApiUnregisterClusterRequest { + //TODO implement me + panic("implement me") +} + +func (m *MockClustersApi) UnregisterClusterExecute(r openapi.ApiUnregisterClusterRequest) (*http.Response, error) { + //TODO implement me + panic("implement me") +} + +func (m *MockClustersApi) UpdateTelemetry(ctx context.Context, clusterId string) openapi.ApiUpdateTelemetryRequest { + return openapi.ApiUpdateTelemetryRequest{ApiService: m} +} + +func (m *MockClustersApi) UpdateTelemetryExecute(r openapi.ApiUpdateTelemetryRequest) (*http.Response, error) { + return &http.Response{StatusCode: 200}, nil +} diff --git a/src/pkg/inspection/controller.go b/src/pkg/inspection/controller.go index fedb7f49..3733c6d7 100644 --- a/src/pkg/inspection/controller.go +++ b/src/pkg/inspection/controller.go @@ -9,6 +9,7 @@ import ( es "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/es" governor "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/governor" osearch "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/opensearch" + openapi "gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" "time" @@ -335,14 +336,15 @@ func (c *controller) Run(ctx context.Context, policy *v1alpha1.InspectionPolicy) // Read config from InspectionPolicy, send assessment reports to Governor api if governor enabled. if policy.Spec.Inspection.Assessment.Governor.Enabled { governorConfig := policy.Spec.Inspection.Assessment.Governor - exporter := governor.GovernorExporter{ - Report: report, - ClusterID: governorConfig.ClusterID, - ApiURL: governorConfig.URL, - ApiToken: governorConfig.APIToken, + + if governorConfig.ClusterID == "" || governorConfig.URL == "" || governorConfig.APIToken == "" { + log.Error("Either ClusterID or URL or APIToken is empty") + return errors.New("Either ClusterID or URL or APIToken is empty") } - if err := exporter.SendReportToGovernor(); err != nil { - return err + + if exporterErr := exportReportToGovernor(report, policy); exporterErr != nil { + log.Errorf("Error from exporter: %v", exporterErr) + return exporterErr } } @@ -370,6 +372,29 @@ func (c *controller) Run(ctx context.Context, policy *v1alpha1.InspectionPolicy) return nil } +func exportReportToGovernor(report *v1alpha1.AssessmentReport, policy *v1alpha1.InspectionPolicy) error { + governorConfig := policy.Spec.Inspection.Assessment.Governor + + // Create api client to governor api. + config := openapi.NewConfiguration() + config.Host = governorConfig.URL + apiClient := openapi.NewAPIClient(config) + + exporter := governor.GovernorExporter{ + Report: report, + ClusterID: governorConfig.ClusterID, + ApiToken: governorConfig.APIToken, + ApiClient: apiClient, + } + + if apiResponseErr := exporter.SendReportToGovernor(); apiResponseErr != nil { + log.Error("Err response from governor exporter", apiResponseErr) + return apiResponseErr + } + + return nil +} + func exportReportToOpenSearch(report *v1alpha1.AssessmentReport, policy *v1alpha1.InspectionPolicy) error { client := osearch.NewClient([]byte{}, policy.Spec.Inspection.Assessment.OpenSearchAddr, diff --git a/src/pkg/inspection/scanner.go b/src/pkg/inspection/scanner.go index 04270cad..78f314e1 100644 --- a/src/pkg/inspection/scanner.go +++ b/src/pkg/inspection/scanner.go @@ -130,7 +130,7 @@ func (ds *DefaultScanner) processPod(ctx context.Context, pod corev1.Pod, indexe return errors.Wrap(err, "get pod reference") } - owner, replicaSet, err := ds.getOwner(ctx, pod) + owner, replicaSetCount, err := ds.getOwner(ctx, pod) if err != nil { return errors.Wrap(err, "get pod owner") } @@ -142,7 +142,7 @@ func (ds *DefaultScanner) processPod(ctx context.Context, pod corev1.Pod, indexe if _, ok := indexer[uid]; !ok { indexer[uid] = &v1alpha1.Workload{} - indexer[uid].Replicas = *replicaSet + indexer[uid].Replicas = replicaSetCount } // Set owner reference. @@ -161,30 +161,30 @@ func (ds *DefaultScanner) processPod(ctx context.Context, pod corev1.Pod, indexe return nil } -func (ds *DefaultScanner) getOwner(ctx context.Context, pod corev1.Pod) (*corev1.ObjectReference, *appsv1.ReplicaSet, error) { +func (ds *DefaultScanner) getOwner(ctx context.Context, pod corev1.Pod) (*corev1.ObjectReference, int32, error) { refs := pod.GetOwnerReferences() if ref, yes := hasReplicaSet(refs); yes { - rsOwnerRef, replicaSetData, err := ds.getReplicaSetOwnerRef(ctx, types.NamespacedName{ + rsOwnerRef, replicaSetCount, err := ds.getReplicaSetOwnerRef(ctx, types.NamespacedName{ Namespace: pod.Namespace, Name: ref.Name, }) if err != nil { - return nil, nil, errors.Wrap(err, "get replicaSet owner ref") + return nil, 0, errors.Wrap(err, "get replicaSet owner ref") } // Deployment? if rsOwnerRef != nil { - return rsOwnerRef, replicaSetData, nil + return rsOwnerRef, replicaSetCount, nil } } - return extractOwnerRef(pod.Namespace, refs), nil, nil + return extractOwnerRef(pod.Namespace, refs), 0, nil } -func (ds *DefaultScanner) getReplicaSetOwnerRef(ctx context.Context, namespacedName types.NamespacedName) (*corev1.ObjectReference, *appsv1.ReplicaSet, error) { +func (ds *DefaultScanner) getReplicaSetOwnerRef(ctx context.Context, namespacedName types.NamespacedName) (*corev1.ObjectReference, int32, error) { var rps appsv1.ReplicaSet if err := ds.Get(ctx, namespacedName, &rps); err != nil { - return nil, nil, err + return nil, 0, err } ownerRef := rps.GetOwnerReferences() @@ -271,14 +271,14 @@ func extractOwnerRef(ns string, ownerRefs []metav1.OwnerReference) *corev1.Objec return nil } -func extractReplicaSet(ownerRefs []metav1.OwnerReference, rps appsv1.ReplicaSet) *appsv1.ReplicaSet { +func extractReplicaSet(ownerRefs []metav1.OwnerReference, rps appsv1.ReplicaSet) int32 { for _, or := range ownerRefs { if isWorkload(or.Kind) { - return &appsv1.ReplicaSet{Spec: appsv1.ReplicaSetSpec{Replicas: rps.Spec.Replicas}} + return *rps.Spec.Replicas } } - return nil + return 0 } func hasReplicaSet(ownerRefs []metav1.OwnerReference) (metav1.OwnerReference, bool) { From b6e8a58d52c9ad32fceb65a9935102dd4225985e Mon Sep 17 00:00:00 2001 From: Nootan Singh Date: Thu, 23 Feb 2023 09:47:42 +0530 Subject: [PATCH 3/6] Added generated go library files for catalog governor api call Signed-off-by: Nootan Singh --- src/go.mod | 1 - src/pkg/data/consumers/governor/exporter.go | 2 +- .../data/consumers/governor/exporter_test.go | 2 +- .../governor/go-client/api_clusters.go | 880 ++++++++++++++++++ .../consumers/governor/go-client/client.go | 564 +++++++++++ .../governor/go-client/configuration.go | 244 +++++ .../go-client/model_constraints_violation.go | 166 ++++ .../model_constraints_violations_error.go | 309 ++++++ .../governor/go-client/model_container.go | 197 ++++ .../governor/go-client/model_error.go | 280 ++++++ .../model_kubernetes_agent_config_request.go | 165 ++++ ...el_kubernetes_cluster_detailed_response.go | 356 +++++++ ...rnetes_cluster_detailed_response_all_of.go | 141 +++ .../model_kubernetes_cluster_response.go | 319 +++++++ .../model_kubernetes_telemetry_request.go | 134 +++ .../model_kubernetes_telemetry_response.go | 211 +++++ .../go-client/model_kubernetes_workload.go | 258 +++++ .../governor/go-client/model_match_label.go | 166 ++++ ...del_register_kubernetes_cluster_request.go | 135 +++ .../data/consumers/governor/mock_client.go | 2 +- src/pkg/inspection/controller.go | 2 +- 21 files changed, 4529 insertions(+), 5 deletions(-) create mode 100644 src/pkg/data/consumers/governor/go-client/api_clusters.go create mode 100644 src/pkg/data/consumers/governor/go-client/client.go create mode 100644 src/pkg/data/consumers/governor/go-client/configuration.go create mode 100644 src/pkg/data/consumers/governor/go-client/model_constraints_violation.go create mode 100644 src/pkg/data/consumers/governor/go-client/model_constraints_violations_error.go create mode 100644 src/pkg/data/consumers/governor/go-client/model_container.go create mode 100644 src/pkg/data/consumers/governor/go-client/model_error.go create mode 100644 src/pkg/data/consumers/governor/go-client/model_kubernetes_agent_config_request.go create mode 100644 src/pkg/data/consumers/governor/go-client/model_kubernetes_cluster_detailed_response.go create mode 100644 src/pkg/data/consumers/governor/go-client/model_kubernetes_cluster_detailed_response_all_of.go create mode 100644 src/pkg/data/consumers/governor/go-client/model_kubernetes_cluster_response.go create mode 100644 src/pkg/data/consumers/governor/go-client/model_kubernetes_telemetry_request.go create mode 100644 src/pkg/data/consumers/governor/go-client/model_kubernetes_telemetry_response.go create mode 100644 src/pkg/data/consumers/governor/go-client/model_kubernetes_workload.go create mode 100644 src/pkg/data/consumers/governor/go-client/model_match_label.go create mode 100644 src/pkg/data/consumers/governor/go-client/model_register_kubernetes_cluster_request.go diff --git a/src/go.mod b/src/go.mod index 30564bb1..17e9c122 100644 --- a/src/go.mod +++ b/src/go.mod @@ -21,7 +21,6 @@ require ( github.com/pkg/errors v0.9.1 github.com/spf13/viper v1.14.0 github.com/stretchr/testify v1.8.1 - gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client v0.0.0-20230217132810-674c08a7361b k8s.io/api v0.25.4 k8s.io/apimachinery v0.25.4 k8s.io/apiserver v0.25.4 diff --git a/src/pkg/data/consumers/governor/exporter.go b/src/pkg/data/consumers/governor/exporter.go index b2c60846..5a4b700c 100644 --- a/src/pkg/data/consumers/governor/exporter.go +++ b/src/pkg/data/consumers/governor/exporter.go @@ -6,7 +6,7 @@ import ( "fmt" api "github.com/vmware-tanzu/cloud-native-security-inspector/src/api/v1alpha1" "github.com/vmware-tanzu/cloud-native-security-inspector/src/lib/log" - openapi "gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client" + openapi "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/governor/go-client" "net/http" ) diff --git a/src/pkg/data/consumers/governor/exporter_test.go b/src/pkg/data/consumers/governor/exporter_test.go index c6741387..2825b58d 100644 --- a/src/pkg/data/consumers/governor/exporter_test.go +++ b/src/pkg/data/consumers/governor/exporter_test.go @@ -2,7 +2,7 @@ package consumers import ( api "github.com/vmware-tanzu/cloud-native-security-inspector/src/api/v1alpha1" - openapi "gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client" + openapi "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/governor/go-client" v1 "k8s.io/api/core/v1" "os" "testing" diff --git a/src/pkg/data/consumers/governor/go-client/api_clusters.go b/src/pkg/data/consumers/governor/go-client/api_clusters.go new file mode 100644 index 00000000..4f1e3288 --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/api_clusters.go @@ -0,0 +1,880 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "bytes" + "context" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + + +type ClustersApi interface { + + /* + FetchAgentConfig fetch cluster agent configuration manifest + + fetch the agent configuration manifest data for a cluster in an organization. OrgId is fetched from CSP token. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param clusterId A string as the identifier of a cluster + @return ApiFetchAgentConfigRequest + */ + FetchAgentConfig(ctx context.Context, clusterId string) ApiFetchAgentConfigRequest + + // FetchAgentConfigExecute executes the request + // @return string + FetchAgentConfigExecute(r ApiFetchAgentConfigRequest) (string, *http.Response, error) + + /* + GetCluster Get cluster and workload telemetry + + Returns information about a cluster and its workloads deployed for an organization. OrgId is fetched from CSP token. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param clusterId A string as the identifier of a cluster + @return ApiGetClusterRequest + */ + GetCluster(ctx context.Context, clusterId string) ApiGetClusterRequest + + // GetClusterExecute executes the request + // @return KubernetesClusterDetailedResponse + GetClusterExecute(r ApiGetClusterRequest) (*KubernetesClusterDetailedResponse, *http.Response, error) + + /* + GetClusters Get information about all clusters and their workloads + + Returns all clusters and their workloads for an organization. OrgId is fetched from CSP token. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiGetClustersRequest + */ + GetClusters(ctx context.Context) ApiGetClustersRequest + + // GetClustersExecute executes the request + // @return []KubernetesClusterResponse + GetClustersExecute(r ApiGetClustersRequest) ([]KubernetesClusterResponse, *http.Response, error) + + /* + RegisterCluster Register a cluster + + Registers a new cluster to start receiving telemetry. OrgId is fetched from CSP token. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiRegisterClusterRequest + */ + RegisterCluster(ctx context.Context) ApiRegisterClusterRequest + + // RegisterClusterExecute executes the request + RegisterClusterExecute(r ApiRegisterClusterRequest) (*http.Response, error) + + /* + UnregisterCluster Unregister a cluster + + Unregisters a cluster. OrgId is fetched from CSP token. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param clusterId A string as the identifier of a cluster + @return ApiUnregisterClusterRequest + */ + UnregisterCluster(ctx context.Context, clusterId string) ApiUnregisterClusterRequest + + // UnregisterClusterExecute executes the request + UnregisterClusterExecute(r ApiUnregisterClusterRequest) (*http.Response, error) + + /* + UpdateTelemetry Save/update cluster workload telemetry + + Saves/updates information about the running workloads in a cluster for an organization. OrgId is fetched from CSP token. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param clusterId A string as the identifier of a cluster + @return ApiUpdateTelemetryRequest + */ + UpdateTelemetry(ctx context.Context, clusterId string) ApiUpdateTelemetryRequest + + // UpdateTelemetryExecute executes the request + UpdateTelemetryExecute(r ApiUpdateTelemetryRequest) (*http.Response, error) +} + +// ClustersApiService ClustersApi service +type ClustersApiService service + +type ApiFetchAgentConfigRequest struct { + ctx context.Context + ApiService ClustersApi + clusterId string + kubernetesAgentConfigRequest *KubernetesAgentConfigRequest +} + +// Details of the agent properties for fetching agent configuration +func (r ApiFetchAgentConfigRequest) KubernetesAgentConfigRequest(kubernetesAgentConfigRequest KubernetesAgentConfigRequest) ApiFetchAgentConfigRequest { + r.kubernetesAgentConfigRequest = &kubernetesAgentConfigRequest + return r +} + +func (r ApiFetchAgentConfigRequest) Execute() (string, *http.Response, error) { + return r.ApiService.FetchAgentConfigExecute(r) +} + +/* +FetchAgentConfig fetch cluster agent configuration manifest + +fetch the agent configuration manifest data for a cluster in an organization. OrgId is fetched from CSP token. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param clusterId A string as the identifier of a cluster + @return ApiFetchAgentConfigRequest +*/ +func (a *ClustersApiService) FetchAgentConfig(ctx context.Context, clusterId string) ApiFetchAgentConfigRequest { + return ApiFetchAgentConfigRequest{ + ApiService: a, + ctx: ctx, + clusterId: clusterId, + } +} + +// Execute executes the request +// @return string +func (a *ClustersApiService) FetchAgentConfigExecute(r ApiFetchAgentConfigRequest) (string, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue string + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ClustersApiService.FetchAgentConfig") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/clusters/{cluster_id}/agent-config" + localVarPath = strings.Replace(localVarPath, "{"+"cluster_id"+"}", url.PathEscape(parameterToString(r.clusterId, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.kubernetesAgentConfigRequest == nil { + return localVarReturnValue, nil, reportError("kubernetesAgentConfigRequest is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json", "application/problem+json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = r.kubernetesAgentConfigRequest + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = ioutil.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v ConstraintsViolationsError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type ApiGetClusterRequest struct { + ctx context.Context + ApiService ClustersApi + clusterId string +} + +func (r ApiGetClusterRequest) Execute() (*KubernetesClusterDetailedResponse, *http.Response, error) { + return r.ApiService.GetClusterExecute(r) +} + +/* +GetCluster Get cluster and workload telemetry + +Returns information about a cluster and its workloads deployed for an organization. OrgId is fetched from CSP token. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param clusterId A string as the identifier of a cluster + @return ApiGetClusterRequest +*/ +func (a *ClustersApiService) GetCluster(ctx context.Context, clusterId string) ApiGetClusterRequest { + return ApiGetClusterRequest{ + ApiService: a, + ctx: ctx, + clusterId: clusterId, + } +} + +// Execute executes the request +// @return KubernetesClusterDetailedResponse +func (a *ClustersApiService) GetClusterExecute(r ApiGetClusterRequest) (*KubernetesClusterDetailedResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *KubernetesClusterDetailedResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ClustersApiService.GetCluster") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/clusters/{cluster_id}" + localVarPath = strings.Replace(localVarPath, "{"+"cluster_id"+"}", url.PathEscape(parameterToString(r.clusterId, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json", "application/problem+json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = ioutil.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v ConstraintsViolationsError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type ApiGetClustersRequest struct { + ctx context.Context + ApiService ClustersApi +} + +func (r ApiGetClustersRequest) Execute() ([]KubernetesClusterResponse, *http.Response, error) { + return r.ApiService.GetClustersExecute(r) +} + +/* +GetClusters Get information about all clusters and their workloads + +Returns all clusters and their workloads for an organization. OrgId is fetched from CSP token. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiGetClustersRequest +*/ +func (a *ClustersApiService) GetClusters(ctx context.Context) ApiGetClustersRequest { + return ApiGetClustersRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return []KubernetesClusterResponse +func (a *ClustersApiService) GetClustersExecute(r ApiGetClustersRequest) ([]KubernetesClusterResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue []KubernetesClusterResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ClustersApiService.GetClusters") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/clusters" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json", "application/problem+json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = ioutil.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v ConstraintsViolationsError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type ApiRegisterClusterRequest struct { + ctx context.Context + ApiService ClustersApi + registerKubernetesClusterRequest *RegisterKubernetesClusterRequest +} + +// Details of the cluster to be registered +func (r ApiRegisterClusterRequest) RegisterKubernetesClusterRequest(registerKubernetesClusterRequest RegisterKubernetesClusterRequest) ApiRegisterClusterRequest { + r.registerKubernetesClusterRequest = ®isterKubernetesClusterRequest + return r +} + +func (r ApiRegisterClusterRequest) Execute() (*http.Response, error) { + return r.ApiService.RegisterClusterExecute(r) +} + +/* +RegisterCluster Register a cluster + +Registers a new cluster to start receiving telemetry. OrgId is fetched from CSP token. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiRegisterClusterRequest +*/ +func (a *ClustersApiService) RegisterCluster(ctx context.Context) ApiRegisterClusterRequest { + return ApiRegisterClusterRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +func (a *ClustersApiService) RegisterClusterExecute(r ApiRegisterClusterRequest) (*http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ClustersApiService.RegisterCluster") + if err != nil { + return nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/clusters" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.registerKubernetesClusterRequest == nil { + return nil, reportError("registerKubernetesClusterRequest is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/problem+json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = r.registerKubernetesClusterRequest + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarHTTPResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = ioutil.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v ConstraintsViolationsError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 409 { + var v ConstraintsViolationsError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarHTTPResponse, newErr + } + + return localVarHTTPResponse, nil +} + +type ApiUnregisterClusterRequest struct { + ctx context.Context + ApiService ClustersApi + clusterId string +} + +func (r ApiUnregisterClusterRequest) Execute() (*http.Response, error) { + return r.ApiService.UnregisterClusterExecute(r) +} + +/* +UnregisterCluster Unregister a cluster + +Unregisters a cluster. OrgId is fetched from CSP token. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param clusterId A string as the identifier of a cluster + @return ApiUnregisterClusterRequest +*/ +func (a *ClustersApiService) UnregisterCluster(ctx context.Context, clusterId string) ApiUnregisterClusterRequest { + return ApiUnregisterClusterRequest{ + ApiService: a, + ctx: ctx, + clusterId: clusterId, + } +} + +// Execute executes the request +func (a *ClustersApiService) UnregisterClusterExecute(r ApiUnregisterClusterRequest) (*http.Response, error) { + var ( + localVarHTTPMethod = http.MethodDelete + localVarPostBody interface{} + formFiles []formFile + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ClustersApiService.UnregisterCluster") + if err != nil { + return nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/clusters/{cluster_id}" + localVarPath = strings.Replace(localVarPath, "{"+"cluster_id"+"}", url.PathEscape(parameterToString(r.clusterId, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/problem+json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarHTTPResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = ioutil.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarHTTPResponse, newErr + } + + return localVarHTTPResponse, nil +} + +type ApiUpdateTelemetryRequest struct { + ctx context.Context + ApiService ClustersApi + clusterId string + kubernetesTelemetryRequest *KubernetesTelemetryRequest +} + +// Workload telemetry of the cluster to be updated +func (r ApiUpdateTelemetryRequest) KubernetesTelemetryRequest(kubernetesTelemetryRequest KubernetesTelemetryRequest) ApiUpdateTelemetryRequest { + r.kubernetesTelemetryRequest = &kubernetesTelemetryRequest + return r +} + +func (r ApiUpdateTelemetryRequest) Execute() (*http.Response, error) { + return r.ApiService.UpdateTelemetryExecute(r) +} + +/* +UpdateTelemetry Save/update cluster workload telemetry + +Saves/updates information about the running workloads in a cluster for an organization. OrgId is fetched from CSP token. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param clusterId A string as the identifier of a cluster + @return ApiUpdateTelemetryRequest +*/ +func (a *ClustersApiService) UpdateTelemetry(ctx context.Context, clusterId string) ApiUpdateTelemetryRequest { + return ApiUpdateTelemetryRequest{ + ApiService: a, + ctx: ctx, + clusterId: clusterId, + } +} + +// Execute executes the request +func (a *ClustersApiService) UpdateTelemetryExecute(r ApiUpdateTelemetryRequest) (*http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPut + localVarPostBody interface{} + formFiles []formFile + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ClustersApiService.UpdateTelemetry") + if err != nil { + return nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/clusters/{cluster_id}/telemetry" + localVarPath = strings.Replace(localVarPath, "{"+"cluster_id"+"}", url.PathEscape(parameterToString(r.clusterId, "")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.kubernetesTelemetryRequest == nil { + return nil, reportError("kubernetesTelemetryRequest is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/problem+json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = r.kubernetesTelemetryRequest + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarHTTPResponse, err + } + + localVarBody, err := ioutil.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = ioutil.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v ConstraintsViolationsError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 404 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + return localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v Error + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.model = v + } + return localVarHTTPResponse, newErr + } + + return localVarHTTPResponse, nil +} diff --git a/src/pkg/data/consumers/governor/go-client/client.go b/src/pkg/data/consumers/governor/go-client/client.go new file mode 100644 index 00000000..8a60e652 --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/client.go @@ -0,0 +1,564 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "bytes" + "context" + "encoding/json" + "encoding/xml" + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "mime/multipart" + "net/http" + "net/http/httputil" + "net/url" + "os" + "path/filepath" + "reflect" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" + + "golang.org/x/oauth2" +) + +var ( + jsonCheck = regexp.MustCompile(`(?i:(?:application|text)/(?:vnd\.[^;]+\+)?json)`) + xmlCheck = regexp.MustCompile(`(?i:(?:application|text)/xml)`) +) + +// APIClient manages communication with the Catalog Governor Service REST API API v0.1.0 +// In most cases there should be only one, shared, APIClient. +type APIClient struct { + cfg *Configuration + common service // Reuse a single struct instead of allocating one for each service on the heap. + + // API Services + + ClustersApi ClustersApi +} + +type service struct { + client *APIClient +} + +// NewAPIClient creates a new API client. Requires a userAgent string describing your application. +// optionally a custom http.Client to allow for advanced features such as caching. +func NewAPIClient(cfg *Configuration) *APIClient { + if cfg.HTTPClient == nil { + cfg.HTTPClient = http.DefaultClient + } + + c := &APIClient{} + c.cfg = cfg + c.common.client = c + + // API Services + c.ClustersApi = (*ClustersApiService)(&c.common) + + return c +} + +func atoi(in string) (int, error) { + return strconv.Atoi(in) +} + +// selectHeaderContentType select a content type from the available list. +func selectHeaderContentType(contentTypes []string) string { + if len(contentTypes) == 0 { + return "" + } + if contains(contentTypes, "application/json") { + return "application/json" + } + return contentTypes[0] // use the first content type specified in 'consumes' +} + +// selectHeaderAccept join all accept types and return +func selectHeaderAccept(accepts []string) string { + if len(accepts) == 0 { + return "" + } + + if contains(accepts, "application/json") { + return "application/json" + } + + return strings.Join(accepts, ",") +} + +// contains is a case insensitive match, finding needle in a haystack +func contains(haystack []string, needle string) bool { + for _, a := range haystack { + if strings.EqualFold(a, needle) { + return true + } + } + return false +} + +// Verify optional parameters are of the correct type. +func typeCheckParameter(obj interface{}, expected string, name string) error { + // Make sure there is an object. + if obj == nil { + return nil + } + + // Check the type is as expected. + if reflect.TypeOf(obj).String() != expected { + return fmt.Errorf("Expected %s to be of type %s but received %s.", name, expected, reflect.TypeOf(obj).String()) + } + return nil +} + +// parameterToString convert interface{} parameters to string, using a delimiter if format is provided. +func parameterToString(obj interface{}, collectionFormat string) string { + var delimiter string + + switch collectionFormat { + case "pipes": + delimiter = "|" + case "ssv": + delimiter = " " + case "tsv": + delimiter = "\t" + case "csv": + delimiter = "," + } + + if reflect.TypeOf(obj).Kind() == reflect.Slice { + return strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]") + } else if t, ok := obj.(time.Time); ok { + return t.Format(time.RFC3339) + } + + return fmt.Sprintf("%v", obj) +} + +// helper for converting interface{} parameters to json strings +func parameterToJson(obj interface{}) (string, error) { + jsonBuf, err := json.Marshal(obj) + if err != nil { + return "", err + } + return string(jsonBuf), err +} + +// callAPI do the request. +func (c *APIClient) callAPI(request *http.Request) (*http.Response, error) { + if c.cfg.Debug { + dump, err := httputil.DumpRequestOut(request, true) + if err != nil { + return nil, err + } + log.Printf("\n%s\n", string(dump)) + } + + resp, err := c.cfg.HTTPClient.Do(request) + if err != nil { + return resp, err + } + + if c.cfg.Debug { + dump, err := httputil.DumpResponse(resp, true) + if err != nil { + return resp, err + } + log.Printf("\n%s\n", string(dump)) + } + return resp, err +} + +// Allow modification of underlying config for alternate implementations and testing +// Caution: modifying the configuration while live can cause data races and potentially unwanted behavior +func (c *APIClient) GetConfig() *Configuration { + return c.cfg +} + +type formFile struct { + fileBytes []byte + fileName string + formFileName string +} + +// prepareRequest build the request +func (c *APIClient) prepareRequest( + ctx context.Context, + path string, method string, + postBody interface{}, + headerParams map[string]string, + queryParams url.Values, + formParams url.Values, + formFiles []formFile) (localVarRequest *http.Request, err error) { + + var body *bytes.Buffer + + // Detect postBody type and post. + if postBody != nil { + contentType := headerParams["Content-Type"] + if contentType == "" { + contentType = detectContentType(postBody) + headerParams["Content-Type"] = contentType + } + + body, err = setBody(postBody, contentType) + if err != nil { + return nil, err + } + } + + // add form parameters and file if available. + if strings.HasPrefix(headerParams["Content-Type"], "multipart/form-data") && len(formParams) > 0 || (len(formFiles) > 0) { + if body != nil { + return nil, errors.New("Cannot specify postBody and multipart form at the same time.") + } + body = &bytes.Buffer{} + w := multipart.NewWriter(body) + + for k, v := range formParams { + for _, iv := range v { + if strings.HasPrefix(k, "@") { // file + err = addFile(w, k[1:], iv) + if err != nil { + return nil, err + } + } else { // form value + w.WriteField(k, iv) + } + } + } + for _, formFile := range formFiles { + if len(formFile.fileBytes) > 0 && formFile.fileName != "" { + w.Boundary() + part, err := w.CreateFormFile(formFile.formFileName, filepath.Base(formFile.fileName)) + if err != nil { + return nil, err + } + _, err = part.Write(formFile.fileBytes) + if err != nil { + return nil, err + } + } + } + + // Set the Boundary in the Content-Type + headerParams["Content-Type"] = w.FormDataContentType() + + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + w.Close() + } + + if strings.HasPrefix(headerParams["Content-Type"], "application/x-www-form-urlencoded") && len(formParams) > 0 { + if body != nil { + return nil, errors.New("Cannot specify postBody and x-www-form-urlencoded form at the same time.") + } + body = &bytes.Buffer{} + body.WriteString(formParams.Encode()) + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + } + + // Setup path and query parameters + url, err := url.Parse(path) + if err != nil { + return nil, err + } + + // Override request host, if applicable + if c.cfg.Host != "" { + url.Host = c.cfg.Host + } + + // Override request scheme, if applicable + if c.cfg.Scheme != "" { + url.Scheme = c.cfg.Scheme + } + + // Adding Query Param + query := url.Query() + for k, v := range queryParams { + for _, iv := range v { + query.Add(k, iv) + } + } + + // Encode the parameters. + url.RawQuery = query.Encode() + + // Generate a new request + if body != nil { + localVarRequest, err = http.NewRequest(method, url.String(), body) + } else { + localVarRequest, err = http.NewRequest(method, url.String(), nil) + } + if err != nil { + return nil, err + } + + // add header parameters, if any + if len(headerParams) > 0 { + headers := http.Header{} + for h, v := range headerParams { + headers[h] = []string{v} + } + localVarRequest.Header = headers + } + + // Add the user agent to the request. + localVarRequest.Header.Add("User-Agent", c.cfg.UserAgent) + + if ctx != nil { + // add context to the request + localVarRequest = localVarRequest.WithContext(ctx) + + // Walk through any authentication. + + // OAuth2 authentication + if tok, ok := ctx.Value(ContextOAuth2).(oauth2.TokenSource); ok { + // We were able to grab an oauth2 token from the context + var latestToken *oauth2.Token + if latestToken, err = tok.Token(); err != nil { + return nil, err + } + + latestToken.SetAuthHeader(localVarRequest) + } + + // Basic HTTP Authentication + if auth, ok := ctx.Value(ContextBasicAuth).(BasicAuth); ok { + localVarRequest.SetBasicAuth(auth.UserName, auth.Password) + } + + // AccessToken Authentication + if auth, ok := ctx.Value(ContextAccessToken).(string); ok { + localVarRequest.Header.Add("Authorization", "Bearer "+auth) + } + + } + + for header, value := range c.cfg.DefaultHeader { + localVarRequest.Header.Add(header, value) + } + return localVarRequest, nil +} + +func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err error) { + if len(b) == 0 { + return nil + } + if s, ok := v.(*string); ok { + *s = string(b) + return nil + } + if f, ok := v.(**os.File); ok { + *f, err = ioutil.TempFile("", "HttpClientFile") + if err != nil { + return + } + _, err = (*f).Write(b) + if err != nil { + return + } + _, err = (*f).Seek(0, io.SeekStart) + return + } + if xmlCheck.MatchString(contentType) { + if err = xml.Unmarshal(b, v); err != nil { + return err + } + return nil + } + if jsonCheck.MatchString(contentType) { + if actualObj, ok := v.(interface{ GetActualInstance() interface{} }); ok { // oneOf, anyOf schemas + if unmarshalObj, ok := actualObj.(interface{ UnmarshalJSON([]byte) error }); ok { // make sure it has UnmarshalJSON defined + if err = unmarshalObj.UnmarshalJSON(b); err != nil { + return err + } + } else { + return errors.New("Unknown type with GetActualInstance but no unmarshalObj.UnmarshalJSON defined") + } + } else if err = json.Unmarshal(b, v); err != nil { // simple model + return err + } + return nil + } + return errors.New("undefined response type") +} + +// Add a file to the multipart request +func addFile(w *multipart.Writer, fieldName, path string) error { + file, err := os.Open(filepath.Clean(path)) + if err != nil { + return err + } + err = file.Close() + if err != nil { + return err + } + + part, err := w.CreateFormFile(fieldName, filepath.Base(path)) + if err != nil { + return err + } + _, err = io.Copy(part, file) + + return err +} + +// Prevent trying to import "fmt" +func reportError(format string, a ...interface{}) error { + return fmt.Errorf(format, a...) +} + +// A wrapper for strict JSON decoding +func newStrictDecoder(data []byte) *json.Decoder { + dec := json.NewDecoder(bytes.NewBuffer(data)) + dec.DisallowUnknownFields() + return dec +} + +// Set request body from an interface{} +func setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) { + if bodyBuf == nil { + bodyBuf = &bytes.Buffer{} + } + + if reader, ok := body.(io.Reader); ok { + _, err = bodyBuf.ReadFrom(reader) + } else if fp, ok := body.(**os.File); ok { + _, err = bodyBuf.ReadFrom(*fp) + } else if b, ok := body.([]byte); ok { + _, err = bodyBuf.Write(b) + } else if s, ok := body.(string); ok { + _, err = bodyBuf.WriteString(s) + } else if s, ok := body.(*string); ok { + _, err = bodyBuf.WriteString(*s) + } else if jsonCheck.MatchString(contentType) { + err = json.NewEncoder(bodyBuf).Encode(body) + } else if xmlCheck.MatchString(contentType) { + err = xml.NewEncoder(bodyBuf).Encode(body) + } + + if err != nil { + return nil, err + } + + if bodyBuf.Len() == 0 { + err = fmt.Errorf("Invalid body type %s\n", contentType) + return nil, err + } + return bodyBuf, nil +} + +// detectContentType method is used to figure out `Request.Body` content type for request header +func detectContentType(body interface{}) string { + contentType := "text/plain; charset=utf-8" + kind := reflect.TypeOf(body).Kind() + + switch kind { + case reflect.Struct, reflect.Map, reflect.Ptr: + contentType = "application/json; charset=utf-8" + case reflect.String: + contentType = "text/plain; charset=utf-8" + default: + if b, ok := body.([]byte); ok { + contentType = http.DetectContentType(b) + } else if kind == reflect.Slice { + contentType = "application/json; charset=utf-8" + } + } + + return contentType +} + +// Ripped from https://github.com/gregjones/httpcache/blob/master/httpcache.go +type cacheControl map[string]string + +func parseCacheControl(headers http.Header) cacheControl { + cc := cacheControl{} + ccHeader := headers.Get("Cache-Control") + for _, part := range strings.Split(ccHeader, ",") { + part = strings.Trim(part, " ") + if part == "" { + continue + } + if strings.ContainsRune(part, '=') { + keyval := strings.Split(part, "=") + cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",") + } else { + cc[part] = "" + } + } + return cc +} + +// CacheExpires helper function to determine remaining time before repeating a request. +func CacheExpires(r *http.Response) time.Time { + // Figure out when the cache expires. + var expires time.Time + now, err := time.Parse(time.RFC1123, r.Header.Get("date")) + if err != nil { + return time.Now() + } + respCacheControl := parseCacheControl(r.Header) + + if maxAge, ok := respCacheControl["max-age"]; ok { + lifetime, err := time.ParseDuration(maxAge + "s") + if err != nil { + expires = now + } else { + expires = now.Add(lifetime) + } + } else { + expiresHeader := r.Header.Get("Expires") + if expiresHeader != "" { + expires, err = time.Parse(time.RFC1123, expiresHeader) + if err != nil { + expires = now + } + } + } + return expires +} + +func strlen(s string) int { + return utf8.RuneCountInString(s) +} + +// GenericOpenAPIError Provides access to the body, error and model on returned errors. +type GenericOpenAPIError struct { + body []byte + error string + model interface{} +} + +// Error returns non-empty string if there was an error. +func (e GenericOpenAPIError) Error() string { + return e.error +} + +// Body returns the raw bytes of the response +func (e GenericOpenAPIError) Body() []byte { + return e.body +} + +// Model returns the unpacked model of the error +func (e GenericOpenAPIError) Model() interface{} { + return e.model +} diff --git a/src/pkg/data/consumers/governor/go-client/configuration.go b/src/pkg/data/consumers/governor/go-client/configuration.go new file mode 100644 index 00000000..b9ae6267 --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/configuration.go @@ -0,0 +1,244 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "context" + "fmt" + "net/http" + "strings" +) + +// contextKeys are used to identify the type of value in the context. +// Since these are string, it is possible to get a short description of the +// context key for logging and debugging using key.String(). + +type contextKey string + +func (c contextKey) String() string { + return "auth " + string(c) +} + +var ( + // ContextOAuth2 takes an oauth2.TokenSource as authentication for the request. + ContextOAuth2 = contextKey("token") + + // ContextBasicAuth takes BasicAuth as authentication for the request. + ContextBasicAuth = contextKey("basic") + + // ContextAccessToken takes a string oauth2 access token as authentication for the request. + ContextAccessToken = contextKey("accesstoken") + + // ContextAPIKeys takes a string apikey as authentication for the request + ContextAPIKeys = contextKey("apiKeys") + + // ContextHttpSignatureAuth takes HttpSignatureAuth as authentication for the request. + ContextHttpSignatureAuth = contextKey("httpsignature") + + // ContextServerIndex uses a server configuration from the index. + ContextServerIndex = contextKey("serverIndex") + + // ContextOperationServerIndices uses a server configuration from the index mapping. + ContextOperationServerIndices = contextKey("serverOperationIndices") + + // ContextServerVariables overrides a server configuration variables. + ContextServerVariables = contextKey("serverVariables") + + // ContextOperationServerVariables overrides a server configuration variables using operation specific values. + ContextOperationServerVariables = contextKey("serverOperationVariables") +) + +// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth +type BasicAuth struct { + UserName string `json:"userName,omitempty"` + Password string `json:"password,omitempty"` +} + +// APIKey provides API key based authentication to a request passed via context using ContextAPIKey +type APIKey struct { + Key string + Prefix string +} + +// ServerVariable stores the information about a server variable +type ServerVariable struct { + Description string + DefaultValue string + EnumValues []string +} + +// ServerConfiguration stores the information about a server +type ServerConfiguration struct { + URL string + Description string + Variables map[string]ServerVariable +} + +// ServerConfigurations stores multiple ServerConfiguration items +type ServerConfigurations []ServerConfiguration + +// Configuration stores the configuration of the API client +type Configuration struct { + Host string `json:"host,omitempty"` + Scheme string `json:"scheme,omitempty"` + DefaultHeader map[string]string `json:"defaultHeader,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + Debug bool `json:"debug,omitempty"` + Servers ServerConfigurations + OperationServers map[string]ServerConfigurations + HTTPClient *http.Client +} + +// NewConfiguration returns a new Configuration object +func NewConfiguration() *Configuration { + cfg := &Configuration{ + DefaultHeader: make(map[string]string), + UserAgent: "OpenAPI-Generator/1.0.0/go", + Debug: false, + Servers: ServerConfigurations{ + { + URL: "https://localhost:{port}/v1", + Description: "Production Environment", + }, + { + URL: "http://localhost:{port}/v1", + Description: "Local Development Environment", + Variables: map[string]ServerVariable{ + "port": ServerVariable{ + Description: "No description provided", + DefaultValue: "8080", + EnumValues: []string{ + "8080", + }, + }, + }, + }, + }, + OperationServers: map[string]ServerConfigurations{ + }, + } + return cfg +} + +// AddDefaultHeader adds a new HTTP header to the default header in the request +func (c *Configuration) AddDefaultHeader(key string, value string) { + c.DefaultHeader[key] = value +} + +// URL formats template on a index using given variables +func (sc ServerConfigurations) URL(index int, variables map[string]string) (string, error) { + if index < 0 || len(sc) <= index { + return "", fmt.Errorf("Index %v out of range %v", index, len(sc)-1) + } + server := sc[index] + url := server.URL + + // go through variables and replace placeholders + for name, variable := range server.Variables { + if value, ok := variables[name]; ok { + found := bool(len(variable.EnumValues) == 0) + for _, enumValue := range variable.EnumValues { + if value == enumValue { + found = true + } + } + if !found { + return "", fmt.Errorf("The variable %s in the server URL has invalid value %v. Must be %v", name, value, variable.EnumValues) + } + url = strings.Replace(url, "{"+name+"}", value, -1) + } else { + url = strings.Replace(url, "{"+name+"}", variable.DefaultValue, -1) + } + } + return url, nil +} + +// ServerURL returns URL based on server settings +func (c *Configuration) ServerURL(index int, variables map[string]string) (string, error) { + return c.Servers.URL(index, variables) +} + +func getServerIndex(ctx context.Context) (int, error) { + si := ctx.Value(ContextServerIndex) + if si != nil { + if index, ok := si.(int); ok { + return index, nil + } + return 0, reportError("Invalid type %T should be int", si) + } + return 0, nil +} + +func getServerOperationIndex(ctx context.Context, endpoint string) (int, error) { + osi := ctx.Value(ContextOperationServerIndices) + if osi != nil { + if operationIndices, ok := osi.(map[string]int); !ok { + return 0, reportError("Invalid type %T should be map[string]int", osi) + } else { + index, ok := operationIndices[endpoint] + if ok { + return index, nil + } + } + } + return getServerIndex(ctx) +} + +func getServerVariables(ctx context.Context) (map[string]string, error) { + sv := ctx.Value(ContextServerVariables) + if sv != nil { + if variables, ok := sv.(map[string]string); ok { + return variables, nil + } + return nil, reportError("ctx value of ContextServerVariables has invalid type %T should be map[string]string", sv) + } + return nil, nil +} + +func getServerOperationVariables(ctx context.Context, endpoint string) (map[string]string, error) { + osv := ctx.Value(ContextOperationServerVariables) + if osv != nil { + if operationVariables, ok := osv.(map[string]map[string]string); !ok { + return nil, reportError("ctx value of ContextOperationServerVariables has invalid type %T should be map[string]map[string]string", osv) + } else { + variables, ok := operationVariables[endpoint] + if ok { + return variables, nil + } + } + } + return getServerVariables(ctx) +} + +// ServerURLWithContext returns a new server URL given an endpoint +func (c *Configuration) ServerURLWithContext(ctx context.Context, endpoint string) (string, error) { + sc, ok := c.OperationServers[endpoint] + if !ok { + sc = c.Servers + } + + if ctx == nil { + return sc.URL(0, nil) + } + + index, err := getServerOperationIndex(ctx, endpoint) + if err != nil { + return "", err + } + + variables, err := getServerOperationVariables(ctx, endpoint) + if err != nil { + return "", err + } + + return sc.URL(index, variables) +} diff --git a/src/pkg/data/consumers/governor/go-client/model_constraints_violation.go b/src/pkg/data/consumers/governor/go-client/model_constraints_violation.go new file mode 100644 index 00000000..822efd02 --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/model_constraints_violation.go @@ -0,0 +1,166 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" +) + +// ConstraintsViolation It represents a constraints violation error +type ConstraintsViolation struct { + // The field that is causing the constraints violation + Field string `json:"field"` + // The human-readable constraints violation description + Message string `json:"message"` + AdditionalProperties map[string]interface{} +} + +type _ConstraintsViolation ConstraintsViolation + +// NewConstraintsViolation instantiates a new ConstraintsViolation object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewConstraintsViolation(field string, message string) *ConstraintsViolation { + this := ConstraintsViolation{} + this.Field = field + this.Message = message + return &this +} + +// NewConstraintsViolationWithDefaults instantiates a new ConstraintsViolation object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewConstraintsViolationWithDefaults() *ConstraintsViolation { + this := ConstraintsViolation{} + return &this +} + +// GetField returns the Field field value +func (o *ConstraintsViolation) GetField() string { + if o == nil { + var ret string + return ret + } + + return o.Field +} + +// GetFieldOk returns a tuple with the Field field value +// and a boolean to check if the value has been set. +func (o *ConstraintsViolation) GetFieldOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Field, true +} + +// SetField sets field value +func (o *ConstraintsViolation) SetField(v string) { + o.Field = v +} + +// GetMessage returns the Message field value +func (o *ConstraintsViolation) GetMessage() string { + if o == nil { + var ret string + return ret + } + + return o.Message +} + +// GetMessageOk returns a tuple with the Message field value +// and a boolean to check if the value has been set. +func (o *ConstraintsViolation) GetMessageOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Message, true +} + +// SetMessage sets field value +func (o *ConstraintsViolation) SetMessage(v string) { + o.Message = v +} + +func (o ConstraintsViolation) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["field"] = o.Field + } + if true { + toSerialize["message"] = o.Message + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return json.Marshal(toSerialize) +} + +func (o *ConstraintsViolation) UnmarshalJSON(bytes []byte) (err error) { + varConstraintsViolation := _ConstraintsViolation{} + + if err = json.Unmarshal(bytes, &varConstraintsViolation); err == nil { + *o = ConstraintsViolation(varConstraintsViolation) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + delete(additionalProperties, "field") + delete(additionalProperties, "message") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableConstraintsViolation struct { + value *ConstraintsViolation + isSet bool +} + +func (v NullableConstraintsViolation) Get() *ConstraintsViolation { + return v.value +} + +func (v *NullableConstraintsViolation) Set(val *ConstraintsViolation) { + v.value = val + v.isSet = true +} + +func (v NullableConstraintsViolation) IsSet() bool { + return v.isSet +} + +func (v *NullableConstraintsViolation) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableConstraintsViolation(val *ConstraintsViolation) *NullableConstraintsViolation { + return &NullableConstraintsViolation{value: val, isSet: true} +} + +func (v NullableConstraintsViolation) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableConstraintsViolation) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/src/pkg/data/consumers/governor/go-client/model_constraints_violations_error.go b/src/pkg/data/consumers/governor/go-client/model_constraints_violations_error.go new file mode 100644 index 00000000..3962965c --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/model_constraints_violations_error.go @@ -0,0 +1,309 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" +) + +// ConstraintsViolationsError Error object extension for returning a constraints violation error +type ConstraintsViolationsError struct { + Violations []ConstraintsViolation `json:"violations"` + // A URI reference that identifies the problem type When dereferenced, it provide human-readable documentation for the problem type using HTML + Type string `json:"type"` + // A short, human-readable summary of the problem type + Title string `json:"title"` + // The HTTP status code generated by the origin server for this occurrence of the problem + Status *int32 `json:"status,omitempty"` + // A human-readable explanation specific to this occurrence of the problem + Detail *string `json:"detail,omitempty"` + // A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced + Instance *string `json:"instance,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _ConstraintsViolationsError ConstraintsViolationsError + +// NewConstraintsViolationsError instantiates a new ConstraintsViolationsError object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewConstraintsViolationsError(violations []ConstraintsViolation, type_ string, title string) *ConstraintsViolationsError { + this := ConstraintsViolationsError{} + this.Type = type_ + this.Title = title + return &this +} + +// NewConstraintsViolationsErrorWithDefaults instantiates a new ConstraintsViolationsError object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewConstraintsViolationsErrorWithDefaults() *ConstraintsViolationsError { + this := ConstraintsViolationsError{} + return &this +} + +// GetViolations returns the Violations field value +func (o *ConstraintsViolationsError) GetViolations() []ConstraintsViolation { + if o == nil { + var ret []ConstraintsViolation + return ret + } + + return o.Violations +} + +// GetViolationsOk returns a tuple with the Violations field value +// and a boolean to check if the value has been set. +func (o *ConstraintsViolationsError) GetViolationsOk() ([]ConstraintsViolation, bool) { + if o == nil { + return nil, false + } + return o.Violations, true +} + +// SetViolations sets field value +func (o *ConstraintsViolationsError) SetViolations(v []ConstraintsViolation) { + o.Violations = v +} + +// GetType returns the Type field value +func (o *ConstraintsViolationsError) GetType() string { + if o == nil { + var ret string + return ret + } + + return o.Type +} + +// GetTypeOk returns a tuple with the Type field value +// and a boolean to check if the value has been set. +func (o *ConstraintsViolationsError) GetTypeOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Type, true +} + +// SetType sets field value +func (o *ConstraintsViolationsError) SetType(v string) { + o.Type = v +} + +// GetTitle returns the Title field value +func (o *ConstraintsViolationsError) GetTitle() string { + if o == nil { + var ret string + return ret + } + + return o.Title +} + +// GetTitleOk returns a tuple with the Title field value +// and a boolean to check if the value has been set. +func (o *ConstraintsViolationsError) GetTitleOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Title, true +} + +// SetTitle sets field value +func (o *ConstraintsViolationsError) SetTitle(v string) { + o.Title = v +} + +// GetStatus returns the Status field value if set, zero value otherwise. +func (o *ConstraintsViolationsError) GetStatus() int32 { + if o == nil || o.Status == nil { + var ret int32 + return ret + } + return *o.Status +} + +// GetStatusOk returns a tuple with the Status field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ConstraintsViolationsError) GetStatusOk() (*int32, bool) { + if o == nil || o.Status == nil { + return nil, false + } + return o.Status, true +} + +// HasStatus returns a boolean if a field has been set. +func (o *ConstraintsViolationsError) HasStatus() bool { + if o != nil && o.Status != nil { + return true + } + + return false +} + +// SetStatus gets a reference to the given int32 and assigns it to the Status field. +func (o *ConstraintsViolationsError) SetStatus(v int32) { + o.Status = &v +} + +// GetDetail returns the Detail field value if set, zero value otherwise. +func (o *ConstraintsViolationsError) GetDetail() string { + if o == nil || o.Detail == nil { + var ret string + return ret + } + return *o.Detail +} + +// GetDetailOk returns a tuple with the Detail field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ConstraintsViolationsError) GetDetailOk() (*string, bool) { + if o == nil || o.Detail == nil { + return nil, false + } + return o.Detail, true +} + +// HasDetail returns a boolean if a field has been set. +func (o *ConstraintsViolationsError) HasDetail() bool { + if o != nil && o.Detail != nil { + return true + } + + return false +} + +// SetDetail gets a reference to the given string and assigns it to the Detail field. +func (o *ConstraintsViolationsError) SetDetail(v string) { + o.Detail = &v +} + +// GetInstance returns the Instance field value if set, zero value otherwise. +func (o *ConstraintsViolationsError) GetInstance() string { + if o == nil || o.Instance == nil { + var ret string + return ret + } + return *o.Instance +} + +// GetInstanceOk returns a tuple with the Instance field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ConstraintsViolationsError) GetInstanceOk() (*string, bool) { + if o == nil || o.Instance == nil { + return nil, false + } + return o.Instance, true +} + +// HasInstance returns a boolean if a field has been set. +func (o *ConstraintsViolationsError) HasInstance() bool { + if o != nil && o.Instance != nil { + return true + } + + return false +} + +// SetInstance gets a reference to the given string and assigns it to the Instance field. +func (o *ConstraintsViolationsError) SetInstance(v string) { + o.Instance = &v +} + +func (o ConstraintsViolationsError) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["violations"] = o.Violations + } + if true { + toSerialize["type"] = o.Type + } + if true { + toSerialize["title"] = o.Title + } + if o.Status != nil { + toSerialize["status"] = o.Status + } + if o.Detail != nil { + toSerialize["detail"] = o.Detail + } + if o.Instance != nil { + toSerialize["instance"] = o.Instance + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return json.Marshal(toSerialize) +} + +func (o *ConstraintsViolationsError) UnmarshalJSON(bytes []byte) (err error) { + varConstraintsViolationsError := _ConstraintsViolationsError{} + + if err = json.Unmarshal(bytes, &varConstraintsViolationsError); err == nil { + *o = ConstraintsViolationsError(varConstraintsViolationsError) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + delete(additionalProperties, "violations") + delete(additionalProperties, "type") + delete(additionalProperties, "title") + delete(additionalProperties, "status") + delete(additionalProperties, "detail") + delete(additionalProperties, "instance") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableConstraintsViolationsError struct { + value *ConstraintsViolationsError + isSet bool +} + +func (v NullableConstraintsViolationsError) Get() *ConstraintsViolationsError { + return v.value +} + +func (v *NullableConstraintsViolationsError) Set(val *ConstraintsViolationsError) { + v.value = val + v.isSet = true +} + +func (v NullableConstraintsViolationsError) IsSet() bool { + return v.isSet +} + +func (v *NullableConstraintsViolationsError) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableConstraintsViolationsError(val *ConstraintsViolationsError) *NullableConstraintsViolationsError { + return &NullableConstraintsViolationsError{value: val, isSet: true} +} + +func (v NullableConstraintsViolationsError) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableConstraintsViolationsError) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/src/pkg/data/consumers/governor/go-client/model_container.go b/src/pkg/data/consumers/governor/go-client/model_container.go new file mode 100644 index 00000000..e8c1ca14 --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/model_container.go @@ -0,0 +1,197 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" +) + +// Container Container configured in a workload deployed +type Container struct { + // Name of the container in a workload deployed + Name string `json:"name"` + // Image URI + Image string `json:"image"` + // Image ID + ImageId string `json:"image_id"` + AdditionalProperties map[string]interface{} +} + +type _Container Container + +// NewContainer instantiates a new Container object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewContainer(name string, image string, imageId string) *Container { + this := Container{} + this.Name = name + this.Image = image + this.ImageId = imageId + return &this +} + +// NewContainerWithDefaults instantiates a new Container object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewContainerWithDefaults() *Container { + this := Container{} + return &this +} + +// GetName returns the Name field value +func (o *Container) GetName() string { + if o == nil { + var ret string + return ret + } + + return o.Name +} + +// GetNameOk returns a tuple with the Name field value +// and a boolean to check if the value has been set. +func (o *Container) GetNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Name, true +} + +// SetName sets field value +func (o *Container) SetName(v string) { + o.Name = v +} + +// GetImage returns the Image field value +func (o *Container) GetImage() string { + if o == nil { + var ret string + return ret + } + + return o.Image +} + +// GetImageOk returns a tuple with the Image field value +// and a boolean to check if the value has been set. +func (o *Container) GetImageOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Image, true +} + +// SetImage sets field value +func (o *Container) SetImage(v string) { + o.Image = v +} + +// GetImageId returns the ImageId field value +func (o *Container) GetImageId() string { + if o == nil { + var ret string + return ret + } + + return o.ImageId +} + +// GetImageIdOk returns a tuple with the ImageId field value +// and a boolean to check if the value has been set. +func (o *Container) GetImageIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.ImageId, true +} + +// SetImageId sets field value +func (o *Container) SetImageId(v string) { + o.ImageId = v +} + +func (o Container) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["name"] = o.Name + } + if true { + toSerialize["image"] = o.Image + } + if true { + toSerialize["image_id"] = o.ImageId + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return json.Marshal(toSerialize) +} + +func (o *Container) UnmarshalJSON(bytes []byte) (err error) { + varContainer := _Container{} + + if err = json.Unmarshal(bytes, &varContainer); err == nil { + *o = Container(varContainer) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + delete(additionalProperties, "name") + delete(additionalProperties, "image") + delete(additionalProperties, "image_id") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableContainer struct { + value *Container + isSet bool +} + +func (v NullableContainer) Get() *Container { + return v.value +} + +func (v *NullableContainer) Set(val *Container) { + v.value = val + v.isSet = true +} + +func (v NullableContainer) IsSet() bool { + return v.isSet +} + +func (v *NullableContainer) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableContainer(val *Container) *NullableContainer { + return &NullableContainer{value: val, isSet: true} +} + +func (v NullableContainer) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableContainer) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/src/pkg/data/consumers/governor/go-client/model_error.go b/src/pkg/data/consumers/governor/go-client/model_error.go new file mode 100644 index 00000000..e108ccb3 --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/model_error.go @@ -0,0 +1,280 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" +) + +// Error Note: It follows [RFC-7807](https://tools.ietf.org/html/rfc7807#page-9) **proposed** standard Error object returned on any failure. It can be extended to add more params +type Error struct { + // A URI reference that identifies the problem type When dereferenced, it provide human-readable documentation for the problem type using HTML + Type string `json:"type"` + // A short, human-readable summary of the problem type + Title string `json:"title"` + // The HTTP status code generated by the origin server for this occurrence of the problem + Status *int32 `json:"status,omitempty"` + // A human-readable explanation specific to this occurrence of the problem + Detail *string `json:"detail,omitempty"` + // A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced + Instance *string `json:"instance,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _Error Error + +// NewError instantiates a new Error object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewError(type_ string, title string) *Error { + this := Error{} + this.Type = type_ + this.Title = title + return &this +} + +// NewErrorWithDefaults instantiates a new Error object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewErrorWithDefaults() *Error { + this := Error{} + return &this +} + +// GetType returns the Type field value +func (o *Error) GetType() string { + if o == nil { + var ret string + return ret + } + + return o.Type +} + +// GetTypeOk returns a tuple with the Type field value +// and a boolean to check if the value has been set. +func (o *Error) GetTypeOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Type, true +} + +// SetType sets field value +func (o *Error) SetType(v string) { + o.Type = v +} + +// GetTitle returns the Title field value +func (o *Error) GetTitle() string { + if o == nil { + var ret string + return ret + } + + return o.Title +} + +// GetTitleOk returns a tuple with the Title field value +// and a boolean to check if the value has been set. +func (o *Error) GetTitleOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Title, true +} + +// SetTitle sets field value +func (o *Error) SetTitle(v string) { + o.Title = v +} + +// GetStatus returns the Status field value if set, zero value otherwise. +func (o *Error) GetStatus() int32 { + if o == nil || o.Status == nil { + var ret int32 + return ret + } + return *o.Status +} + +// GetStatusOk returns a tuple with the Status field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Error) GetStatusOk() (*int32, bool) { + if o == nil || o.Status == nil { + return nil, false + } + return o.Status, true +} + +// HasStatus returns a boolean if a field has been set. +func (o *Error) HasStatus() bool { + if o != nil && o.Status != nil { + return true + } + + return false +} + +// SetStatus gets a reference to the given int32 and assigns it to the Status field. +func (o *Error) SetStatus(v int32) { + o.Status = &v +} + +// GetDetail returns the Detail field value if set, zero value otherwise. +func (o *Error) GetDetail() string { + if o == nil || o.Detail == nil { + var ret string + return ret + } + return *o.Detail +} + +// GetDetailOk returns a tuple with the Detail field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Error) GetDetailOk() (*string, bool) { + if o == nil || o.Detail == nil { + return nil, false + } + return o.Detail, true +} + +// HasDetail returns a boolean if a field has been set. +func (o *Error) HasDetail() bool { + if o != nil && o.Detail != nil { + return true + } + + return false +} + +// SetDetail gets a reference to the given string and assigns it to the Detail field. +func (o *Error) SetDetail(v string) { + o.Detail = &v +} + +// GetInstance returns the Instance field value if set, zero value otherwise. +func (o *Error) GetInstance() string { + if o == nil || o.Instance == nil { + var ret string + return ret + } + return *o.Instance +} + +// GetInstanceOk returns a tuple with the Instance field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Error) GetInstanceOk() (*string, bool) { + if o == nil || o.Instance == nil { + return nil, false + } + return o.Instance, true +} + +// HasInstance returns a boolean if a field has been set. +func (o *Error) HasInstance() bool { + if o != nil && o.Instance != nil { + return true + } + + return false +} + +// SetInstance gets a reference to the given string and assigns it to the Instance field. +func (o *Error) SetInstance(v string) { + o.Instance = &v +} + +func (o Error) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["type"] = o.Type + } + if true { + toSerialize["title"] = o.Title + } + if o.Status != nil { + toSerialize["status"] = o.Status + } + if o.Detail != nil { + toSerialize["detail"] = o.Detail + } + if o.Instance != nil { + toSerialize["instance"] = o.Instance + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return json.Marshal(toSerialize) +} + +func (o *Error) UnmarshalJSON(bytes []byte) (err error) { + varError := _Error{} + + if err = json.Unmarshal(bytes, &varError); err == nil { + *o = Error(varError) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + delete(additionalProperties, "type") + delete(additionalProperties, "title") + delete(additionalProperties, "status") + delete(additionalProperties, "detail") + delete(additionalProperties, "instance") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableError struct { + value *Error + isSet bool +} + +func (v NullableError) Get() *Error { + return v.value +} + +func (v *NullableError) Set(val *Error) { + v.value = val + v.isSet = true +} + +func (v NullableError) IsSet() bool { + return v.isSet +} + +func (v *NullableError) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableError(val *Error) *NullableError { + return &NullableError{value: val, isSet: true} +} + +func (v NullableError) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableError) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/src/pkg/data/consumers/governor/go-client/model_kubernetes_agent_config_request.go b/src/pkg/data/consumers/governor/go-client/model_kubernetes_agent_config_request.go new file mode 100644 index 00000000..699f4e7a --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/model_kubernetes_agent_config_request.go @@ -0,0 +1,165 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" +) + +// KubernetesAgentConfigRequest Payload to request agent config manifest for a cluster +type KubernetesAgentConfigRequest struct { + MatchLabels []MatchLabel `json:"match_labels"` + // API token for agent running in a cluster + ApiToken string `json:"api_token"` + AdditionalProperties map[string]interface{} +} + +type _KubernetesAgentConfigRequest KubernetesAgentConfigRequest + +// NewKubernetesAgentConfigRequest instantiates a new KubernetesAgentConfigRequest object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewKubernetesAgentConfigRequest(matchLabels []MatchLabel, apiToken string) *KubernetesAgentConfigRequest { + this := KubernetesAgentConfigRequest{} + this.MatchLabels = matchLabels + this.ApiToken = apiToken + return &this +} + +// NewKubernetesAgentConfigRequestWithDefaults instantiates a new KubernetesAgentConfigRequest object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewKubernetesAgentConfigRequestWithDefaults() *KubernetesAgentConfigRequest { + this := KubernetesAgentConfigRequest{} + return &this +} + +// GetMatchLabels returns the MatchLabels field value +func (o *KubernetesAgentConfigRequest) GetMatchLabels() []MatchLabel { + if o == nil { + var ret []MatchLabel + return ret + } + + return o.MatchLabels +} + +// GetMatchLabelsOk returns a tuple with the MatchLabels field value +// and a boolean to check if the value has been set. +func (o *KubernetesAgentConfigRequest) GetMatchLabelsOk() ([]MatchLabel, bool) { + if o == nil { + return nil, false + } + return o.MatchLabels, true +} + +// SetMatchLabels sets field value +func (o *KubernetesAgentConfigRequest) SetMatchLabels(v []MatchLabel) { + o.MatchLabels = v +} + +// GetApiToken returns the ApiToken field value +func (o *KubernetesAgentConfigRequest) GetApiToken() string { + if o == nil { + var ret string + return ret + } + + return o.ApiToken +} + +// GetApiTokenOk returns a tuple with the ApiToken field value +// and a boolean to check if the value has been set. +func (o *KubernetesAgentConfigRequest) GetApiTokenOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.ApiToken, true +} + +// SetApiToken sets field value +func (o *KubernetesAgentConfigRequest) SetApiToken(v string) { + o.ApiToken = v +} + +func (o KubernetesAgentConfigRequest) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["match_labels"] = o.MatchLabels + } + if true { + toSerialize["api_token"] = o.ApiToken + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return json.Marshal(toSerialize) +} + +func (o *KubernetesAgentConfigRequest) UnmarshalJSON(bytes []byte) (err error) { + varKubernetesAgentConfigRequest := _KubernetesAgentConfigRequest{} + + if err = json.Unmarshal(bytes, &varKubernetesAgentConfigRequest); err == nil { + *o = KubernetesAgentConfigRequest(varKubernetesAgentConfigRequest) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + delete(additionalProperties, "match_labels") + delete(additionalProperties, "api_token") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableKubernetesAgentConfigRequest struct { + value *KubernetesAgentConfigRequest + isSet bool +} + +func (v NullableKubernetesAgentConfigRequest) Get() *KubernetesAgentConfigRequest { + return v.value +} + +func (v *NullableKubernetesAgentConfigRequest) Set(val *KubernetesAgentConfigRequest) { + v.value = val + v.isSet = true +} + +func (v NullableKubernetesAgentConfigRequest) IsSet() bool { + return v.isSet +} + +func (v *NullableKubernetesAgentConfigRequest) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableKubernetesAgentConfigRequest(val *KubernetesAgentConfigRequest) *NullableKubernetesAgentConfigRequest { + return &NullableKubernetesAgentConfigRequest{value: val, isSet: true} +} + +func (v NullableKubernetesAgentConfigRequest) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableKubernetesAgentConfigRequest) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/src/pkg/data/consumers/governor/go-client/model_kubernetes_cluster_detailed_response.go b/src/pkg/data/consumers/governor/go-client/model_kubernetes_cluster_detailed_response.go new file mode 100644 index 00000000..e51163a2 --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/model_kubernetes_cluster_detailed_response.go @@ -0,0 +1,356 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" + "time" +) + +// KubernetesClusterDetailedResponse A Kubernetes cluster with its metadata, workloads and audit information included +type KubernetesClusterDetailedResponse struct { + // id of the cluster where the workloads are deployed + Id string `json:"id"` + // name of the cluster where the workloads are deployed + Name string `json:"name"` + // Creation date + CreatedAt *time.Time `json:"created_at,omitempty"` + // Last updated date + UpdatedAt *time.Time `json:"updated_at,omitempty"` + // User who created the cluster + CreatedBy *string `json:"created_by,omitempty"` + // User who last updated the cluster + UpdatedBy *string `json:"updated_by,omitempty"` + Telemetry *KubernetesTelemetryResponse `json:"telemetry,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _KubernetesClusterDetailedResponse KubernetesClusterDetailedResponse + +// NewKubernetesClusterDetailedResponse instantiates a new KubernetesClusterDetailedResponse object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewKubernetesClusterDetailedResponse(id string, name string) *KubernetesClusterDetailedResponse { + this := KubernetesClusterDetailedResponse{} + this.Id = id + this.Name = name + return &this +} + +// NewKubernetesClusterDetailedResponseWithDefaults instantiates a new KubernetesClusterDetailedResponse object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewKubernetesClusterDetailedResponseWithDefaults() *KubernetesClusterDetailedResponse { + this := KubernetesClusterDetailedResponse{} + return &this +} + +// GetId returns the Id field value +func (o *KubernetesClusterDetailedResponse) GetId() string { + if o == nil { + var ret string + return ret + } + + return o.Id +} + +// GetIdOk returns a tuple with the Id field value +// and a boolean to check if the value has been set. +func (o *KubernetesClusterDetailedResponse) GetIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Id, true +} + +// SetId sets field value +func (o *KubernetesClusterDetailedResponse) SetId(v string) { + o.Id = v +} + +// GetName returns the Name field value +func (o *KubernetesClusterDetailedResponse) GetName() string { + if o == nil { + var ret string + return ret + } + + return o.Name +} + +// GetNameOk returns a tuple with the Name field value +// and a boolean to check if the value has been set. +func (o *KubernetesClusterDetailedResponse) GetNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Name, true +} + +// SetName sets field value +func (o *KubernetesClusterDetailedResponse) SetName(v string) { + o.Name = v +} + +// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise. +func (o *KubernetesClusterDetailedResponse) GetCreatedAt() time.Time { + if o == nil || o.CreatedAt == nil { + var ret time.Time + return ret + } + return *o.CreatedAt +} + +// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KubernetesClusterDetailedResponse) GetCreatedAtOk() (*time.Time, bool) { + if o == nil || o.CreatedAt == nil { + return nil, false + } + return o.CreatedAt, true +} + +// HasCreatedAt returns a boolean if a field has been set. +func (o *KubernetesClusterDetailedResponse) HasCreatedAt() bool { + if o != nil && o.CreatedAt != nil { + return true + } + + return false +} + +// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field. +func (o *KubernetesClusterDetailedResponse) SetCreatedAt(v time.Time) { + o.CreatedAt = &v +} + +// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise. +func (o *KubernetesClusterDetailedResponse) GetUpdatedAt() time.Time { + if o == nil || o.UpdatedAt == nil { + var ret time.Time + return ret + } + return *o.UpdatedAt +} + +// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KubernetesClusterDetailedResponse) GetUpdatedAtOk() (*time.Time, bool) { + if o == nil || o.UpdatedAt == nil { + return nil, false + } + return o.UpdatedAt, true +} + +// HasUpdatedAt returns a boolean if a field has been set. +func (o *KubernetesClusterDetailedResponse) HasUpdatedAt() bool { + if o != nil && o.UpdatedAt != nil { + return true + } + + return false +} + +// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field. +func (o *KubernetesClusterDetailedResponse) SetUpdatedAt(v time.Time) { + o.UpdatedAt = &v +} + +// GetCreatedBy returns the CreatedBy field value if set, zero value otherwise. +func (o *KubernetesClusterDetailedResponse) GetCreatedBy() string { + if o == nil || o.CreatedBy == nil { + var ret string + return ret + } + return *o.CreatedBy +} + +// GetCreatedByOk returns a tuple with the CreatedBy field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KubernetesClusterDetailedResponse) GetCreatedByOk() (*string, bool) { + if o == nil || o.CreatedBy == nil { + return nil, false + } + return o.CreatedBy, true +} + +// HasCreatedBy returns a boolean if a field has been set. +func (o *KubernetesClusterDetailedResponse) HasCreatedBy() bool { + if o != nil && o.CreatedBy != nil { + return true + } + + return false +} + +// SetCreatedBy gets a reference to the given string and assigns it to the CreatedBy field. +func (o *KubernetesClusterDetailedResponse) SetCreatedBy(v string) { + o.CreatedBy = &v +} + +// GetUpdatedBy returns the UpdatedBy field value if set, zero value otherwise. +func (o *KubernetesClusterDetailedResponse) GetUpdatedBy() string { + if o == nil || o.UpdatedBy == nil { + var ret string + return ret + } + return *o.UpdatedBy +} + +// GetUpdatedByOk returns a tuple with the UpdatedBy field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KubernetesClusterDetailedResponse) GetUpdatedByOk() (*string, bool) { + if o == nil || o.UpdatedBy == nil { + return nil, false + } + return o.UpdatedBy, true +} + +// HasUpdatedBy returns a boolean if a field has been set. +func (o *KubernetesClusterDetailedResponse) HasUpdatedBy() bool { + if o != nil && o.UpdatedBy != nil { + return true + } + + return false +} + +// SetUpdatedBy gets a reference to the given string and assigns it to the UpdatedBy field. +func (o *KubernetesClusterDetailedResponse) SetUpdatedBy(v string) { + o.UpdatedBy = &v +} + +// GetTelemetry returns the Telemetry field value if set, zero value otherwise. +func (o *KubernetesClusterDetailedResponse) GetTelemetry() KubernetesTelemetryResponse { + if o == nil || o.Telemetry == nil { + var ret KubernetesTelemetryResponse + return ret + } + return *o.Telemetry +} + +// GetTelemetryOk returns a tuple with the Telemetry field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KubernetesClusterDetailedResponse) GetTelemetryOk() (*KubernetesTelemetryResponse, bool) { + if o == nil || o.Telemetry == nil { + return nil, false + } + return o.Telemetry, true +} + +// HasTelemetry returns a boolean if a field has been set. +func (o *KubernetesClusterDetailedResponse) HasTelemetry() bool { + if o != nil && o.Telemetry != nil { + return true + } + + return false +} + +// SetTelemetry gets a reference to the given KubernetesTelemetryResponse and assigns it to the Telemetry field. +func (o *KubernetesClusterDetailedResponse) SetTelemetry(v KubernetesTelemetryResponse) { + o.Telemetry = &v +} + +func (o KubernetesClusterDetailedResponse) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["id"] = o.Id + } + if true { + toSerialize["name"] = o.Name + } + if o.CreatedAt != nil { + toSerialize["created_at"] = o.CreatedAt + } + if o.UpdatedAt != nil { + toSerialize["updated_at"] = o.UpdatedAt + } + if o.CreatedBy != nil { + toSerialize["created_by"] = o.CreatedBy + } + if o.UpdatedBy != nil { + toSerialize["updated_by"] = o.UpdatedBy + } + if o.Telemetry != nil { + toSerialize["telemetry"] = o.Telemetry + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return json.Marshal(toSerialize) +} + +func (o *KubernetesClusterDetailedResponse) UnmarshalJSON(bytes []byte) (err error) { + varKubernetesClusterDetailedResponse := _KubernetesClusterDetailedResponse{} + + if err = json.Unmarshal(bytes, &varKubernetesClusterDetailedResponse); err == nil { + *o = KubernetesClusterDetailedResponse(varKubernetesClusterDetailedResponse) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + delete(additionalProperties, "id") + delete(additionalProperties, "name") + delete(additionalProperties, "created_at") + delete(additionalProperties, "updated_at") + delete(additionalProperties, "created_by") + delete(additionalProperties, "updated_by") + delete(additionalProperties, "telemetry") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableKubernetesClusterDetailedResponse struct { + value *KubernetesClusterDetailedResponse + isSet bool +} + +func (v NullableKubernetesClusterDetailedResponse) Get() *KubernetesClusterDetailedResponse { + return v.value +} + +func (v *NullableKubernetesClusterDetailedResponse) Set(val *KubernetesClusterDetailedResponse) { + v.value = val + v.isSet = true +} + +func (v NullableKubernetesClusterDetailedResponse) IsSet() bool { + return v.isSet +} + +func (v *NullableKubernetesClusterDetailedResponse) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableKubernetesClusterDetailedResponse(val *KubernetesClusterDetailedResponse) *NullableKubernetesClusterDetailedResponse { + return &NullableKubernetesClusterDetailedResponse{value: val, isSet: true} +} + +func (v NullableKubernetesClusterDetailedResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableKubernetesClusterDetailedResponse) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/src/pkg/data/consumers/governor/go-client/model_kubernetes_cluster_detailed_response_all_of.go b/src/pkg/data/consumers/governor/go-client/model_kubernetes_cluster_detailed_response_all_of.go new file mode 100644 index 00000000..65f960a8 --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/model_kubernetes_cluster_detailed_response_all_of.go @@ -0,0 +1,141 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" +) + +// KubernetesClusterDetailedResponseAllOf struct for KubernetesClusterDetailedResponseAllOf +type KubernetesClusterDetailedResponseAllOf struct { + Telemetry *KubernetesTelemetryResponse `json:"telemetry,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _KubernetesClusterDetailedResponseAllOf KubernetesClusterDetailedResponseAllOf + +// NewKubernetesClusterDetailedResponseAllOf instantiates a new KubernetesClusterDetailedResponseAllOf object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewKubernetesClusterDetailedResponseAllOf() *KubernetesClusterDetailedResponseAllOf { + this := KubernetesClusterDetailedResponseAllOf{} + return &this +} + +// NewKubernetesClusterDetailedResponseAllOfWithDefaults instantiates a new KubernetesClusterDetailedResponseAllOf object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewKubernetesClusterDetailedResponseAllOfWithDefaults() *KubernetesClusterDetailedResponseAllOf { + this := KubernetesClusterDetailedResponseAllOf{} + return &this +} + +// GetTelemetry returns the Telemetry field value if set, zero value otherwise. +func (o *KubernetesClusterDetailedResponseAllOf) GetTelemetry() KubernetesTelemetryResponse { + if o == nil || o.Telemetry == nil { + var ret KubernetesTelemetryResponse + return ret + } + return *o.Telemetry +} + +// GetTelemetryOk returns a tuple with the Telemetry field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KubernetesClusterDetailedResponseAllOf) GetTelemetryOk() (*KubernetesTelemetryResponse, bool) { + if o == nil || o.Telemetry == nil { + return nil, false + } + return o.Telemetry, true +} + +// HasTelemetry returns a boolean if a field has been set. +func (o *KubernetesClusterDetailedResponseAllOf) HasTelemetry() bool { + if o != nil && o.Telemetry != nil { + return true + } + + return false +} + +// SetTelemetry gets a reference to the given KubernetesTelemetryResponse and assigns it to the Telemetry field. +func (o *KubernetesClusterDetailedResponseAllOf) SetTelemetry(v KubernetesTelemetryResponse) { + o.Telemetry = &v +} + +func (o KubernetesClusterDetailedResponseAllOf) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if o.Telemetry != nil { + toSerialize["telemetry"] = o.Telemetry + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return json.Marshal(toSerialize) +} + +func (o *KubernetesClusterDetailedResponseAllOf) UnmarshalJSON(bytes []byte) (err error) { + varKubernetesClusterDetailedResponseAllOf := _KubernetesClusterDetailedResponseAllOf{} + + if err = json.Unmarshal(bytes, &varKubernetesClusterDetailedResponseAllOf); err == nil { + *o = KubernetesClusterDetailedResponseAllOf(varKubernetesClusterDetailedResponseAllOf) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + delete(additionalProperties, "telemetry") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableKubernetesClusterDetailedResponseAllOf struct { + value *KubernetesClusterDetailedResponseAllOf + isSet bool +} + +func (v NullableKubernetesClusterDetailedResponseAllOf) Get() *KubernetesClusterDetailedResponseAllOf { + return v.value +} + +func (v *NullableKubernetesClusterDetailedResponseAllOf) Set(val *KubernetesClusterDetailedResponseAllOf) { + v.value = val + v.isSet = true +} + +func (v NullableKubernetesClusterDetailedResponseAllOf) IsSet() bool { + return v.isSet +} + +func (v *NullableKubernetesClusterDetailedResponseAllOf) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableKubernetesClusterDetailedResponseAllOf(val *KubernetesClusterDetailedResponseAllOf) *NullableKubernetesClusterDetailedResponseAllOf { + return &NullableKubernetesClusterDetailedResponseAllOf{value: val, isSet: true} +} + +func (v NullableKubernetesClusterDetailedResponseAllOf) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableKubernetesClusterDetailedResponseAllOf) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/src/pkg/data/consumers/governor/go-client/model_kubernetes_cluster_response.go b/src/pkg/data/consumers/governor/go-client/model_kubernetes_cluster_response.go new file mode 100644 index 00000000..70f87fc7 --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/model_kubernetes_cluster_response.go @@ -0,0 +1,319 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" + "time" +) + +// KubernetesClusterResponse A Kubernetes cluster with its metadata and audit information +type KubernetesClusterResponse struct { + // id of the cluster where the workloads are deployed + Id string `json:"id"` + // name of the cluster where the workloads are deployed + Name string `json:"name"` + // Creation date + CreatedAt *time.Time `json:"created_at,omitempty"` + // Last updated date + UpdatedAt *time.Time `json:"updated_at,omitempty"` + // User who created the cluster + CreatedBy *string `json:"created_by,omitempty"` + // User who last updated the cluster + UpdatedBy *string `json:"updated_by,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _KubernetesClusterResponse KubernetesClusterResponse + +// NewKubernetesClusterResponse instantiates a new KubernetesClusterResponse object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewKubernetesClusterResponse(id string, name string) *KubernetesClusterResponse { + this := KubernetesClusterResponse{} + this.Id = id + this.Name = name + return &this +} + +// NewKubernetesClusterResponseWithDefaults instantiates a new KubernetesClusterResponse object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewKubernetesClusterResponseWithDefaults() *KubernetesClusterResponse { + this := KubernetesClusterResponse{} + return &this +} + +// GetId returns the Id field value +func (o *KubernetesClusterResponse) GetId() string { + if o == nil { + var ret string + return ret + } + + return o.Id +} + +// GetIdOk returns a tuple with the Id field value +// and a boolean to check if the value has been set. +func (o *KubernetesClusterResponse) GetIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Id, true +} + +// SetId sets field value +func (o *KubernetesClusterResponse) SetId(v string) { + o.Id = v +} + +// GetName returns the Name field value +func (o *KubernetesClusterResponse) GetName() string { + if o == nil { + var ret string + return ret + } + + return o.Name +} + +// GetNameOk returns a tuple with the Name field value +// and a boolean to check if the value has been set. +func (o *KubernetesClusterResponse) GetNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Name, true +} + +// SetName sets field value +func (o *KubernetesClusterResponse) SetName(v string) { + o.Name = v +} + +// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise. +func (o *KubernetesClusterResponse) GetCreatedAt() time.Time { + if o == nil || o.CreatedAt == nil { + var ret time.Time + return ret + } + return *o.CreatedAt +} + +// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KubernetesClusterResponse) GetCreatedAtOk() (*time.Time, bool) { + if o == nil || o.CreatedAt == nil { + return nil, false + } + return o.CreatedAt, true +} + +// HasCreatedAt returns a boolean if a field has been set. +func (o *KubernetesClusterResponse) HasCreatedAt() bool { + if o != nil && o.CreatedAt != nil { + return true + } + + return false +} + +// SetCreatedAt gets a reference to the given time.Time and assigns it to the CreatedAt field. +func (o *KubernetesClusterResponse) SetCreatedAt(v time.Time) { + o.CreatedAt = &v +} + +// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise. +func (o *KubernetesClusterResponse) GetUpdatedAt() time.Time { + if o == nil || o.UpdatedAt == nil { + var ret time.Time + return ret + } + return *o.UpdatedAt +} + +// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KubernetesClusterResponse) GetUpdatedAtOk() (*time.Time, bool) { + if o == nil || o.UpdatedAt == nil { + return nil, false + } + return o.UpdatedAt, true +} + +// HasUpdatedAt returns a boolean if a field has been set. +func (o *KubernetesClusterResponse) HasUpdatedAt() bool { + if o != nil && o.UpdatedAt != nil { + return true + } + + return false +} + +// SetUpdatedAt gets a reference to the given time.Time and assigns it to the UpdatedAt field. +func (o *KubernetesClusterResponse) SetUpdatedAt(v time.Time) { + o.UpdatedAt = &v +} + +// GetCreatedBy returns the CreatedBy field value if set, zero value otherwise. +func (o *KubernetesClusterResponse) GetCreatedBy() string { + if o == nil || o.CreatedBy == nil { + var ret string + return ret + } + return *o.CreatedBy +} + +// GetCreatedByOk returns a tuple with the CreatedBy field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KubernetesClusterResponse) GetCreatedByOk() (*string, bool) { + if o == nil || o.CreatedBy == nil { + return nil, false + } + return o.CreatedBy, true +} + +// HasCreatedBy returns a boolean if a field has been set. +func (o *KubernetesClusterResponse) HasCreatedBy() bool { + if o != nil && o.CreatedBy != nil { + return true + } + + return false +} + +// SetCreatedBy gets a reference to the given string and assigns it to the CreatedBy field. +func (o *KubernetesClusterResponse) SetCreatedBy(v string) { + o.CreatedBy = &v +} + +// GetUpdatedBy returns the UpdatedBy field value if set, zero value otherwise. +func (o *KubernetesClusterResponse) GetUpdatedBy() string { + if o == nil || o.UpdatedBy == nil { + var ret string + return ret + } + return *o.UpdatedBy +} + +// GetUpdatedByOk returns a tuple with the UpdatedBy field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KubernetesClusterResponse) GetUpdatedByOk() (*string, bool) { + if o == nil || o.UpdatedBy == nil { + return nil, false + } + return o.UpdatedBy, true +} + +// HasUpdatedBy returns a boolean if a field has been set. +func (o *KubernetesClusterResponse) HasUpdatedBy() bool { + if o != nil && o.UpdatedBy != nil { + return true + } + + return false +} + +// SetUpdatedBy gets a reference to the given string and assigns it to the UpdatedBy field. +func (o *KubernetesClusterResponse) SetUpdatedBy(v string) { + o.UpdatedBy = &v +} + +func (o KubernetesClusterResponse) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["id"] = o.Id + } + if true { + toSerialize["name"] = o.Name + } + if o.CreatedAt != nil { + toSerialize["created_at"] = o.CreatedAt + } + if o.UpdatedAt != nil { + toSerialize["updated_at"] = o.UpdatedAt + } + if o.CreatedBy != nil { + toSerialize["created_by"] = o.CreatedBy + } + if o.UpdatedBy != nil { + toSerialize["updated_by"] = o.UpdatedBy + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return json.Marshal(toSerialize) +} + +func (o *KubernetesClusterResponse) UnmarshalJSON(bytes []byte) (err error) { + varKubernetesClusterResponse := _KubernetesClusterResponse{} + + if err = json.Unmarshal(bytes, &varKubernetesClusterResponse); err == nil { + *o = KubernetesClusterResponse(varKubernetesClusterResponse) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + delete(additionalProperties, "id") + delete(additionalProperties, "name") + delete(additionalProperties, "created_at") + delete(additionalProperties, "updated_at") + delete(additionalProperties, "created_by") + delete(additionalProperties, "updated_by") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableKubernetesClusterResponse struct { + value *KubernetesClusterResponse + isSet bool +} + +func (v NullableKubernetesClusterResponse) Get() *KubernetesClusterResponse { + return v.value +} + +func (v *NullableKubernetesClusterResponse) Set(val *KubernetesClusterResponse) { + v.value = val + v.isSet = true +} + +func (v NullableKubernetesClusterResponse) IsSet() bool { + return v.isSet +} + +func (v *NullableKubernetesClusterResponse) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableKubernetesClusterResponse(val *KubernetesClusterResponse) *NullableKubernetesClusterResponse { + return &NullableKubernetesClusterResponse{value: val, isSet: true} +} + +func (v NullableKubernetesClusterResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableKubernetesClusterResponse) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/src/pkg/data/consumers/governor/go-client/model_kubernetes_telemetry_request.go b/src/pkg/data/consumers/governor/go-client/model_kubernetes_telemetry_request.go new file mode 100644 index 00000000..df27c9aa --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/model_kubernetes_telemetry_request.go @@ -0,0 +1,134 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" +) + +// KubernetesTelemetryRequest Request for updating telemetry for a cluster +type KubernetesTelemetryRequest struct { + Workloads []KubernetesWorkload `json:"workloads"` + AdditionalProperties map[string]interface{} +} + +type _KubernetesTelemetryRequest KubernetesTelemetryRequest + +// NewKubernetesTelemetryRequest instantiates a new KubernetesTelemetryRequest object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewKubernetesTelemetryRequest(workloads []KubernetesWorkload) *KubernetesTelemetryRequest { + this := KubernetesTelemetryRequest{} + this.Workloads = workloads + return &this +} + +// NewKubernetesTelemetryRequestWithDefaults instantiates a new KubernetesTelemetryRequest object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewKubernetesTelemetryRequestWithDefaults() *KubernetesTelemetryRequest { + this := KubernetesTelemetryRequest{} + return &this +} + +// GetWorkloads returns the Workloads field value +func (o *KubernetesTelemetryRequest) GetWorkloads() []KubernetesWorkload { + if o == nil { + var ret []KubernetesWorkload + return ret + } + + return o.Workloads +} + +// GetWorkloadsOk returns a tuple with the Workloads field value +// and a boolean to check if the value has been set. +func (o *KubernetesTelemetryRequest) GetWorkloadsOk() ([]KubernetesWorkload, bool) { + if o == nil { + return nil, false + } + return o.Workloads, true +} + +// SetWorkloads sets field value +func (o *KubernetesTelemetryRequest) SetWorkloads(v []KubernetesWorkload) { + o.Workloads = v +} + +func (o KubernetesTelemetryRequest) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["workloads"] = o.Workloads + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return json.Marshal(toSerialize) +} + +func (o *KubernetesTelemetryRequest) UnmarshalJSON(bytes []byte) (err error) { + varKubernetesTelemetryRequest := _KubernetesTelemetryRequest{} + + if err = json.Unmarshal(bytes, &varKubernetesTelemetryRequest); err == nil { + *o = KubernetesTelemetryRequest(varKubernetesTelemetryRequest) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + delete(additionalProperties, "workloads") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableKubernetesTelemetryRequest struct { + value *KubernetesTelemetryRequest + isSet bool +} + +func (v NullableKubernetesTelemetryRequest) Get() *KubernetesTelemetryRequest { + return v.value +} + +func (v *NullableKubernetesTelemetryRequest) Set(val *KubernetesTelemetryRequest) { + v.value = val + v.isSet = true +} + +func (v NullableKubernetesTelemetryRequest) IsSet() bool { + return v.isSet +} + +func (v *NullableKubernetesTelemetryRequest) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableKubernetesTelemetryRequest(val *KubernetesTelemetryRequest) *NullableKubernetesTelemetryRequest { + return &NullableKubernetesTelemetryRequest{value: val, isSet: true} +} + +func (v NullableKubernetesTelemetryRequest) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableKubernetesTelemetryRequest) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/src/pkg/data/consumers/governor/go-client/model_kubernetes_telemetry_response.go b/src/pkg/data/consumers/governor/go-client/model_kubernetes_telemetry_response.go new file mode 100644 index 00000000..7df2657b --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/model_kubernetes_telemetry_response.go @@ -0,0 +1,211 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" + "time" +) + +// KubernetesTelemetryResponse Response with telemetry properties when querying for cluster details +type KubernetesTelemetryResponse struct { + // Last synchronization of telemetry time + SyncAt *time.Time `json:"sync_at,omitempty"` + // User who sent last telemetry data + SyncBy *string `json:"sync_by,omitempty"` + Workloads []KubernetesWorkload `json:"workloads"` + AdditionalProperties map[string]interface{} +} + +type _KubernetesTelemetryResponse KubernetesTelemetryResponse + +// NewKubernetesTelemetryResponse instantiates a new KubernetesTelemetryResponse object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewKubernetesTelemetryResponse(workloads []KubernetesWorkload) *KubernetesTelemetryResponse { + this := KubernetesTelemetryResponse{} + this.Workloads = workloads + return &this +} + +// NewKubernetesTelemetryResponseWithDefaults instantiates a new KubernetesTelemetryResponse object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewKubernetesTelemetryResponseWithDefaults() *KubernetesTelemetryResponse { + this := KubernetesTelemetryResponse{} + return &this +} + +// GetSyncAt returns the SyncAt field value if set, zero value otherwise. +func (o *KubernetesTelemetryResponse) GetSyncAt() time.Time { + if o == nil || o.SyncAt == nil { + var ret time.Time + return ret + } + return *o.SyncAt +} + +// GetSyncAtOk returns a tuple with the SyncAt field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KubernetesTelemetryResponse) GetSyncAtOk() (*time.Time, bool) { + if o == nil || o.SyncAt == nil { + return nil, false + } + return o.SyncAt, true +} + +// HasSyncAt returns a boolean if a field has been set. +func (o *KubernetesTelemetryResponse) HasSyncAt() bool { + if o != nil && o.SyncAt != nil { + return true + } + + return false +} + +// SetSyncAt gets a reference to the given time.Time and assigns it to the SyncAt field. +func (o *KubernetesTelemetryResponse) SetSyncAt(v time.Time) { + o.SyncAt = &v +} + +// GetSyncBy returns the SyncBy field value if set, zero value otherwise. +func (o *KubernetesTelemetryResponse) GetSyncBy() string { + if o == nil || o.SyncBy == nil { + var ret string + return ret + } + return *o.SyncBy +} + +// GetSyncByOk returns a tuple with the SyncBy field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KubernetesTelemetryResponse) GetSyncByOk() (*string, bool) { + if o == nil || o.SyncBy == nil { + return nil, false + } + return o.SyncBy, true +} + +// HasSyncBy returns a boolean if a field has been set. +func (o *KubernetesTelemetryResponse) HasSyncBy() bool { + if o != nil && o.SyncBy != nil { + return true + } + + return false +} + +// SetSyncBy gets a reference to the given string and assigns it to the SyncBy field. +func (o *KubernetesTelemetryResponse) SetSyncBy(v string) { + o.SyncBy = &v +} + +// GetWorkloads returns the Workloads field value +func (o *KubernetesTelemetryResponse) GetWorkloads() []KubernetesWorkload { + if o == nil { + var ret []KubernetesWorkload + return ret + } + + return o.Workloads +} + +// GetWorkloadsOk returns a tuple with the Workloads field value +// and a boolean to check if the value has been set. +func (o *KubernetesTelemetryResponse) GetWorkloadsOk() ([]KubernetesWorkload, bool) { + if o == nil { + return nil, false + } + return o.Workloads, true +} + +// SetWorkloads sets field value +func (o *KubernetesTelemetryResponse) SetWorkloads(v []KubernetesWorkload) { + o.Workloads = v +} + +func (o KubernetesTelemetryResponse) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if o.SyncAt != nil { + toSerialize["sync_at"] = o.SyncAt + } + if o.SyncBy != nil { + toSerialize["sync_by"] = o.SyncBy + } + if true { + toSerialize["workloads"] = o.Workloads + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return json.Marshal(toSerialize) +} + +func (o *KubernetesTelemetryResponse) UnmarshalJSON(bytes []byte) (err error) { + varKubernetesTelemetryResponse := _KubernetesTelemetryResponse{} + + if err = json.Unmarshal(bytes, &varKubernetesTelemetryResponse); err == nil { + *o = KubernetesTelemetryResponse(varKubernetesTelemetryResponse) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + delete(additionalProperties, "sync_at") + delete(additionalProperties, "sync_by") + delete(additionalProperties, "workloads") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableKubernetesTelemetryResponse struct { + value *KubernetesTelemetryResponse + isSet bool +} + +func (v NullableKubernetesTelemetryResponse) Get() *KubernetesTelemetryResponse { + return v.value +} + +func (v *NullableKubernetesTelemetryResponse) Set(val *KubernetesTelemetryResponse) { + v.value = val + v.isSet = true +} + +func (v NullableKubernetesTelemetryResponse) IsSet() bool { + return v.isSet +} + +func (v *NullableKubernetesTelemetryResponse) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableKubernetesTelemetryResponse(val *KubernetesTelemetryResponse) *NullableKubernetesTelemetryResponse { + return &NullableKubernetesTelemetryResponse{value: val, isSet: true} +} + +func (v NullableKubernetesTelemetryResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableKubernetesTelemetryResponse) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/src/pkg/data/consumers/governor/go-client/model_kubernetes_workload.go b/src/pkg/data/consumers/governor/go-client/model_kubernetes_workload.go new file mode 100644 index 00000000..65f153a6 --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/model_kubernetes_workload.go @@ -0,0 +1,258 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" +) + +// KubernetesWorkload Kubernetes workload deployed in a remote system +type KubernetesWorkload struct { + // Name of the workload deployed + Name string `json:"name"` + // Namespace where the workload is deployed + Namespace string `json:"namespace"` + // Kind of the workload Resource + Kind string `json:"kind"` + // Replicas of Workload + Replicas int32 `json:"replicas"` + Containers []Container `json:"containers"` + AdditionalProperties map[string]interface{} +} + +type _KubernetesWorkload KubernetesWorkload + +// NewKubernetesWorkload instantiates a new KubernetesWorkload object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewKubernetesWorkload(name string, namespace string, kind string, replicas int32, containers []Container) *KubernetesWorkload { + this := KubernetesWorkload{} + this.Name = name + this.Namespace = namespace + this.Kind = kind + this.Replicas = replicas + this.Containers = containers + return &this +} + +// NewKubernetesWorkloadWithDefaults instantiates a new KubernetesWorkload object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewKubernetesWorkloadWithDefaults() *KubernetesWorkload { + this := KubernetesWorkload{} + return &this +} + +// GetName returns the Name field value +func (o *KubernetesWorkload) GetName() string { + if o == nil { + var ret string + return ret + } + + return o.Name +} + +// GetNameOk returns a tuple with the Name field value +// and a boolean to check if the value has been set. +func (o *KubernetesWorkload) GetNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Name, true +} + +// SetName sets field value +func (o *KubernetesWorkload) SetName(v string) { + o.Name = v +} + +// GetNamespace returns the Namespace field value +func (o *KubernetesWorkload) GetNamespace() string { + if o == nil { + var ret string + return ret + } + + return o.Namespace +} + +// GetNamespaceOk returns a tuple with the Namespace field value +// and a boolean to check if the value has been set. +func (o *KubernetesWorkload) GetNamespaceOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Namespace, true +} + +// SetNamespace sets field value +func (o *KubernetesWorkload) SetNamespace(v string) { + o.Namespace = v +} + +// GetKind returns the Kind field value +func (o *KubernetesWorkload) GetKind() string { + if o == nil { + var ret string + return ret + } + + return o.Kind +} + +// GetKindOk returns a tuple with the Kind field value +// and a boolean to check if the value has been set. +func (o *KubernetesWorkload) GetKindOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Kind, true +} + +// SetKind sets field value +func (o *KubernetesWorkload) SetKind(v string) { + o.Kind = v +} + +// GetReplicas returns the Replicas field value +func (o *KubernetesWorkload) GetReplicas() int32 { + if o == nil { + var ret int32 + return ret + } + + return o.Replicas +} + +// GetReplicasOk returns a tuple with the Replicas field value +// and a boolean to check if the value has been set. +func (o *KubernetesWorkload) GetReplicasOk() (*int32, bool) { + if o == nil { + return nil, false + } + return &o.Replicas, true +} + +// SetReplicas sets field value +func (o *KubernetesWorkload) SetReplicas(v int32) { + o.Replicas = v +} + +// GetContainers returns the Containers field value +func (o *KubernetesWorkload) GetContainers() []Container { + if o == nil { + var ret []Container + return ret + } + + return o.Containers +} + +// GetContainersOk returns a tuple with the Containers field value +// and a boolean to check if the value has been set. +func (o *KubernetesWorkload) GetContainersOk() ([]Container, bool) { + if o == nil { + return nil, false + } + return o.Containers, true +} + +// SetContainers sets field value +func (o *KubernetesWorkload) SetContainers(v []Container) { + o.Containers = v +} + +func (o KubernetesWorkload) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["name"] = o.Name + } + if true { + toSerialize["namespace"] = o.Namespace + } + if true { + toSerialize["kind"] = o.Kind + } + if true { + toSerialize["replicas"] = o.Replicas + } + if true { + toSerialize["containers"] = o.Containers + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return json.Marshal(toSerialize) +} + +func (o *KubernetesWorkload) UnmarshalJSON(bytes []byte) (err error) { + varKubernetesWorkload := _KubernetesWorkload{} + + if err = json.Unmarshal(bytes, &varKubernetesWorkload); err == nil { + *o = KubernetesWorkload(varKubernetesWorkload) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + delete(additionalProperties, "name") + delete(additionalProperties, "namespace") + delete(additionalProperties, "kind") + delete(additionalProperties, "replicas") + delete(additionalProperties, "containers") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableKubernetesWorkload struct { + value *KubernetesWorkload + isSet bool +} + +func (v NullableKubernetesWorkload) Get() *KubernetesWorkload { + return v.value +} + +func (v *NullableKubernetesWorkload) Set(val *KubernetesWorkload) { + v.value = val + v.isSet = true +} + +func (v NullableKubernetesWorkload) IsSet() bool { + return v.isSet +} + +func (v *NullableKubernetesWorkload) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableKubernetesWorkload(val *KubernetesWorkload) *NullableKubernetesWorkload { + return &NullableKubernetesWorkload{value: val, isSet: true} +} + +func (v NullableKubernetesWorkload) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableKubernetesWorkload) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/src/pkg/data/consumers/governor/go-client/model_match_label.go b/src/pkg/data/consumers/governor/go-client/model_match_label.go new file mode 100644 index 00000000..8bf0fb5c --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/model_match_label.go @@ -0,0 +1,166 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" +) + +// MatchLabel Resources with matching label on which agent can scan workloads +type MatchLabel struct { + // MatchLabel key + Key string `json:"key"` + // MatchLabel value + Value string `json:"value"` + AdditionalProperties map[string]interface{} +} + +type _MatchLabel MatchLabel + +// NewMatchLabel instantiates a new MatchLabel object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewMatchLabel(key string, value string) *MatchLabel { + this := MatchLabel{} + this.Key = key + this.Value = value + return &this +} + +// NewMatchLabelWithDefaults instantiates a new MatchLabel object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewMatchLabelWithDefaults() *MatchLabel { + this := MatchLabel{} + return &this +} + +// GetKey returns the Key field value +func (o *MatchLabel) GetKey() string { + if o == nil { + var ret string + return ret + } + + return o.Key +} + +// GetKeyOk returns a tuple with the Key field value +// and a boolean to check if the value has been set. +func (o *MatchLabel) GetKeyOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Key, true +} + +// SetKey sets field value +func (o *MatchLabel) SetKey(v string) { + o.Key = v +} + +// GetValue returns the Value field value +func (o *MatchLabel) GetValue() string { + if o == nil { + var ret string + return ret + } + + return o.Value +} + +// GetValueOk returns a tuple with the Value field value +// and a boolean to check if the value has been set. +func (o *MatchLabel) GetValueOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Value, true +} + +// SetValue sets field value +func (o *MatchLabel) SetValue(v string) { + o.Value = v +} + +func (o MatchLabel) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["key"] = o.Key + } + if true { + toSerialize["value"] = o.Value + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return json.Marshal(toSerialize) +} + +func (o *MatchLabel) UnmarshalJSON(bytes []byte) (err error) { + varMatchLabel := _MatchLabel{} + + if err = json.Unmarshal(bytes, &varMatchLabel); err == nil { + *o = MatchLabel(varMatchLabel) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + delete(additionalProperties, "key") + delete(additionalProperties, "value") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableMatchLabel struct { + value *MatchLabel + isSet bool +} + +func (v NullableMatchLabel) Get() *MatchLabel { + return v.value +} + +func (v *NullableMatchLabel) Set(val *MatchLabel) { + v.value = val + v.isSet = true +} + +func (v NullableMatchLabel) IsSet() bool { + return v.isSet +} + +func (v *NullableMatchLabel) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableMatchLabel(val *MatchLabel) *NullableMatchLabel { + return &NullableMatchLabel{value: val, isSet: true} +} + +func (v NullableMatchLabel) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableMatchLabel) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/src/pkg/data/consumers/governor/go-client/model_register_kubernetes_cluster_request.go b/src/pkg/data/consumers/governor/go-client/model_register_kubernetes_cluster_request.go new file mode 100644 index 00000000..7bf5e2ff --- /dev/null +++ b/src/pkg/data/consumers/governor/go-client/model_register_kubernetes_cluster_request.go @@ -0,0 +1,135 @@ +/* +Catalog Governor Service REST API + +This is the service to track assets deployed in customer clusters + +API version: 0.1.0 +Contact: content-building-ecosystem@vmware.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package openapi + +import ( + "encoding/json" +) + +// RegisterKubernetesClusterRequest Payload to request registering a Kubernetes cluster +type RegisterKubernetesClusterRequest struct { + // Name of the cluster to be registered + Name string `json:"name"` + AdditionalProperties map[string]interface{} +} + +type _RegisterKubernetesClusterRequest RegisterKubernetesClusterRequest + +// NewRegisterKubernetesClusterRequest instantiates a new RegisterKubernetesClusterRequest object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewRegisterKubernetesClusterRequest(name string) *RegisterKubernetesClusterRequest { + this := RegisterKubernetesClusterRequest{} + this.Name = name + return &this +} + +// NewRegisterKubernetesClusterRequestWithDefaults instantiates a new RegisterKubernetesClusterRequest object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewRegisterKubernetesClusterRequestWithDefaults() *RegisterKubernetesClusterRequest { + this := RegisterKubernetesClusterRequest{} + return &this +} + +// GetName returns the Name field value +func (o *RegisterKubernetesClusterRequest) GetName() string { + if o == nil { + var ret string + return ret + } + + return o.Name +} + +// GetNameOk returns a tuple with the Name field value +// and a boolean to check if the value has been set. +func (o *RegisterKubernetesClusterRequest) GetNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Name, true +} + +// SetName sets field value +func (o *RegisterKubernetesClusterRequest) SetName(v string) { + o.Name = v +} + +func (o RegisterKubernetesClusterRequest) MarshalJSON() ([]byte, error) { + toSerialize := map[string]interface{}{} + if true { + toSerialize["name"] = o.Name + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return json.Marshal(toSerialize) +} + +func (o *RegisterKubernetesClusterRequest) UnmarshalJSON(bytes []byte) (err error) { + varRegisterKubernetesClusterRequest := _RegisterKubernetesClusterRequest{} + + if err = json.Unmarshal(bytes, &varRegisterKubernetesClusterRequest); err == nil { + *o = RegisterKubernetesClusterRequest(varRegisterKubernetesClusterRequest) + } + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(bytes, &additionalProperties); err == nil { + delete(additionalProperties, "name") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableRegisterKubernetesClusterRequest struct { + value *RegisterKubernetesClusterRequest + isSet bool +} + +func (v NullableRegisterKubernetesClusterRequest) Get() *RegisterKubernetesClusterRequest { + return v.value +} + +func (v *NullableRegisterKubernetesClusterRequest) Set(val *RegisterKubernetesClusterRequest) { + v.value = val + v.isSet = true +} + +func (v NullableRegisterKubernetesClusterRequest) IsSet() bool { + return v.isSet +} + +func (v *NullableRegisterKubernetesClusterRequest) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableRegisterKubernetesClusterRequest(val *RegisterKubernetesClusterRequest) *NullableRegisterKubernetesClusterRequest { + return &NullableRegisterKubernetesClusterRequest{value: val, isSet: true} +} + +func (v NullableRegisterKubernetesClusterRequest) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableRegisterKubernetesClusterRequest) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/src/pkg/data/consumers/governor/mock_client.go b/src/pkg/data/consumers/governor/mock_client.go index b3570d8e..1738bce6 100644 --- a/src/pkg/data/consumers/governor/mock_client.go +++ b/src/pkg/data/consumers/governor/mock_client.go @@ -2,7 +2,7 @@ package consumers import ( "context" - openapi "gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client" + openapi "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/governor/go-client" "net/http" ) diff --git a/src/pkg/inspection/controller.go b/src/pkg/inspection/controller.go index 3733c6d7..6617f0b7 100644 --- a/src/pkg/inspection/controller.go +++ b/src/pkg/inspection/controller.go @@ -8,8 +8,8 @@ import ( "github.com/vmware-tanzu/cloud-native-security-inspector/src/lib/log" es "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/es" governor "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/governor" + openapi "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/governor/go-client" osearch "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/opensearch" - openapi "gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" "time" From 2e13bcf424b91f9943ac5ed5b3e20721cfe3b7f2 Mon Sep 17 00:00:00 2001 From: Nootan Singh Date: Thu, 23 Feb 2023 17:16:52 +0530 Subject: [PATCH 4/6] Added server url of governor api Signed-off-by: Nootan Singh --- src/config/samples/policy.yaml | 2 +- src/go.mod | 3 +- src/go.sum | 3 - src/pkg/data/consumers/governor/exporter.go | 20 +- .../data/consumers/governor/exporter_test.go | 162 +++++++--- .../data/consumers/governor/mock_client.go | 74 ----- .../governor/mock_cluster_client_api.go | 297 ++++++++++++++++++ src/pkg/inspection/controller.go | 4 +- 8 files changed, 433 insertions(+), 132 deletions(-) delete mode 100644 src/pkg/data/consumers/governor/mock_client.go create mode 100644 src/pkg/data/consumers/governor/mock_cluster_client_api.go diff --git a/src/config/samples/policy.yaml b/src/config/samples/policy.yaml index b53c7615..0970ad73 100644 --- a/src/config/samples/policy.yaml +++ b/src/config/samples/policy.yaml @@ -60,7 +60,7 @@ spec: governor: enabled: true clusterId: "65a03970-c53a-4ba1-8d1f-42c9f95d2761" - url: "app-catalog.vmware.com/api" + url: "https://api.int.app-catalog.vmware.com/catalog-governor/v1" apiToken: "n6yDMkMEghPUYJDGsn39I_GYNnPh4Vi-LnaH2URjpgwcXbFIVYzMU-n8LRzTJGKO" baselines: - kind: "vulnerability" diff --git a/src/go.mod b/src/go.mod index 17e9c122..d2543355 100644 --- a/src/go.mod +++ b/src/go.mod @@ -21,6 +21,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/spf13/viper v1.14.0 github.com/stretchr/testify v1.8.1 + golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 k8s.io/api v0.25.4 k8s.io/apimachinery v0.25.4 k8s.io/apiserver v0.25.4 @@ -117,6 +118,7 @@ require ( github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.0 // indirect github.com/subosito/gotenv v1.4.1 // indirect github.com/ugorji/go/codec v1.2.7 // indirect github.com/vladimirvivien/gexe v0.1.1 // indirect @@ -138,7 +140,6 @@ require ( go.uber.org/zap v1.23.0 // indirect golang.org/x/crypto v0.1.0 // indirect golang.org/x/net v0.2.0 // indirect - golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect golang.org/x/sys v0.2.0 // indirect golang.org/x/term v0.2.0 // indirect golang.org/x/text v0.4.0 // indirect diff --git a/src/go.sum b/src/go.sum index 4607634d..6c0a0854 100644 --- a/src/go.sum +++ b/src/go.sum @@ -638,8 +638,6 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client v0.0.0-20230217132810-674c08a7361b h1:jW9yVVi1meezoQI1h12LVDKu6QgIbZTOtKEBNEwD08A= -gitlab.eng.vmware.com/vac/catalog-governor/api-specs/catalog-governor-service-rest/go-client v0.0.0-20230217132810-674c08a7361b/go.mod h1:Z3g5cIHSpGMDg7W8dXek1xsxZn4ZsOqNCu5vBXY0DXM= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY= @@ -811,7 +809,6 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 h1:nt+Q6cXKz4MosCSpnbMtqiQ8Oz0pxTef2B4Vca2lvfk= diff --git a/src/pkg/data/consumers/governor/exporter.go b/src/pkg/data/consumers/governor/exporter.go index 5a4b700c..a7a1bb25 100644 --- a/src/pkg/data/consumers/governor/exporter.go +++ b/src/pkg/data/consumers/governor/exporter.go @@ -20,8 +20,9 @@ type GovernorExporter struct { // SendReportToGovernor is used to send report to governor url http end point. func (g GovernorExporter) SendReportToGovernor() error { // Get governor api request model from assessment report. - kubernetesCluster := getGovernorAPIPayload(*g.Report) - + kubernetesCluster := g.getGovernorAPIPayload() + log.Info("Payload data for governor:") + log.Info(kubernetesCluster) apiSaveClusterRequest := g.ApiClient.ClustersApi.UpdateTelemetry(context.Background(), g.ClusterID).KubernetesTelemetryRequest(kubernetesCluster) // Call api cluster to send telemetry data and get response. @@ -30,7 +31,11 @@ func (g GovernorExporter) SendReportToGovernor() error { log.Errorf("Governor api response error: %v", err) return err } - if response.StatusCode != http.StatusOK { + + log.Info("successful called governor api") + log.Info(response) + + if response.StatusCode != http.StatusNoContent { log.Errorf("Governor api response status: %v", response.StatusCode) return errors.New(fmt.Sprintf("Governor api response status: %s", response.Status)) } @@ -39,16 +44,15 @@ func (g GovernorExporter) SendReportToGovernor() error { } // getGovernorAPIPayload is used to map assessment report to client model. -func getGovernorAPIPayload(doc api.AssessmentReport) openapi.KubernetesTelemetryRequest { +func (g GovernorExporter) getGovernorAPIPayload() openapi.KubernetesTelemetryRequest { kubernetesCluster := openapi.NewKubernetesTelemetryRequestWithDefaults() - for _, nsa := range doc.Spec.NamespaceAssessments { + + for _, nsa := range g.Report.Spec.NamespaceAssessments { for _, workloadAssessment := range nsa.WorkloadAssessments { kubernetesWorkloads := openapi.NewKubernetesWorkloadWithDefaults() kubernetesWorkloads.Name = workloadAssessment.Workload.Name kubernetesWorkloads.Kind = workloadAssessment.Workload.Kind - if nsa.Namespace.Name != "" { - kubernetesWorkloads.Namespace = nsa.Namespace.Name - } + kubernetesWorkloads.Namespace = nsa.Namespace.Name kubernetesWorkloads.Replicas = workloadAssessment.Workload.Replicas for _, pod := range workloadAssessment.Workload.Pods { containerData := openapi.NewContainerWithDefaults() diff --git a/src/pkg/data/consumers/governor/exporter_test.go b/src/pkg/data/consumers/governor/exporter_test.go index 2825b58d..2566ca33 100644 --- a/src/pkg/data/consumers/governor/exporter_test.go +++ b/src/pkg/data/consumers/governor/exporter_test.go @@ -1,64 +1,138 @@ package consumers import ( + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" api "github.com/vmware-tanzu/cloud-native-security-inspector/src/api/v1alpha1" openapi "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/governor/go-client" v1 "k8s.io/api/core/v1" - "os" + "net/http" "testing" ) var ( - mockClient *openapi.APIClient - clusterID = "testingId" - apiToken = "apiToken" - namespace = "testingNamespace" - name = "name" - image = "image" - imageID = "imageId" - replicaCount = 2 + clusterID = "testingId" + apiToken = "apiToken" + namespace = "testingNamespace" + name = "name" + image = "image" + imageID = "imageId" + replicaCount = 2 + testHeader = "testHeader" + testHeaderValue = "testHeaderValue" ) const ( - testHost = "clusterapi.swagger.io:80" - testScheme = "http" + testHost = "clusterapi.swagger.io:80" + testInvalidURL = "asdfdasfasv.sadfdsf" + testScheme = "http" ) -func TestMain(m *testing.M) { - cfg := openapi.NewConfiguration() - cfg.AddDefaultHeader("testheader", "testvalue") - cfg.Host = testHost - cfg.Scheme = testScheme - mockClient = openapi.NewAPIClient(cfg) - retCode := m.Run() - os.Exit(retCode) -} +func TestSendReportToGovernor(t *testing.T) { + testDataStruct := []struct { + testCaseDescription string + testHost string + testHeader string + testHeaderValue string + testReportData *api.AssessmentReport + testClusterID string + testAPIToken string + testStatusCode int + }{ + { + testCaseDescription: "Success: Happy flow end to end.", + testHost: testHost, + testHeader: testHeader, + testHeaderValue: "testvalue", + testReportData: &api.AssessmentReport{ + Spec: api.AssessmentReportSpec{NamespaceAssessments: []*api.NamespaceAssessment{{Namespace: v1.LocalObjectReference{ + Name: namespace, + }, + WorkloadAssessments: []*api.WorkloadAssessment{{Workload: api.Workload{Replicas: int32(replicaCount), + Pods: []*api.Pod{{Containers: []*api.Container{{ + Name: name, + Image: image, + ImageID: imageID, + }}}}}}}}}}}, + testClusterID: clusterID, + testAPIToken: apiToken, + testStatusCode: http.StatusNoContent, + }, + { + testCaseDescription: "Success: Empty payload, successful case", + testHost: testHost, + testHeader: testHeader, + testHeaderValue: testHeaderValue, + testReportData: &api.AssessmentReport{}, + testClusterID: clusterID, + testAPIToken: apiToken, + testStatusCode: http.StatusNoContent, + }, + { + testCaseDescription: "Failure: Error from API call.", + testHost: testHost, + testHeader: testHeader, + testHeaderValue: testHeaderValue, + testReportData: &api.AssessmentReport{}, + testClusterID: clusterID, + testAPIToken: apiToken, + testStatusCode: http.StatusInternalServerError, + }, + { + testCaseDescription: "Failure Invalid URL: Error from api.", + testHost: testInvalidURL, + testHeader: testHeader, + testHeaderValue: testHeaderValue, + testReportData: &api.AssessmentReport{}, + testClusterID: clusterID, + testAPIToken: apiToken, + testStatusCode: http.StatusBadRequest, + }, + { + testCaseDescription: "Failure: Timeout to receive response from api.", + testHost: testHost, + testHeader: testHeader, + testHeaderValue: testHeaderValue, + testReportData: &api.AssessmentReport{}, + testClusterID: clusterID, + testAPIToken: apiToken, + testStatusCode: http.StatusRequestTimeout, + }, + } -func TestSendReportToGovernorSuccess(t *testing.T) { - actualApi := mockClient.ClustersApi - mockApi := NewMockClustersApi() - mockClient.ClustersApi = mockApi + for _, tt := range testDataStruct { + t.Run(tt.testCaseDescription, func(t *testing.T) { + var clusterClient *openapi.APIClient + mockConfig := openapi.NewConfiguration() + mockConfig.AddDefaultHeader(tt.testHeader, tt.testHeaderValue) + mockConfig.Host = tt.testHost + mockConfig.Scheme = testScheme + clusterClient = openapi.NewAPIClient(mockConfig) - g := GovernorExporter{ - Report: &api.AssessmentReport{ - Spec: api.AssessmentReportSpec{NamespaceAssessments: []*api.NamespaceAssessment{{Namespace: v1.LocalObjectReference{ - Name: namespace, - }, - WorkloadAssessments: []*api.WorkloadAssessment{{Workload: api.Workload{Replicas: int32(replicaCount), - Pods: []*api.Pod{{Containers: []*api.Container{{ - Name: name, - Image: image, - ImageID: imageID, - }}}}}}}}}}}, - ClusterID: clusterID, - ApiToken: apiToken, - ApiClient: mockClient, - } + g := GovernorExporter{ + Report: tt.testReportData, + ApiClient: clusterClient, + ApiToken: apiToken, + ClusterID: tt.testClusterID, + } + mockAPIClient := new(ClustersApi) - errFromSendReportToGovernor := g.SendReportToGovernor() - if errFromSendReportToGovernor != nil { - t.Fatalf("Error while updating telemetry data of workloads in cluster: %v", errFromSendReportToGovernor) - } - mockClient.ClustersApi = actualApi + response := openapi.ApiUpdateTelemetryRequest{ + ApiService: mockAPIClient, + } + clusterClient.ClustersApi = mockAPIClient + response.KubernetesTelemetryRequest(g.getGovernorAPIPayload()) + mockAPIClient.On("UpdateTelemetry", mock.Anything, mock.Anything).Return(response) + mockAPIClient.On("UpdateTelemetryExecute", mock.Anything).Return(&http.Response{ + StatusCode: tt.testStatusCode, + }, nil) + errFromSendReportToGovernor := g.SendReportToGovernor() + if tt.testStatusCode != http.StatusNoContent { + assert.Error(t, errFromSendReportToGovernor) + } else { + assert.NoError(t, errFromSendReportToGovernor) + } + }) + } } diff --git a/src/pkg/data/consumers/governor/mock_client.go b/src/pkg/data/consumers/governor/mock_client.go deleted file mode 100644 index 1738bce6..00000000 --- a/src/pkg/data/consumers/governor/mock_client.go +++ /dev/null @@ -1,74 +0,0 @@ -package consumers - -import ( - "context" - openapi "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/governor/go-client" - "net/http" -) - -// MockClustersApi is a mock of the ClustersApi interface -type MockClustersApi struct { -} - -// NewMockClustersApi creates a new mock instance -func NewMockClustersApi() *MockClustersApi { - return &MockClustersApi{} -} - -func (m *MockClustersApi) FetchAgentConfig(ctx context.Context, clusterId string) openapi.ApiFetchAgentConfigRequest { - //TODO implement me - panic("implement me") -} - -func (m *MockClustersApi) FetchAgentConfigExecute(r openapi.ApiFetchAgentConfigRequest) (string, *http.Response, error) { - //TODO implement me - panic("implement me") -} - -func (m *MockClustersApi) GetCluster(ctx context.Context, clusterId string) openapi.ApiGetClusterRequest { - //TODO implement me - panic("implement me") -} - -func (m *MockClustersApi) GetClusterExecute(r openapi.ApiGetClusterRequest) (*openapi.KubernetesClusterDetailedResponse, *http.Response, error) { - //TODO implement me - panic("implement me") -} - -func (m *MockClustersApi) GetClusters(ctx context.Context) openapi.ApiGetClustersRequest { - //TODO implement me - panic("implement me") -} - -func (m *MockClustersApi) GetClustersExecute(r openapi.ApiGetClustersRequest) ([]openapi.KubernetesClusterResponse, *http.Response, error) { - //TODO implement me - panic("implement me") -} - -func (m *MockClustersApi) RegisterCluster(ctx context.Context) openapi.ApiRegisterClusterRequest { - //TODO implement me - panic("implement me") -} - -func (m *MockClustersApi) RegisterClusterExecute(r openapi.ApiRegisterClusterRequest) (*http.Response, error) { - //TODO implement me - panic("implement me") -} - -func (m *MockClustersApi) UnregisterCluster(ctx context.Context, clusterId string) openapi.ApiUnregisterClusterRequest { - //TODO implement me - panic("implement me") -} - -func (m *MockClustersApi) UnregisterClusterExecute(r openapi.ApiUnregisterClusterRequest) (*http.Response, error) { - //TODO implement me - panic("implement me") -} - -func (m *MockClustersApi) UpdateTelemetry(ctx context.Context, clusterId string) openapi.ApiUpdateTelemetryRequest { - return openapi.ApiUpdateTelemetryRequest{ApiService: m} -} - -func (m *MockClustersApi) UpdateTelemetryExecute(r openapi.ApiUpdateTelemetryRequest) (*http.Response, error) { - return &http.Response{StatusCode: 200}, nil -} diff --git a/src/pkg/data/consumers/governor/mock_cluster_client_api.go b/src/pkg/data/consumers/governor/mock_cluster_client_api.go new file mode 100644 index 00000000..b058ce5c --- /dev/null +++ b/src/pkg/data/consumers/governor/mock_cluster_client_api.go @@ -0,0 +1,297 @@ +// Code generated by mockery v2.20.2. DO NOT EDIT. + +package consumers + +import ( + context "context" + http "net/http" + + mock "github.com/stretchr/testify/mock" + + openapi "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/governor/go-client" +) + +// ClustersApi is an autogenerated mock type for the ClustersApi type +type ClustersApi struct { + mock.Mock +} + +// FetchAgentConfig provides a mock function with given fields: ctx, clusterId +func (_m *ClustersApi) FetchAgentConfig(ctx context.Context, clusterId string) openapi.ApiFetchAgentConfigRequest { + ret := _m.Called(ctx, clusterId) + + var r0 openapi.ApiFetchAgentConfigRequest + if rf, ok := ret.Get(0).(func(context.Context, string) openapi.ApiFetchAgentConfigRequest); ok { + r0 = rf(ctx, clusterId) + } else { + r0 = ret.Get(0).(openapi.ApiFetchAgentConfigRequest) + } + + return r0 +} + +// FetchAgentConfigExecute provides a mock function with given fields: r +func (_m *ClustersApi) FetchAgentConfigExecute(r openapi.ApiFetchAgentConfigRequest) (string, *http.Response, error) { + ret := _m.Called(r) + + var r0 string + var r1 *http.Response + var r2 error + if rf, ok := ret.Get(0).(func(openapi.ApiFetchAgentConfigRequest) (string, *http.Response, error)); ok { + return rf(r) + } + if rf, ok := ret.Get(0).(func(openapi.ApiFetchAgentConfigRequest) string); ok { + r0 = rf(r) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(openapi.ApiFetchAgentConfigRequest) *http.Response); ok { + r1 = rf(r) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*http.Response) + } + } + + if rf, ok := ret.Get(2).(func(openapi.ApiFetchAgentConfigRequest) error); ok { + r2 = rf(r) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// GetCluster provides a mock function with given fields: ctx, clusterId +func (_m *ClustersApi) GetCluster(ctx context.Context, clusterId string) openapi.ApiGetClusterRequest { + ret := _m.Called(ctx, clusterId) + + var r0 openapi.ApiGetClusterRequest + if rf, ok := ret.Get(0).(func(context.Context, string) openapi.ApiGetClusterRequest); ok { + r0 = rf(ctx, clusterId) + } else { + r0 = ret.Get(0).(openapi.ApiGetClusterRequest) + } + + return r0 +} + +// GetClusterExecute provides a mock function with given fields: r +func (_m *ClustersApi) GetClusterExecute(r openapi.ApiGetClusterRequest) (*openapi.KubernetesClusterDetailedResponse, *http.Response, error) { + ret := _m.Called(r) + + var r0 *openapi.KubernetesClusterDetailedResponse + var r1 *http.Response + var r2 error + if rf, ok := ret.Get(0).(func(openapi.ApiGetClusterRequest) (*openapi.KubernetesClusterDetailedResponse, *http.Response, error)); ok { + return rf(r) + } + if rf, ok := ret.Get(0).(func(openapi.ApiGetClusterRequest) *openapi.KubernetesClusterDetailedResponse); ok { + r0 = rf(r) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*openapi.KubernetesClusterDetailedResponse) + } + } + + if rf, ok := ret.Get(1).(func(openapi.ApiGetClusterRequest) *http.Response); ok { + r1 = rf(r) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*http.Response) + } + } + + if rf, ok := ret.Get(2).(func(openapi.ApiGetClusterRequest) error); ok { + r2 = rf(r) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// GetClusters provides a mock function with given fields: ctx +func (_m *ClustersApi) GetClusters(ctx context.Context) openapi.ApiGetClustersRequest { + ret := _m.Called(ctx) + + var r0 openapi.ApiGetClustersRequest + if rf, ok := ret.Get(0).(func(context.Context) openapi.ApiGetClustersRequest); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(openapi.ApiGetClustersRequest) + } + + return r0 +} + +// GetClustersExecute provides a mock function with given fields: r +func (_m *ClustersApi) GetClustersExecute(r openapi.ApiGetClustersRequest) ([]openapi.KubernetesClusterResponse, *http.Response, error) { + ret := _m.Called(r) + + var r0 []openapi.KubernetesClusterResponse + var r1 *http.Response + var r2 error + if rf, ok := ret.Get(0).(func(openapi.ApiGetClustersRequest) ([]openapi.KubernetesClusterResponse, *http.Response, error)); ok { + return rf(r) + } + if rf, ok := ret.Get(0).(func(openapi.ApiGetClustersRequest) []openapi.KubernetesClusterResponse); ok { + r0 = rf(r) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]openapi.KubernetesClusterResponse) + } + } + + if rf, ok := ret.Get(1).(func(openapi.ApiGetClustersRequest) *http.Response); ok { + r1 = rf(r) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*http.Response) + } + } + + if rf, ok := ret.Get(2).(func(openapi.ApiGetClustersRequest) error); ok { + r2 = rf(r) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// RegisterCluster provides a mock function with given fields: ctx +func (_m *ClustersApi) RegisterCluster(ctx context.Context) openapi.ApiRegisterClusterRequest { + ret := _m.Called(ctx) + + var r0 openapi.ApiRegisterClusterRequest + if rf, ok := ret.Get(0).(func(context.Context) openapi.ApiRegisterClusterRequest); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(openapi.ApiRegisterClusterRequest) + } + + return r0 +} + +// RegisterClusterExecute provides a mock function with given fields: r +func (_m *ClustersApi) RegisterClusterExecute(r openapi.ApiRegisterClusterRequest) (*http.Response, error) { + ret := _m.Called(r) + + var r0 *http.Response + var r1 error + if rf, ok := ret.Get(0).(func(openapi.ApiRegisterClusterRequest) (*http.Response, error)); ok { + return rf(r) + } + if rf, ok := ret.Get(0).(func(openapi.ApiRegisterClusterRequest) *http.Response); ok { + r0 = rf(r) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*http.Response) + } + } + + if rf, ok := ret.Get(1).(func(openapi.ApiRegisterClusterRequest) error); ok { + r1 = rf(r) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UnregisterCluster provides a mock function with given fields: ctx, clusterId +func (_m *ClustersApi) UnregisterCluster(ctx context.Context, clusterId string) openapi.ApiUnregisterClusterRequest { + ret := _m.Called(ctx, clusterId) + + var r0 openapi.ApiUnregisterClusterRequest + if rf, ok := ret.Get(0).(func(context.Context, string) openapi.ApiUnregisterClusterRequest); ok { + r0 = rf(ctx, clusterId) + } else { + r0 = ret.Get(0).(openapi.ApiUnregisterClusterRequest) + } + + return r0 +} + +// UnregisterClusterExecute provides a mock function with given fields: r +func (_m *ClustersApi) UnregisterClusterExecute(r openapi.ApiUnregisterClusterRequest) (*http.Response, error) { + ret := _m.Called(r) + + var r0 *http.Response + var r1 error + if rf, ok := ret.Get(0).(func(openapi.ApiUnregisterClusterRequest) (*http.Response, error)); ok { + return rf(r) + } + if rf, ok := ret.Get(0).(func(openapi.ApiUnregisterClusterRequest) *http.Response); ok { + r0 = rf(r) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*http.Response) + } + } + + if rf, ok := ret.Get(1).(func(openapi.ApiUnregisterClusterRequest) error); ok { + r1 = rf(r) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UpdateTelemetry provides a mock function with given fields: ctx, clusterId +func (_m *ClustersApi) UpdateTelemetry(ctx context.Context, clusterId string) openapi.ApiUpdateTelemetryRequest { + ret := _m.Called(ctx, clusterId) + + var r0 openapi.ApiUpdateTelemetryRequest + if rf, ok := ret.Get(0).(func(context.Context, string) openapi.ApiUpdateTelemetryRequest); ok { + r0 = rf(ctx, clusterId) + } else { + r0 = ret.Get(0).(openapi.ApiUpdateTelemetryRequest) + } + + return r0 +} + +// UpdateTelemetryExecute provides a mock function with given fields: r +func (_m *ClustersApi) UpdateTelemetryExecute(r openapi.ApiUpdateTelemetryRequest) (*http.Response, error) { + ret := _m.Called(r) + + var r0 *http.Response + var r1 error + if rf, ok := ret.Get(0).(func(openapi.ApiUpdateTelemetryRequest) (*http.Response, error)); ok { + return rf(r) + } + if rf, ok := ret.Get(0).(func(openapi.ApiUpdateTelemetryRequest) *http.Response); ok { + r0 = rf(r) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*http.Response) + } + } + + if rf, ok := ret.Get(1).(func(openapi.ApiUpdateTelemetryRequest) error); ok { + r1 = rf(r) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewClustersApi interface { + mock.TestingT + Cleanup(func()) +} + +// NewClustersApi creates a new instance of ClustersApi. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewClustersApi(t mockConstructorTestingTNewClustersApi) *ClustersApi { + mock := &ClustersApi{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/src/pkg/inspection/controller.go b/src/pkg/inspection/controller.go index 6617f0b7..75299055 100644 --- a/src/pkg/inspection/controller.go +++ b/src/pkg/inspection/controller.go @@ -342,6 +342,7 @@ func (c *controller) Run(ctx context.Context, policy *v1alpha1.InspectionPolicy) return errors.New("Either ClusterID or URL or APIToken is empty") } + log.Info("Calling governor exporter") if exporterErr := exportReportToGovernor(report, policy); exporterErr != nil { log.Errorf("Error from exporter: %v", exporterErr) return exporterErr @@ -377,7 +378,8 @@ func exportReportToGovernor(report *v1alpha1.AssessmentReport, policy *v1alpha1. // Create api client to governor api. config := openapi.NewConfiguration() - config.Host = governorConfig.URL + //config.Host = governorConfig.URL + config.Servers = openapi.ServerConfigurations{{URL: governorConfig.URL}} apiClient := openapi.NewAPIClient(config) exporter := governor.GovernorExporter{ From 1ded538e3914450b71e1497cca7af256d3f53ac0 Mon Sep 17 00:00:00 2001 From: harshsharma071988 <125853522+harshsharma071988@users.noreply.github.com> Date: Tue, 28 Feb 2023 19:23:05 +0530 Subject: [PATCH 5/6] feat/csp_implementation (#1) * Add governor api call provision to send cluster telemetry data Signed-off-by: Nootan Singh * Added clusterId, url and apiToken as part of governor policy Signed-off-by: Nootan Singh * [BEGONIA-62] ADD CSP support in agent * [BEGONIA-71] Add CSP implementation --> Added code to get CSP api-token from secret --> Added code to send request to CSP and get access token for governor backend --> Added code to send token with every request to governor backend * refactored some code * removed unused file * removed unused file 1 * removed unused file 2 * refactored some code * removed unwanted changes * removed gitignore file changes * ran make manifests to generate manifests automatically * removed dependecy on csp gitlab library * Added UTs and addressed comments Signed-off-by: Nootan Singh * added UTs Signed-off-by: harshsharma071988 * setting correct context while update telemetry Signed-off-by: harshsharma071988 * fixed governor url config and governor api response status Signed-off-by: harshsharma071988 * Added server url of governor api Signed-off-by: Nootan Singh * changed accessSecret to API_TOKEN Signed-off-by: harshsharma071988 * handled empty namespace case and empty workload case * Added UTs for new files * Resolved minor comments * resolved minor comments Signed-off-by: harshsharma071988 * removed compilation issue with %w Signed-off-by: harshsharma071988 * CSP Refresh used using config maps + UTs added * removed fmt and used log Signed-off-by: harshsharma071988 * Used Errorf in place of Error for logging Signed-off-by: harshsharma071988 * changed comment Signed-off-by: harshsharma071988 * Used Secret in place of ConfigMap for storing access token of governor. --------- Signed-off-by: Nootan Singh Signed-off-by: harshsharma071988 Co-authored-by: Nootan Singh --- src/api/v1alpha1/inspectionpolicy_types.go | 4 +- src/cmd/inspector/main.go | 5 + ...oharbor.goharbor.io_assessmentreports.yaml | 4 +- ...harbor.goharbor.io_inspectionpolicies.yaml | 4 +- src/go.mod | 1 + src/go.sum | 1 + src/lib/cspauth/csp_auth.go | 115 +++++++++++++++ src/lib/cspauth/csp_auth_test.go | 139 ++++++++++++++++++ src/lib/cspauth/mock_token_manager.go | 30 ++++ src/lib/cspauth/mocks/mock_csp_auth.go | 55 +++++++ src/lib/cspauth/token_manager.go | 117 +++++++++++++++ src/lib/cspauth/token_manager_test.go | 70 +++++++++ src/lib/retry/retry.go | 108 ++++++++++++++ src/lib/retry/retry_test.go | 50 +++++++ src/pkg/data/consumers/governor/exporter.go | 37 ++++- .../data/consumers/governor/exporter_test.go | 89 +++++++++-- src/pkg/inspection/controller.go | 96 +++++++----- 17 files changed, 865 insertions(+), 60 deletions(-) create mode 100644 src/lib/cspauth/csp_auth.go create mode 100644 src/lib/cspauth/csp_auth_test.go create mode 100644 src/lib/cspauth/mock_token_manager.go create mode 100644 src/lib/cspauth/mocks/mock_csp_auth.go create mode 100644 src/lib/cspauth/token_manager.go create mode 100644 src/lib/cspauth/token_manager_test.go create mode 100644 src/lib/retry/retry.go create mode 100644 src/lib/retry/retry_test.go diff --git a/src/api/v1alpha1/inspectionpolicy_types.go b/src/api/v1alpha1/inspectionpolicy_types.go index 7a902f2b..893abb87 100644 --- a/src/api/v1alpha1/inspectionpolicy_types.go +++ b/src/api/v1alpha1/inspectionpolicy_types.go @@ -130,9 +130,9 @@ type Governor struct { // Api url to send telemetry data // +kubebuilder:validation:Optional URL string `json:"url"` - // Api token for user authentication + // Secret name where CSP api token is stored in cnsi-system namespace // +kubebuilder:validation:Optional - APIToken string `json:"apiToken"` + CspSecretName string `json:"cspSecretName"` } // FollowupAction defines what actions should be applied when security expectations are matched. diff --git a/src/cmd/inspector/main.go b/src/cmd/inspector/main.go index ae95c63b..b9e4880b 100644 --- a/src/cmd/inspector/main.go +++ b/src/cmd/inspector/main.go @@ -41,6 +41,7 @@ func main() { k8sClient, err := client.New(ctrl.GetConfigOrDie(), client.Options{ Scheme: scheme, }) + if err != nil { log.Error(err, "unable to create k8s client") os.Exit(1) @@ -56,6 +57,10 @@ func main() { os.Exit(1) } + if inspectionPolicy.Spec.Inspection.Assessment.Governor.Enabled { + ctx = context.WithValue(ctx, "cspSecretName", inspectionPolicy.Spec.Inspection.Assessment.Governor.CspSecretName) + } + runner := inspection.NewController(). WithScheme(scheme). WithK8sClient(k8sClient). diff --git a/src/config/crd/bases/goharbor.goharbor.io_assessmentreports.yaml b/src/config/crd/bases/goharbor.goharbor.io_assessmentreports.yaml index c7eed68a..ca6296bf 100644 --- a/src/config/crd/bases/goharbor.goharbor.io_assessmentreports.yaml +++ b/src/config/crd/bases/goharbor.goharbor.io_assessmentreports.yaml @@ -143,8 +143,8 @@ spec: governor: description: Indicate whether to config of governor properties: - apiToken: - description: Api token for user authentication + cspSecretName: + description: Secret name where CSP api token is stored in cnsi-system namespace type: string clusterId: description: Unique identifier of the cluster diff --git a/src/config/crd/bases/goharbor.goharbor.io_inspectionpolicies.yaml b/src/config/crd/bases/goharbor.goharbor.io_inspectionpolicies.yaml index 760aab35..67397ee0 100644 --- a/src/config/crd/bases/goharbor.goharbor.io_inspectionpolicies.yaml +++ b/src/config/crd/bases/goharbor.goharbor.io_inspectionpolicies.yaml @@ -155,8 +155,8 @@ spec: governor: description: Indicate whether to config of governor properties: - apiToken: - description: Api token for user authentication + cspSecretName: + description: Secret name where CSP api token is stored in cnsi-system namespace type: string clusterId: description: Unique identifier of the cluster diff --git a/src/go.mod b/src/go.mod index d2543355..9868c843 100644 --- a/src/go.mod +++ b/src/go.mod @@ -53,6 +53,7 @@ require ( github.com/docker/distribution v2.8.1+incompatible // indirect github.com/elastic/elastic-transport-go/v8 v8.1.0 // indirect github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/felixge/httpsnoop v1.0.2 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect diff --git a/src/go.sum b/src/go.sum index 6c0a0854..e9b423ea 100644 --- a/src/go.sum +++ b/src/go.sum @@ -151,6 +151,7 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= diff --git a/src/lib/cspauth/csp_auth.go b/src/lib/cspauth/csp_auth.go new file mode 100644 index 00000000..f591e77e --- /dev/null +++ b/src/lib/cspauth/csp_auth.go @@ -0,0 +1,115 @@ +package cspauth + +import ( + "context" + "fmt" + "github.com/vmware-tanzu/cloud-native-security-inspector/src/lib/log" + "github.com/vmware-tanzu/cloud-native-security-inspector/src/lib/retry" + v12 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "math" + "time" +) + +const ( + tokenMaxAgeSeconds = 1700 + apiToken = "API_TOKEN" + accessTokenSecretName = "governor-accesstoken" + governorTokenExpiresIn = "governorAccessTokenExpiresIn" + governorAccessTokenKey = "governorAccessToken" + Retry = 3 +) + +var RetryDelay time.Duration = 5 + +// Provider is an interface to interact with an authorization service +type Provider interface { + // GetBearerToken retrieves a short-lived access token to use in a single HTTP request + GetBearerToken(kubernetes.Interface, context.Context, string, string) (string, error) +} + +type CspAuth struct { + CspClient CSPClient + + apiToken string +} + +func (a *CspAuth) GetBearerToken(clientSet kubernetes.Interface, ctx context.Context, cspSecretNamespace string, cspSecretName string) (string, error) { + accessSecret, err := getOrCreateSecretForAccessToken(clientSet, ctx, cspSecretNamespace) + if err != nil { + return "", err + } + + accessToken := string(accessSecret.Data[governorAccessTokenKey]) + expiresIn := string(accessSecret.Data[governorTokenExpiresIn]) + accessTokenExpiresIn, _ := time.Parse(time.Layout, expiresIn) + + if accessToken == "" || time.Now().After(accessTokenExpiresIn) { + apiToken, err := getCSPTokenFromSecret(clientSet, ctx, cspSecretNamespace, cspSecretName) + if err != nil { + return "", fmt.Errorf("Failed to fetch CSP api-token: %w", err) + } + a.apiToken = apiToken + if err := a.refreshToken(ctx, clientSet, cspSecretNamespace, accessSecret); err != nil { + return "", err + } + } + return string(accessSecret.Data[governorAccessTokenKey]), nil +} + +func (a *CspAuth) refreshToken(ctx context.Context, clientSet kubernetes.Interface, cspSecretNamespace string, accessTokenSecret *v12.Secret) error { + return retry.NewRetry( + retry.WithName("auth token refresh"), + retry.WithMaxAttempts(Retry), + retry.WithIncrementDelay(RetryDelay*time.Second, RetryDelay*time.Second), + ).Run(ctx, func() (bool, error) { + now := time.Now() + cspAuthResponse, err := a.CspClient.GetCspAuthorization(ctx, a.apiToken) + if err != nil { + log.Error(err, "We got an error back from CSP") + return false, nil + } + + expiresIn := time.Duration(math.Min(float64(cspAuthResponse.ExpiresIn), tokenMaxAgeSeconds)) * time.Second + formattedExpiration := now.Add(expiresIn).Format(time.Layout) + + log.Infof("Refreshed access token for governor: %s which expires in %s", cspAuthResponse.AccessToken, formattedExpiration) + accessTokenSecret.Data[governorAccessTokenKey] = []byte(cspAuthResponse.AccessToken) + accessTokenSecret.Data[governorTokenExpiresIn] = []byte(formattedExpiration) + _, err = clientSet.CoreV1().Secrets(cspSecretNamespace).Update(ctx, accessTokenSecret, v1.UpdateOptions{}) + if err != nil { + log.Error(err, "We got an error updating access token secret") + return false, nil + } + log.Infof("Obtained CSP access token, next refresh in %s\n", expiresIn) + return true, nil + }) +} + +func getCSPTokenFromSecret(clientSet kubernetes.Interface, ctx context.Context, ns string, secretName string) (string, error) { + secret, err := clientSet.CoreV1().Secrets(ns).Get(ctx, secretName, v1.GetOptions{}) + if err != nil { + log.Error(err, "Failed to fetch secret") + return "", err + } + cspApiToken := string(secret.Data[apiToken]) + return cspApiToken, err +} + +func getOrCreateSecretForAccessToken(clientSet kubernetes.Interface, ctx context.Context, ns string) (*v12.Secret, error) { + secret, err := clientSet.CoreV1().Secrets(ns).Get(ctx, accessTokenSecretName, v1.GetOptions{}) + if err != nil { + log.Warning(err, "Failed to fetch secret for access token, Now Trying to create new secret for same") + secret = &v12.Secret{} + secret.Name = accessTokenSecretName + secret.Namespace = ns + secret.Data = map[string][]byte{} + secret, err = clientSet.CoreV1().Secrets(ns).Create(ctx, secret, v1.CreateOptions{}) + if err != nil { + log.Error(err, "Failed to create secret for storing access token.") + return nil, err + } + } + return secret, err +} diff --git a/src/lib/cspauth/csp_auth_test.go b/src/lib/cspauth/csp_auth_test.go new file mode 100644 index 00000000..bb1001aa --- /dev/null +++ b/src/lib/cspauth/csp_auth_test.go @@ -0,0 +1,139 @@ +package cspauth + +import ( + "context" + v12 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/fake" + "testing" +) + +const ( + ApiToken = "API_TOKEN" + GovernorAccessTokenKey = "governorAccessToken" +) + +func TestNewCSPAuthSuccessCase(t *testing.T) { + + RetryDelay = 1 + secret := &v12.Secret{} + secret.Name = "csp-secret" + secret.Namespace = "csp-namespace" + secret.Data = map[string][]byte{ApiToken: []byte("test-api-token")} + + errorSecret := &v12.Secret{} + errorSecret.Name = "csp-secret" + errorSecret.Namespace = "csp-namespace" + errorSecret.Data = map[string][]byte{ApiToken: []byte(SendError)} + + accessSecret := &v12.Secret{} + accessSecret.Name = "governor-accesstoken" + accessSecret.Namespace = "csp-namespace" + accessSecret.Data = map[string][]byte{GovernorAccessTokenKey: []byte("test-access-token")} + + tt := []struct { + name string + secretObject *v12.Secret + accessSecret *v12.Secret + wantErr bool + }{ + { + name: "Get CSP Auth should Pass", + secretObject: secret, + accessSecret: accessSecret, + wantErr: false, + }, + { + name: "Get CSP Auth should fail because no secret found for csp api-token", + secretObject: nil, + accessSecret: accessSecret, + wantErr: true, + }, + { + name: "Get CSP Auth should fail with giving up refresh retry(3times)", + secretObject: errorSecret, + accessSecret: accessSecret, + wantErr: true, + }, + { + name: "Get CSP Auth should pass with accessSecret not found", + secretObject: secret, + accessSecret: nil, + wantErr: false, + }, + } + + for i := range tt { + tc := tt[i] + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + objects := make([]runtime.Object, 0) + + if tc.secretObject != nil { + objects = append(objects, tc.secretObject) + } + if tc.accessSecret != nil { + objects = append(objects, tc.accessSecret) + } + clientSet := fake.NewSimpleClientset(objects...) + + tokenManager := NewMockCSPClient() + provider := &CspAuth{CspClient: tokenManager} + auth, err := provider.GetBearerToken(clientSet, context.Background(), secret.Namespace, secret.Name) + + if tc.wantErr && (auth != "" || err == nil) { + t.Fatal("NewCSPAuth call failed on tc: " + tc.name) + } + + if !tc.wantErr && (auth == "" || err != nil) { + t.Fatal("NewCSPAuth call failed on tc: " + tc.name) + } + }) + } + +} + +func TestGetBearerTokenSuccess(t *testing.T) { + secret := &v12.Secret{} + secret.Name = "csp-secret" + secret.Namespace = "csp-namespace" + secret.Data = map[string][]byte{ApiToken: []byte("test-api-token")} + + clientSet := fake.NewSimpleClientset(secret) + + tokenManager := NewMockCSPClient() + provider := &CspAuth{CspClient: tokenManager} + authToken, _ := provider.GetBearerToken(clientSet, context.Background(), secret.Namespace, secret.Name) + + if authToken != DummyAccessToken { + t.Fatal("GetBearer must not fail in this test case!") + } +} + +func TestGetBearerTokenReturnSameTokenSuccess(t *testing.T) { + secret := &v12.Secret{} + secret.Name = "csp-secret" + secret.Namespace = "csp-namespace" + secret.Data = map[string][]byte{ApiToken: []byte("test-api-token")} + + clientSet := fake.NewSimpleClientset(secret) + + tokenManager := NewMockCSPClient() + provider := &CspAuth{CspClient: tokenManager} + authToken, _ := provider.GetBearerToken(clientSet, context.Background(), secret.Namespace, secret.Name) + + if authToken != DummyAccessToken { + t.Fatal("GetBearer must not fail in this test case!") + } + + tokenPrev := DummyAccessToken + DummyAccessToken = "changed-dummy-access-token" + authToken1, _ := provider.GetBearerToken(clientSet, context.Background(), secret.Namespace, secret.Name) + + if authToken != authToken1 { + t.Fatal("GetBearer must return same token if called consequently, \nAuth1: " + authToken + "\n Auth2: " + authToken1) + } + DummyAccessToken = tokenPrev +} diff --git a/src/lib/cspauth/mock_token_manager.go b/src/lib/cspauth/mock_token_manager.go new file mode 100644 index 00000000..b2084970 --- /dev/null +++ b/src/lib/cspauth/mock_token_manager.go @@ -0,0 +1,30 @@ +package cspauth + +import ( + "context" + "github.com/pkg/errors" +) + +var ( + DummyAccessToken = "dummy-access-token" + SendError = "send-error" +) + +// MockCSPClient is a mock of the CSPClient interface +type MockCSPClient struct { +} + +// NewMockCSPClient creates a new mock instance +func NewMockCSPClient() *MockCSPClient { + return &MockCSPClient{} +} + +func (m *MockCSPClient) GetCspAuthorization(ctx context.Context, apiToken string) (*CSPAuthorizeResponse, error) { + if apiToken == SendError { + return nil, errors.New("Failed to get CSP Auth") + } + response := CSPAuthorizeResponse{} + response.AccessToken = DummyAccessToken + response.ExpiresIn = 1000 + return &response, nil +} diff --git a/src/lib/cspauth/mocks/mock_csp_auth.go b/src/lib/cspauth/mocks/mock_csp_auth.go new file mode 100644 index 00000000..4e862881 --- /dev/null +++ b/src/lib/cspauth/mocks/mock_csp_auth.go @@ -0,0 +1,55 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + kubernetes "k8s.io/client-go/kubernetes" + + mock "github.com/stretchr/testify/mock" +) + +// Provider is an autogenerated mock type for the Provider type +type Provider struct { + mock.Mock +} + +// GetBearerToken provides a mock function with given fields: _a0, _a1, _a2, _a3 +func (_m *Provider) GetBearerToken(_a0 kubernetes.Interface, _a1 context.Context, _a2 string, _a3 string) (string, error) { + ret := _m.Called(_a0, _a1, _a2, _a3) + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(kubernetes.Interface, context.Context, string, string) (string, error)); ok { + return rf(_a0, _a1, _a2, _a3) + } + if rf, ok := ret.Get(0).(func(kubernetes.Interface, context.Context, string, string) string); ok { + r0 = rf(_a0, _a1, _a2, _a3) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(kubernetes.Interface, context.Context, string, string) error); ok { + r1 = rf(_a0, _a1, _a2, _a3) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewProvider interface { + mock.TestingT + Cleanup(func()) +} + +// NewProvider creates a new instance of Provider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewProvider(t mockConstructorTestingTNewProvider) *Provider { + mock := &Provider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/src/lib/cspauth/token_manager.go b/src/lib/cspauth/token_manager.go new file mode 100644 index 00000000..fecb2aa8 --- /dev/null +++ b/src/lib/cspauth/token_manager.go @@ -0,0 +1,117 @@ +package cspauth + +import ( + "context" + "encoding/json" + "errors" + "github.com/goharbor/harbor/src/lib/log" + "io" + "net/http" + "net/url" + "strings" +) + +var ( + ErrorCspForbidden = errors.New("forbidden") + ErrorCspUnauthorized = errors.New("unauthorized") + ErrorCspBadRequest = errors.New("invalid api_token, it might be expired") + ErrorRefreshTokenNotValid = errors.New("refresh_token cannot be empty") + UnexpectedResponseStatusCode = errors.New("unexpected csp error") +) + +const ( + cspUrl = "https://console.cloud.vmware.com/csp/gateway/am/api/auth/api-tokens/authorize" +) + +// CSPClient represents a CSPClient place here for future mocking purposes +// Will contain all methods related to calls directly to CSP. +// Please note this is not usual since it would be TAC the one +// talking with CSP. +type CSPClient interface { + GetCspAuthorization(ctx context.Context, refreshToken string) (*CSPAuthorizeResponse, error) +} + +// CSPHttpClient is the client to perform talks to CSP. +type CSPHttpClient struct { + client *http.Client + host *url.URL +} + +// CSPAuthorizeResponse represents a response from CSP with information +// about the authorization. AccessToken is the value needed to +// perform valid calls to TAC backend. +type CSPAuthorizeResponse struct { + RefreshToken string `json:"refresh_token"` + ExpiresIn int `json:"expires_in"` + AccessToken string `json:"access_token"` + Scope string `json:"scope"` + IDToken string `json:"id_token"` + TokenType string `json:"token_type"` +} + +// NewCspHTTPClient creates a new CSPHttpClient +func NewCspHTTPClient() (*CSPHttpClient, error) { + + parsedURL, err := url.Parse(cspUrl) + return &CSPHttpClient{ + client: http.DefaultClient, + host: parsedURL, + }, err +} + +// GetCspAuthorization connects to CSP to retrieve information regarding the given API token +func (c *CSPHttpClient) GetCspAuthorization(ctx context.Context, apiToken string) (*CSPAuthorizeResponse, error) { + if apiToken == "" { + return nil, ErrorRefreshTokenNotValid + } + values := url.Values{"grant_type": {"refresh_token"}, "refresh_token": {apiToken}} + + req, err := http.NewRequestWithContext(ctx, "POST", c.host.String(), strings.NewReader(values.Encode())) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("accept-encoding", "application/json") + + resp, err := c.client.Do(req) + if err != nil { + return nil, err + } + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + return + } + }(resp.Body) + + if err := c.checkCspAuthStatusCode(resp); err != nil { + log.Errorf("Found an error code: %v", err.Error()) + return nil, err + } + + var cspAuthResponse CSPAuthorizeResponse + return &cspAuthResponse, json.NewDecoder(resp.Body).Decode(&cspAuthResponse) +} + +// Checks the status code for the auth service. +// 400 status code is ambiguous because it can mean: +// - The api_token is wrong +// - The api_token is expired +// In the later case the user would need to generate a new from +// CSP console since, expired tokens are automatically removed from +// CSP. +func (c *CSPHttpClient) checkCspAuthStatusCode(resp *http.Response) error { + switch resp.StatusCode { + case http.StatusOK: + return nil + case http.StatusBadRequest: + // api_token can be expired + return ErrorCspBadRequest + case http.StatusUnauthorized: + return ErrorCspUnauthorized + case http.StatusForbidden: + return ErrorCspForbidden + default: + return UnexpectedResponseStatusCode + } +} diff --git a/src/lib/cspauth/token_manager_test.go b/src/lib/cspauth/token_manager_test.go new file mode 100644 index 00000000..d81d144e --- /dev/null +++ b/src/lib/cspauth/token_manager_test.go @@ -0,0 +1,70 @@ +package cspauth + +import ( + "context" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" +) + +func TestGetCspAuthorization(t *testing.T) { + tt := []struct { + name string + token string + wantErr bool + }{ + { + name: "Get CSP Auth Success", + token: "dummy-api-token", + wantErr: false, + }, + { + name: "Get CSP Auth Failure", + token: "dummy-api-error-token", + wantErr: true, + }, + } + + for i := range tt { + tc := tt[i] + + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, _ := io.ReadAll(r.Body) + bodyStr := string(body) + if strings.Contains(bodyStr, "refresh_token=dummy-api-token") { + var cspAuthResponse CSPAuthorizeResponse + cspAuthResponse.AccessToken = "dummy-access-token" + successResponse, _ := json.Marshal(cspAuthResponse) + _, _ = w.Write(successResponse) + w.WriteHeader(http.StatusOK) + } else { + w.WriteHeader(http.StatusBadGateway) + } + })) + + defer server.Close() + + client, _ := NewCspHTTPClient() + client.client = http.DefaultClient + client.host, _ = url.Parse(server.URL) + apiToken := tc.token + authorization, err := client.GetCspAuthorization(context.Background(), apiToken) + + if tc.wantErr && (authorization != nil || err == nil) { + t.Fatalf("GetCspAuthorizationCase must fail but got success with token: %v", authorization) + } + + if !tc.wantErr && (authorization == nil || err != nil) { + t.Fatalf("GetCspAuthorizationCase should not fail but failed with error: %v", err) + } + }) + } + +} diff --git a/src/lib/retry/retry.go b/src/lib/retry/retry.go new file mode 100644 index 00000000..44da4ada --- /dev/null +++ b/src/lib/retry/retry.go @@ -0,0 +1,108 @@ +package retry + +import ( + "context" + "fmt" + "log" + "time" +) + +const ( + defaultMaxAttempts = 3 + defaultRetryStep = 3 * time.Second +) + +type delayFunc func(attempt int) time.Duration + +type retryConfig struct { + name string + maxAttempts int + delayFunc delayFunc +} + +type Option func(*retryConfig) + +// WithName allows configuring the name of the function in the error message +func WithName(name string) Option { + return func(o *retryConfig) { + o.name = name + } +} + +// WithMaxAttempts allows configuring the maximum tries for a given function +func WithMaxAttempts(n int) Option { + return func(o *retryConfig) { + o.maxAttempts = n + } +} + +// WithFixedDelay allows configuring a fixed-delay waiting strategy between retries +func WithFixedDelay(delay time.Duration) Option { + return func(o *retryConfig) { + o.delayFunc = func(_ int) time.Duration { return delay } + } +} + +// WithIncrementDelay allows configuring a waiting strategy with a custom base delay and increment +func WithIncrementDelay(baseDuration time.Duration, increment time.Duration) Option { + return func(o *retryConfig) { + o.delayFunc = func(n int) time.Duration { + stepIncrement := increment * time.Duration(n) + return baseDuration + stepIncrement + } + } +} + +type Retry struct { + retryConfig +} + +func NewRetry(opts ...Option) *Retry { + var c retryConfig + for _, o := range append([]Option{ + // Default values + WithName("retryable function"), + WithMaxAttempts(defaultMaxAttempts), + WithFixedDelay(defaultRetryStep), + }, opts...) { + o(&c) + } + return &Retry{c} +} + +func (r *Retry) Run(ctx context.Context, f func() (bool, error)) error { + timer := time.NewTimer(0) + defer timer.Stop() + var attempts int + for { + if success, err := f(); err != nil { + return fmt.Errorf("non retryable error running %q: %w", r.name, err) + } else if success { + return nil + } else { + log.Printf("running %q failed (%d/%d)\n", r.name, attempts+1, r.maxAttempts) + } + + delay := r.delayFunc(attempts) + attempts++ + if attempts == r.maxAttempts { + return fmt.Errorf("giving up retrying, max attempts %d reached", r.maxAttempts) + } + + timer.Reset(delay) + select { + case <-ctx.Done(): + return ctx.Err() + case <-timer.C: + } + } +} + +// SetNextRetry allows configuring a custom duration only for the next retry calculated +func (r *Retry) SetNextRetry(duration time.Duration) { + orig := r.delayFunc + r.delayFunc = func(_ int) time.Duration { + r.delayFunc = orig // restore original function + return duration + } +} diff --git a/src/lib/retry/retry_test.go b/src/lib/retry/retry_test.go new file mode 100644 index 00000000..f5c5fa27 --- /dev/null +++ b/src/lib/retry/retry_test.go @@ -0,0 +1,50 @@ +package retry_test + +import ( + "context" + "fmt" + "github.com/vmware-tanzu/cloud-native-security-inspector/src/lib/retry" + "testing" + "time" +) + +func TestSuccessCase(t *testing.T) { + var count int + err := retry.NewRetry().Run(context.Background(), func() (bool, error) { + count++ + return true, nil + }) + if err != nil { + t.Fatal(err) + } + if got, want := count, 1; got != want { + t.Errorf("unexpected executions count, got: %d, want: %d", got, want) + } +} + +func TestWithMaxAttempts(t *testing.T) { + testCases := []struct { + maxAttempts int + }{ + {maxAttempts: 1}, + {maxAttempts: 3}, + {maxAttempts: 10}, + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("%d attempts", tc.maxAttempts), func(t *testing.T) { + var count int + if err := retry.NewRetry( + retry.WithMaxAttempts(tc.maxAttempts), + retry.WithFixedDelay(10*time.Millisecond), + ).Run(context.Background(), func() (bool, error) { + count++ + return count == tc.maxAttempts, nil + }); err != nil { + t.Fatal(err) + } + if got, want := count, tc.maxAttempts; got != want { + t.Errorf("expected function to be executed %d, got: %d", want, got) + } + }) + } +} diff --git a/src/pkg/data/consumers/governor/exporter.go b/src/pkg/data/consumers/governor/exporter.go index a7a1bb25..8a449ad0 100644 --- a/src/pkg/data/consumers/governor/exporter.go +++ b/src/pkg/data/consumers/governor/exporter.go @@ -5,25 +5,47 @@ import ( "errors" "fmt" api "github.com/vmware-tanzu/cloud-native-security-inspector/src/api/v1alpha1" + "github.com/vmware-tanzu/cloud-native-security-inspector/src/lib/cspauth" "github.com/vmware-tanzu/cloud-native-security-inspector/src/lib/log" openapi "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/governor/go-client" + "k8s.io/client-go/kubernetes" "net/http" ) +const ( + cspSecretNamespace = "cnsi-system" +) + type GovernorExporter struct { - Report *api.AssessmentReport - ClusterID string - ApiToken string - ApiClient *openapi.APIClient + Report *api.AssessmentReport + ClusterID string + ApiClient *openapi.APIClient + CspProvider cspauth.Provider + KubeInterface kubernetes.Interface } // SendReportToGovernor is used to send report to governor url http end point. -func (g GovernorExporter) SendReportToGovernor() error { +func (g GovernorExporter) SendReportToGovernor(ctx context.Context) error { // Get governor api request model from assessment report. kubernetesCluster := g.getGovernorAPIPayload() + log.Info("Payload data for governor:") log.Info(kubernetesCluster) - apiSaveClusterRequest := g.ApiClient.ClustersApi.UpdateTelemetry(context.Background(), g.ClusterID).KubernetesTelemetryRequest(kubernetesCluster) + + cspSecretName := ctx.Value("cspSecretName") + if cspSecretName == nil { + log.Error("Error while retrieving access token !") + return errors.New("CSP secret name must be set to connect to Governor") + } + governorAccessToken, err := g.CspProvider.GetBearerToken(g.KubeInterface, ctx, cspSecretNamespace, cspSecretName.(string)) + if err != nil { + log.Error("Error while retrieving access token !") + return err + } + + ctx = context.WithValue(ctx, openapi.ContextAccessToken, governorAccessToken) + + apiSaveClusterRequest := g.ApiClient.ClustersApi.UpdateTelemetry(ctx, g.ClusterID).KubernetesTelemetryRequest(kubernetesCluster) // Call api cluster to send telemetry data and get response. response, err := g.ApiClient.ClustersApi.UpdateTelemetryExecute(apiSaveClusterRequest) @@ -46,7 +68,7 @@ func (g GovernorExporter) SendReportToGovernor() error { // getGovernorAPIPayload is used to map assessment report to client model. func (g GovernorExporter) getGovernorAPIPayload() openapi.KubernetesTelemetryRequest { kubernetesCluster := openapi.NewKubernetesTelemetryRequestWithDefaults() - + kubernetesCluster.Workloads = make([]openapi.KubernetesWorkload, 0) for _, nsa := range g.Report.Spec.NamespaceAssessments { for _, workloadAssessment := range nsa.WorkloadAssessments { kubernetesWorkloads := openapi.NewKubernetesWorkloadWithDefaults() @@ -54,6 +76,7 @@ func (g GovernorExporter) getGovernorAPIPayload() openapi.KubernetesTelemetryReq kubernetesWorkloads.Kind = workloadAssessment.Workload.Kind kubernetesWorkloads.Namespace = nsa.Namespace.Name kubernetesWorkloads.Replicas = workloadAssessment.Workload.Replicas + for _, pod := range workloadAssessment.Workload.Pods { containerData := openapi.NewContainerWithDefaults() for _, container := range pod.Containers { diff --git a/src/pkg/data/consumers/governor/exporter_test.go b/src/pkg/data/consumers/governor/exporter_test.go index 2566ca33..1b8f16ac 100644 --- a/src/pkg/data/consumers/governor/exporter_test.go +++ b/src/pkg/data/consumers/governor/exporter_test.go @@ -1,9 +1,12 @@ package consumers import ( + "context" + "errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" api "github.com/vmware-tanzu/cloud-native-security-inspector/src/api/v1alpha1" + "github.com/vmware-tanzu/cloud-native-security-inspector/src/lib/cspauth/mocks" openapi "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/governor/go-client" v1 "k8s.io/api/core/v1" "net/http" @@ -11,15 +14,17 @@ import ( ) var ( - clusterID = "testingId" - apiToken = "apiToken" - namespace = "testingNamespace" - name = "name" - image = "image" - imageID = "imageId" - replicaCount = 2 - testHeader = "testHeader" - testHeaderValue = "testHeaderValue" + clusterID = "testingId" + apiToken = "apiToken" + namespace = "testingNamespace" + name = "name" + image = "image" + imageID = "imageId" + replicaCount = 2 + testHeader = "testHeader" + testHeaderValue = "testHeaderValue" + testApiTokentoken = "test-access-token" + testCspSecretName = "cspSecretName" ) const ( @@ -38,6 +43,9 @@ func TestSendReportToGovernor(t *testing.T) { testClusterID string testAPIToken string testStatusCode int + testSecretName string + createCSPProvider bool + authToken string }{ { testCaseDescription: "Success: Happy flow end to end.", @@ -54,9 +62,12 @@ func TestSendReportToGovernor(t *testing.T) { Image: image, ImageID: imageID, }}}}}}}}}}}, - testClusterID: clusterID, - testAPIToken: apiToken, - testStatusCode: http.StatusNoContent, + testClusterID: clusterID, + testAPIToken: apiToken, + testStatusCode: http.StatusNoContent, + testSecretName: testCspSecretName, + createCSPProvider: true, + authToken: testApiTokentoken, }, { testCaseDescription: "Success: Empty payload, successful case", @@ -67,6 +78,9 @@ func TestSendReportToGovernor(t *testing.T) { testClusterID: clusterID, testAPIToken: apiToken, testStatusCode: http.StatusNoContent, + testSecretName: testCspSecretName, + createCSPProvider: true, + authToken: testApiTokentoken, }, { testCaseDescription: "Failure: Error from API call.", @@ -77,6 +91,9 @@ func TestSendReportToGovernor(t *testing.T) { testClusterID: clusterID, testAPIToken: apiToken, testStatusCode: http.StatusInternalServerError, + testSecretName: testCspSecretName, + createCSPProvider: true, + authToken: testApiTokentoken, }, { testCaseDescription: "Failure Invalid URL: Error from api.", @@ -87,6 +104,9 @@ func TestSendReportToGovernor(t *testing.T) { testClusterID: clusterID, testAPIToken: apiToken, testStatusCode: http.StatusBadRequest, + testSecretName: testCspSecretName, + createCSPProvider: true, + authToken: testApiTokentoken, }, { testCaseDescription: "Failure: Timeout to receive response from api.", @@ -97,6 +117,35 @@ func TestSendReportToGovernor(t *testing.T) { testClusterID: clusterID, testAPIToken: apiToken, testStatusCode: http.StatusRequestTimeout, + testSecretName: testCspSecretName, + createCSPProvider: true, + authToken: testApiTokentoken, + }, + { + testCaseDescription: "Failure: CSP Secret name not found", + testHost: testHost, + testHeader: testHeader, + testHeaderValue: testHeaderValue, + testReportData: &api.AssessmentReport{}, + testClusterID: clusterID, + testAPIToken: apiToken, + testStatusCode: http.StatusNotFound, + testSecretName: "", + createCSPProvider: false, + authToken: testApiTokentoken, + }, + { + testCaseDescription: "Failure: Access Token not available", + testHost: testHost, + testHeader: testHeader, + testHeaderValue: testHeaderValue, + testReportData: &api.AssessmentReport{}, + testClusterID: clusterID, + testAPIToken: apiToken, + testStatusCode: http.StatusNotFound, + testSecretName: testCspSecretName, + createCSPProvider: true, + authToken: "", }, } @@ -112,7 +161,6 @@ func TestSendReportToGovernor(t *testing.T) { g := GovernorExporter{ Report: tt.testReportData, ApiClient: clusterClient, - ApiToken: apiToken, ClusterID: tt.testClusterID, } mockAPIClient := new(ClustersApi) @@ -127,7 +175,20 @@ func TestSendReportToGovernor(t *testing.T) { StatusCode: tt.testStatusCode, }, nil) - errFromSendReportToGovernor := g.SendReportToGovernor() + ctx := context.Background() + if tt.testSecretName != "" { + ctx = context.WithValue(ctx, "cspSecretName", tt.testSecretName) + } + + provider := new(mocks.Provider) + if tt.authToken == "" { + provider.On("GetBearerToken", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tt.authToken, errors.New("Failed to fetch CSP auth token")) + } else { + provider.On("GetBearerToken", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tt.authToken, nil) + } + g.CspProvider = provider + + errFromSendReportToGovernor := g.SendReportToGovernor(ctx) if tt.testStatusCode != http.StatusNoContent { assert.Error(t, errFromSendReportToGovernor) } else { diff --git a/src/pkg/inspection/controller.go b/src/pkg/inspection/controller.go index 75299055..653aba70 100644 --- a/src/pkg/inspection/controller.go +++ b/src/pkg/inspection/controller.go @@ -5,6 +5,7 @@ package inspection import ( "context" "fmt" + "github.com/vmware-tanzu/cloud-native-security-inspector/src/lib/cspauth" "github.com/vmware-tanzu/cloud-native-security-inspector/src/lib/log" es "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/es" governor "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/governor" @@ -12,6 +13,7 @@ import ( osearch "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/data/consumers/opensearch" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" "time" "github.com/vmware-tanzu/cloud-native-security-inspector/src/pkg/policy/enforcement" @@ -141,6 +143,10 @@ func (c *controller) Run(ctx context.Context, policy *v1alpha1.InspectionPolicy) // Just in case. if len(nsl) == 0 { log.Info("no namespaces found") + err2 := c.checkAndSendReportToGovernor(ctx, policy, &v1alpha1.AssessmentReport{}) + if err2 != nil { + return err2 + } return nil } @@ -333,20 +339,10 @@ func (c *controller) Run(ctx context.Context, policy *v1alpha1.InspectionPolicy) } } - // Read config from InspectionPolicy, send assessment reports to Governor api if governor enabled. - if policy.Spec.Inspection.Assessment.Governor.Enabled { - governorConfig := policy.Spec.Inspection.Assessment.Governor - - if governorConfig.ClusterID == "" || governorConfig.URL == "" || governorConfig.APIToken == "" { - log.Error("Either ClusterID or URL or APIToken is empty") - return errors.New("Either ClusterID or URL or APIToken is empty") - } - - log.Info("Calling governor exporter") - if exporterErr := exportReportToGovernor(report, policy); exporterErr != nil { - log.Errorf("Error from exporter: %v", exporterErr) - return exporterErr - } + err2 := c.checkAndSendReportToGovernor(ctx, policy, report) + log.Info("Calling governor exporter") + if err2 != nil { + return err2 } // Create report CR if necessary. @@ -373,23 +369,57 @@ func (c *controller) Run(ctx context.Context, policy *v1alpha1.InspectionPolicy) return nil } -func exportReportToGovernor(report *v1alpha1.AssessmentReport, policy *v1alpha1.InspectionPolicy) error { +func (c *controller) checkAndSendReportToGovernor(ctx context.Context, policy *v1alpha1.InspectionPolicy, report *v1alpha1.AssessmentReport) error { + // Read config from InspectionPolicy, send assessment reports to Governor api if governor enabled. + if policy.Spec.Inspection.Assessment.Governor.Enabled { + governorConfig := policy.Spec.Inspection.Assessment.Governor + + if governorConfig.ClusterID == "" || governorConfig.URL == "" || governorConfig.CspSecretName == "" { + log.Error("Either ClusterID or URL or CSPSecretName is empty") + return errors.New("Either ClusterID or URL or CSPSecretName is empty") + } + + log.Info("Calling governor exporter") + if exporterErr := exportReportToGovernor(ctx, report, policy); exporterErr != nil { + log.Errorf("Error from exporter: %v", exporterErr) + return exporterErr + } + } + return nil +} + +func exportReportToGovernor(ctx context.Context, report *v1alpha1.AssessmentReport, policy *v1alpha1.InspectionPolicy) error { governorConfig := policy.Spec.Inspection.Assessment.Governor // Create api client to governor api. config := openapi.NewConfiguration() - //config.Host = governorConfig.URL - config.Servers = openapi.ServerConfigurations{{URL: governorConfig.URL}} + config.Servers = openapi.ServerConfigurations{{ + URL: governorConfig.URL, + }} apiClient := openapi.NewAPIClient(config) + cspClient, err := cspauth.NewCspHTTPClient() + if err != nil { + log.Errorf("Initializing CSP : %v", err) + return err + } + provider := &cspauth.CspAuth{CspClient: cspClient} + + clientSet, err := kubernetes.NewForConfig(ctrl.GetConfigOrDie()) + if err != nil { + log.Error(err, "Failed to get kubernetes clientSet, check if kube config is correctly configured!") + return err + } + exporter := governor.GovernorExporter{ - Report: report, - ClusterID: governorConfig.ClusterID, - ApiToken: governorConfig.APIToken, - ApiClient: apiClient, + Report: report, + ClusterID: governorConfig.ClusterID, + ApiClient: apiClient, + CspProvider: provider, + KubeInterface: clientSet, } - if apiResponseErr := exporter.SendReportToGovernor(); apiResponseErr != nil { + if apiResponseErr := exporter.SendReportToGovernor(ctx); apiResponseErr != nil { log.Error("Err response from governor exporter", apiResponseErr) return apiResponseErr } @@ -398,15 +428,15 @@ func exportReportToGovernor(report *v1alpha1.AssessmentReport, policy *v1alpha1. } func exportReportToOpenSearch(report *v1alpha1.AssessmentReport, policy *v1alpha1.InspectionPolicy) error { - client := osearch.NewClient([]byte{}, + openSearchClient := osearch.NewClient([]byte{}, policy.Spec.Inspection.Assessment.OpenSearchAddr, policy.Spec.Inspection.Assessment.OpenSearchUser, policy.Spec.Inspection.Assessment.OpenSearchPasswd) - if client == nil { - log.Info("OpenSearch client is nil") + if openSearchClient == nil { + log.Info("OpenSearch openSearchClient is nil") } - exporter := osearch.OpenSearchExporter{Client: client} - err := exporter.NewExporter(client, "assessment_report") + exporter := osearch.OpenSearchExporter{Client: openSearchClient} + err := exporter.NewExporter(openSearchClient, "assessment_report") if err != nil { return err } @@ -434,17 +464,17 @@ func exportReportToES(report *v1alpha1.AssessmentReport, policy *v1alpha1.Inspec } log.Info("ES config: ", "addr", clientArgs.addr) log.Info("ES config: ", "clientArgs.username", clientArgs.username) - client := es.NewClient(clientArgs.cert, clientArgs.addr, clientArgs.username, clientArgs.passwd) - if client == nil { - log.Info("ES client is nil") + esClient := es.NewClient(clientArgs.cert, clientArgs.addr, clientArgs.username, clientArgs.passwd) + if esClient == nil { + log.Info("ES esClient is nil") } if err := es.TestClient(); err != nil { - log.Info("client test error") + log.Info("esClient test error") return err } - exporter := es.ElasticSearchExporter{Client: client} - err := exporter.NewExporter(client, "assessment_report") + exporter := es.ElasticSearchExporter{Client: esClient} + err := exporter.NewExporter(esClient, "assessment_report") if err != nil { return err } From b75bea04f19f780da43905857b34ee6be7e55f63 Mon Sep 17 00:00:00 2001 From: harshsharma071988 Date: Wed, 1 Mar 2023 16:38:39 +0530 Subject: [PATCH 6/6] Minor fixes -- cspSecretName in place of ApiToken in policy.yml -- Secret Data initialized while creation Signed-off-by: harshsharma071988 --- src/config/samples/policy.yaml | 2 +- src/lib/cspauth/csp_auth.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/config/samples/policy.yaml b/src/config/samples/policy.yaml index 0970ad73..32085098 100644 --- a/src/config/samples/policy.yaml +++ b/src/config/samples/policy.yaml @@ -61,7 +61,7 @@ spec: enabled: true clusterId: "65a03970-c53a-4ba1-8d1f-42c9f95d2761" url: "https://api.int.app-catalog.vmware.com/catalog-governor/v1" - apiToken: "n6yDMkMEghPUYJDGsn39I_GYNnPh4Vi-LnaH2URjpgwcXbFIVYzMU-n8LRzTJGKO" + cspSecretName: "csp-secret" baselines: - kind: "vulnerability" baseline: "High" diff --git a/src/lib/cspauth/csp_auth.go b/src/lib/cspauth/csp_auth.go index f591e77e..09f92414 100644 --- a/src/lib/cspauth/csp_auth.go +++ b/src/lib/cspauth/csp_auth.go @@ -74,7 +74,8 @@ func (a *CspAuth) refreshToken(ctx context.Context, clientSet kubernetes.Interfa expiresIn := time.Duration(math.Min(float64(cspAuthResponse.ExpiresIn), tokenMaxAgeSeconds)) * time.Second formattedExpiration := now.Add(expiresIn).Format(time.Layout) - log.Infof("Refreshed access token for governor: %s which expires in %s", cspAuthResponse.AccessToken, formattedExpiration) + log.Infof("Refreshed access token for governor which expires in %s", formattedExpiration) + accessTokenSecret.Data = make(map[string][]byte, 0) accessTokenSecret.Data[governorAccessTokenKey] = []byte(cspAuthResponse.AccessToken) accessTokenSecret.Data[governorTokenExpiresIn] = []byte(formattedExpiration) _, err = clientSet.CoreV1().Secrets(cspSecretNamespace).Update(ctx, accessTokenSecret, v1.UpdateOptions{}) @@ -104,7 +105,7 @@ func getOrCreateSecretForAccessToken(clientSet kubernetes.Interface, ctx context secret = &v12.Secret{} secret.Name = accessTokenSecretName secret.Namespace = ns - secret.Data = map[string][]byte{} + secret.Data = make(map[string][]byte, 0) secret, err = clientSet.CoreV1().Secrets(ns).Create(ctx, secret, v1.CreateOptions{}) if err != nil { log.Error(err, "Failed to create secret for storing access token.")