From c130c5cd20d971a455d1355c424d6afaf8e4dffc Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 8 Dec 2023 16:36:28 +0100 Subject: [PATCH 01/43] added gitopsruntime test for extending runtime ui ... --- core/server/fluxruntime.go | 80 +++++++++++++++++-------------- core/server/gitopsruntime_test.go | 70 +++++++++++++++++++++++++++ core/server/suite_test.go | 5 ++ 3 files changed, 119 insertions(+), 36 deletions(-) create mode 100644 core/server/gitopsruntime_test.go diff --git a/core/server/fluxruntime.go b/core/server/fluxruntime.go index de0a15a14d..4988a9040c 100644 --- a/core/server/fluxruntime.go +++ b/core/server/fluxruntime.go @@ -29,6 +29,7 @@ import ( const ( FluxNamespacePartOf = "flux" + PartOfWeaveGitops = "weave-gitops" ) var ( @@ -45,6 +46,10 @@ var ( DefaultFluxNamespace = lookupEnv("WEAVE_GITOPS_FALLBACK_NAMESPACE", "flux-system") ) +var RuntimeLabels = []string{ + FluxNamespacePartOf, PartOfWeaveGitops, +} + func lookupEnv(envVar, fallback string) string { if val, ok := os.LookupEnv(envVar); ok { return val @@ -69,51 +74,54 @@ func (cs *coreServer) ListFluxRuntimeObjects(ctx context.Context, msg *pb.ListFl var results []*pb.Deployment - for clusterName, nss := range cs.clustersManager.GetClustersNamespaces() { - fluxNamepsaces := filterFluxNamespace(nss) - if len(fluxNamepsaces) == 0 { - respErrors = append(respErrors, &pb.ListError{ClusterName: clusterName, Namespace: "", Message: ErrFluxNamespaceNotFound.Error()}) - continue - } - - opts := client.MatchingLabels{ - coretypes.PartOfLabel: FluxNamespacePartOf, - } - - list := &appsv1.DeploymentList{} - - for _, fluxNs := range fluxNamepsaces { - if err := clustersClient.List(ctx, clusterName, list, opts, client.InNamespace(fluxNs.Name)); err != nil { - respErrors = append(respErrors, &pb.ListError{ClusterName: clusterName, Namespace: fluxNs.Name, Message: fmt.Sprintf("%s, %s", ErrListingDeployments.Error(), err)}) + for _, runtimeLabel := range RuntimeLabels { + for clusterName, nss := range cs.clustersManager.GetClustersNamespaces() { + fluxNamespaces := filterFluxNamespace(nss) + if len(fluxNamespaces) == 0 { + respErrors = append(respErrors, &pb.ListError{ClusterName: clusterName, Namespace: "", Message: ErrFluxNamespaceNotFound.Error()}) continue } - for _, d := range list.Items { - r := &pb.Deployment{ - Name: d.Name, - Namespace: d.Namespace, - Conditions: []*pb.Condition{}, - ClusterName: clusterName, - Uid: string(d.GetUID()), - Labels: d.Labels, - } + opts := client.MatchingLabels{ + coretypes.PartOfLabel: runtimeLabel, + } - for _, cond := range d.Status.Conditions { - r.Conditions = append(r.Conditions, &pb.Condition{ - Message: cond.Message, - Reason: cond.Reason, - Status: string(cond.Status), - Type: string(cond.Type), - }) - } + deploymentList := &appsv1.DeploymentList{} - for _, img := range d.Spec.Template.Spec.Containers { - r.Images = append(r.Images, img.Image) + for _, fluxNs := range fluxNamespaces { + if err := clustersClient.List(ctx, clusterName, deploymentList, opts, client.InNamespace(fluxNs.Name)); err != nil { + respErrors = append(respErrors, &pb.ListError{ClusterName: clusterName, Namespace: fluxNs.Name, Message: fmt.Sprintf("%s, %s", ErrListingDeployments.Error(), err)}) + continue } - results = append(results, r) + for _, d := range deploymentList.Items { + r := &pb.Deployment{ + Name: d.Name, + Namespace: d.Namespace, + Conditions: []*pb.Condition{}, + ClusterName: clusterName, + Uid: string(d.GetUID()), + Labels: d.Labels, + } + + for _, cond := range d.Status.Conditions { + r.Conditions = append(r.Conditions, &pb.Condition{ + Message: cond.Message, + Reason: cond.Reason, + Status: string(cond.Status), + Type: string(cond.Type), + }) + } + + for _, img := range d.Spec.Template.Spec.Containers { + r.Images = append(r.Images, img.Image) + } + + results = append(results, r) + } } } + } return &pb.ListFluxRuntimeObjectsResponse{Deployments: results, Errors: respErrors}, nil diff --git a/core/server/gitopsruntime_test.go b/core/server/gitopsruntime_test.go new file mode 100644 index 0000000000..91d470e889 --- /dev/null +++ b/core/server/gitopsruntime_test.go @@ -0,0 +1,70 @@ +package server_test + +import ( + "context" + "testing" + + . "github.com/onsi/gomega" + "github.com/weaveworks/weave-gitops/core/clustersmngr/cluster" + "github.com/weaveworks/weave-gitops/core/server" + coretypes "github.com/weaveworks/weave-gitops/core/server/types" + pb "github.com/weaveworks/weave-gitops/pkg/api/core" + "github.com/weaveworks/weave-gitops/pkg/kube" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestListGitopsRuntimeObjects(t *testing.T) { + g := NewGomegaWithT(t) + + ctx := context.Background() + + tests := []struct { + description string + objects []runtime.Object + assertions func(*pb.ListFluxRuntimeObjectsResponse) + }{ + { + "no gitops runtime", + []runtime.Object{ + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "ns1"}}, + }, + func(res *pb.ListFluxRuntimeObjectsResponse) { + g.Expect(res.Errors[0].Message).To(Equal(server.ErrFluxNamespaceNotFound.Error())) + g.Expect(res.Errors[0].Namespace).To(BeEmpty()) + g.Expect(res.Errors[0].ClusterName).To(Equal(cluster.DefaultCluster)) + }, + }, + { + "flux namespace label, with controllers", + []runtime.Object{ + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ + coretypes.PartOfLabel: server.FluxNamespacePartOf, + }}}, + newDeployment("kustomize-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), + newDeployment("weave-gitops-enterprise-mccp-cluster-service", "flux-ns", map[string]string{coretypes.PartOfLabel: server.PartOfWeaveGitops}), + newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), + }, + func(res *pb.ListFluxRuntimeObjectsResponse) { + g.Expect(res.Deployments).To(HaveLen(2), "expected deployments in the flux namespace to be returned") + g.Expect(res.Deployments[0].Name).To(Equal("kustomize-controller")) + g.Expect(res.Deployments[1].Name).To(Equal("weave-gitops-enterprise-mccp-cluster-service")) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + scheme, err := kube.CreateScheme() + g.Expect(err).To(BeNil()) + client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tt.objects...).Build() + cfg := makeServerConfig(client, t, "") + c := makeServer(cfg, t) + res, err := c.ListFluxRuntimeObjects(ctx, &pb.ListFluxRuntimeObjectsRequest{}) + g.Expect(err).NotTo(HaveOccurred()) + tt.assertions(res) + }) + } +} diff --git a/core/server/suite_test.go b/core/server/suite_test.go index d0e89d854f..3f7f2efaa5 100644 --- a/core/server/suite_test.go +++ b/core/server/suite_test.go @@ -36,6 +36,11 @@ var nsChecker nsaccessfakes.FakeChecker func TestMain(m *testing.M) { var err error + + // setup testEnvironment + envTestPath := fmt.Sprintf("%s/tools/bin/envtest", "/Users/enekofb/projects/github.com/weaveworks/weave-gitops-enterprise") + os.Setenv("KUBEBUILDER_ASSETS", envTestPath) + k8sEnv, err = testutils.StartK8sTestEnvironment([]string{ "../../manifests/crds", "../../tools/testcrds", From 1a4d7bf814ebf8d9a4af1540ab9b392078c00302 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 15 Dec 2023 11:39:41 +0100 Subject: [PATCH 02/43] added feature flag for runtime ui Signed-off-by: Eneko Fernandez --- charts/gitops-server/values.yaml | 5 +++++ core/server/fluxruntime.go | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/charts/gitops-server/values.yaml b/charts/gitops-server/values.yaml index 8e27d5c987..df2f10a144 100644 --- a/charts/gitops-server/values.yaml +++ b/charts/gitops-server/values.yaml @@ -24,6 +24,11 @@ envVars: value: "true" - name: WEAVE_GITOPS_FEATURE_CLUSTER value: "false" + # configures the behaviour for weave gitops runtime ui. if enabled, flux runtime UI + # will show other weave-gitops components like policy-agent + - name: WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME + value: "false" + # -- Annotations to add to the deployment annotations: {} # Should the 'oidc-auth' secret be created. For a detailed diff --git a/core/server/fluxruntime.go b/core/server/fluxruntime.go index 4988a9040c..c7c2a562c9 100644 --- a/core/server/fluxruntime.go +++ b/core/server/fluxruntime.go @@ -74,6 +74,8 @@ func (cs *coreServer) ListFluxRuntimeObjects(ctx context.Context, msg *pb.ListFl var results []*pb.Deployment + getRuntimeLabels() + for _, runtimeLabel := range RuntimeLabels { for clusterName, nss := range cs.clustersManager.GetClustersNamespaces() { fluxNamespaces := filterFluxNamespace(nss) @@ -127,6 +129,13 @@ func (cs *coreServer) ListFluxRuntimeObjects(ctx context.Context, msg *pb.ListFl return &pb.ListFluxRuntimeObjectsResponse{Deployments: results, Errors: respErrors}, nil } +// getRuntimeLabels returns the labels that are used to identify the runtime objects based on +// the user expectations set as part of the feature flag + +func getRuntimeLabels() { + +} + func (cs *coreServer) ListFluxCrds(ctx context.Context, msg *pb.ListFluxCrdsRequest) (*pb.ListFluxCrdsResponse, error) { clustersClient, err := cs.clustersManager.GetImpersonatedClient(ctx, auth.Principal(ctx)) if err != nil { From 426dae1b05c149b2592dd8cdd0b75c4b01c69fa3 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 15 Dec 2023 11:50:25 +0100 Subject: [PATCH 03/43] adding unit flux runtime Signed-off-by: Eneko Fernandez --- core/server/fluxruntime.go | 7 +- core/server/fluxruntime_integration_test.go | 465 ++++++++++++++++++++ core/server/fluxruntime_test.go | 459 +------------------ 3 files changed, 480 insertions(+), 451 deletions(-) create mode 100644 core/server/fluxruntime_integration_test.go diff --git a/core/server/fluxruntime.go b/core/server/fluxruntime.go index c7c2a562c9..93bb30098a 100644 --- a/core/server/fluxruntime.go +++ b/core/server/fluxruntime.go @@ -130,10 +130,9 @@ func (cs *coreServer) ListFluxRuntimeObjects(ctx context.Context, msg *pb.ListFl } // getRuntimeLabels returns the labels that are used to identify the runtime objects based on -// the user expectations set as part of the feature flag - -func getRuntimeLabels() { - +// whether the user has enabled `WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME` or not +func getRuntimeLabels() []string { + return []string{} } func (cs *coreServer) ListFluxCrds(ctx context.Context, msg *pb.ListFluxCrdsRequest) (*pb.ListFluxCrdsResponse, error) { diff --git a/core/server/fluxruntime_integration_test.go b/core/server/fluxruntime_integration_test.go new file mode 100644 index 0000000000..36c3e64dc1 --- /dev/null +++ b/core/server/fluxruntime_integration_test.go @@ -0,0 +1,465 @@ +package server_test + +import ( + "context" + "encoding/json" + "fmt" + "testing" + + kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" + . "github.com/onsi/gomega" + "github.com/weaveworks/weave-gitops/core/clustersmngr/cluster" + "github.com/weaveworks/weave-gitops/core/server" + coretypes "github.com/weaveworks/weave-gitops/core/server/types" + pb "github.com/weaveworks/weave-gitops/pkg/api/core" + "github.com/weaveworks/weave-gitops/pkg/kube" + "google.golang.org/grpc/metadata" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestGetReconciledObjects(t *testing.T) { + g := NewGomegaWithT(t) + + ctx := context.Background() + + c := makeGRPCServer(k8sEnv.Rest, t) + + scheme, err := kube.CreateScheme() + g.Expect(err).To(BeNil()) + + k, err := client.New(k8sEnv.Rest, client.Options{ + Scheme: scheme, + }) + g.Expect(err).NotTo(HaveOccurred()) + + automationName := "my-automation" + ns1 := newNamespace(ctx, k, g) + ns2 := newNamespace(ctx, k, g) + + reconciledObjs := []client.Object{ + &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-deployment", + Namespace: ns1.Name, + Labels: map[string]string{ + server.KustomizeNameKey: automationName, + server.KustomizeNamespaceKey: ns1.Name, + }, + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + coretypes.AppLabel: automationName, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{coretypes.AppLabel: automationName}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Name: "nginx", + Image: "nginx", + }}, + }, + }, + }, + }, + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-configmap", + Namespace: ns2.Name, + Labels: map[string]string{ + server.KustomizeNameKey: automationName, + server.KustomizeNamespaceKey: ns1.Name, + }, + }, + }, + } + + for _, obj := range reconciledObjs { + g.Expect(k.Create(ctx, obj)).Should(Succeed()) + } + + crb := rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns1.Name, + Name: "ns-admin", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.SchemeGroupVersion.Group, + Kind: "ClusterRole", + Name: "cluster-admin", + }, + Subjects: []rbacv1.Subject{{ + APIGroup: rbacv1.SchemeGroupVersion.Group, + Kind: rbacv1.UserKind, + Name: "ns-admin", + }}, + } + g.Expect(k.Create(ctx, &crb)).Should((Succeed())) + + type objectAssertion struct { + kind string + name string + } + + tests := []struct { + name string + user string + group string + expectedLen int + expectedObjects []objectAssertion + }{ + { + name: "unknown user doesn't receive any objects", + user: "anne", + expectedLen: 0, + }, + { + name: "ns-admin sees only objects in their namespace", + user: "ns-admin", + expectedLen: 1, + expectedObjects: []objectAssertion{ + { + kind: "Deployment", + name: reconciledObjs[0].GetName(), + }, + }, + }, + { + name: "master user receives all objects", + user: "anne", + group: "system:masters", + expectedLen: 2, + expectedObjects: []objectAssertion{ + { + kind: "Deployment", + name: reconciledObjs[0].GetName(), + }, + { + kind: "ConfigMap", + name: reconciledObjs[1].GetName(), + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g = NewGomegaWithT(t) + + md := metadata.Pairs(MetadataUserKey, tt.user, MetadataGroupsKey, tt.group) + outgoingCtx := metadata.NewOutgoingContext(ctx, md) + res, err := c.GetReconciledObjects(outgoingCtx, &pb.GetReconciledObjectsRequest{ + AutomationName: automationName, + Namespace: ns1.Name, + AutomationKind: kustomizev1.KustomizationKind, + Kinds: []*pb.GroupVersionKind{ + {Group: appsv1.SchemeGroupVersion.Group, Version: appsv1.SchemeGroupVersion.Version, Kind: "Deployment"}, + {Group: corev1.SchemeGroupVersion.Group, Version: corev1.SchemeGroupVersion.Version, Kind: "ConfigMap"}, + }, + ClusterName: cluster.DefaultCluster, + }) + + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(res.Objects).To(HaveLen(tt.expectedLen), "unexpected size of returned object list") + + actualObjs := make([]objectAssertion, len(res.Objects)) + + for idx, actualObj := range res.Objects { + var object map[string]interface{} + + g.Expect(json.Unmarshal([]byte(actualObj.Payload), &object)).To(Succeed(), "failed unmarshalling result object") + metadata, ok := object["metadata"].(map[string]interface{}) + g.Expect(ok).To(BeTrue(), "object has unexpected metadata type") + actualObjs[idx] = objectAssertion{ + kind: object["kind"].(string), + name: metadata["name"].(string), + } + } + g.Expect(actualObjs).To(ContainElements(tt.expectedObjects)) + }) + } +} + +func TestGetReconciledObjectsWithSecret(t *testing.T) { + g := NewGomegaWithT(t) + + ctx := context.Background() + + c := makeGRPCServer(k8sEnv.Rest, t) + + scheme, err := kube.CreateScheme() + g.Expect(err).To(BeNil()) + + k, err := client.New(k8sEnv.Rest, client.Options{ + Scheme: scheme, + }) + g.Expect(err).NotTo(HaveOccurred()) + + automationName := "my-automation" + ns := newNamespace(ctx, k, g) + + reconciledObj := corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-secret", + Namespace: ns.Name, + UID: "this-is-not-an-uid", + Labels: map[string]string{ + server.KustomizeNameKey: automationName, + server.KustomizeNamespaceKey: ns.Name, + }, + }, + TypeMeta: metav1.TypeMeta{Kind: "Secret", APIVersion: "v1"}, + Data: map[string][]byte{"username": []byte("username"), "password": []byte("password")}, + } + + g.Expect(k.Create(ctx, &reconciledObj)).Should(Succeed()) + + md := metadata.Pairs(MetadataUserKey, "anne", MetadataGroupsKey, "system:masters") + outgoingCtx := metadata.NewOutgoingContext(ctx, md) + res, err := c.GetReconciledObjects(outgoingCtx, &pb.GetReconciledObjectsRequest{ + AutomationName: automationName, + Namespace: ns.Name, + AutomationKind: kustomizev1.KustomizationKind, + Kinds: []*pb.GroupVersionKind{{Group: "", Version: "v1", Kind: "Secret"}}, + ClusterName: cluster.DefaultCluster, + }) + + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(res.Objects).To(HaveLen(1)) + + first := res.Objects[0] + g.Expect(first.Payload).To(ContainSubstring("redacted")) +} + +func TestGetChildObjects(t *testing.T) { + g := NewGomegaWithT(t) + + ctx := context.Background() + + automationName := "my-automation" + + ns := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + }, + } + + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-deployment", + Namespace: ns.Name, + UID: "this-is-not-an-uid", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + coretypes.AppLabel: automationName, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{coretypes.AppLabel: automationName}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Name: "nginx", + Image: "nginx", + }}, + }, + }, + }, + } + + rs := &appsv1.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-123abcd", automationName), + Namespace: ns.Name, + }, + Spec: appsv1.ReplicaSetSpec{ + Template: deployment.Spec.Template, + Selector: deployment.Spec.Selector, + }, + Status: appsv1.ReplicaSetStatus{ + Replicas: 1, + }, + } + + rs.SetOwnerReferences([]metav1.OwnerReference{{ + UID: deployment.UID, + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "Deployment", + Name: deployment.Name, + }}) + + scheme, err := kube.CreateScheme() + g.Expect(err).To(BeNil()) + + client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(&ns, deployment, rs).Build() + cfg := makeServerConfig(client, t, "") + c := makeServer(cfg, t) + + res, err := c.GetChildObjects(ctx, &pb.GetChildObjectsRequest{ + ParentUid: string(deployment.UID), + Namespace: ns.Name, + GroupVersionKind: &pb.GroupVersionKind{ + Group: "apps", + Version: "v1", + Kind: "ReplicaSet", + }, + ClusterName: cluster.DefaultCluster, + }) + + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(res.Objects).To(HaveLen(1)) + + first := res.Objects[0] + g.Expect(first.Payload).To(ContainSubstring("ReplicaSet")) + g.Expect(first.Payload).To(ContainSubstring(rs.Name)) +} + +func TestListFluxRuntimeObjects(t *testing.T) { + g := NewGomegaWithT(t) + + ctx := context.Background() + + tests := []struct { + description string + objects []runtime.Object + assertions func(*pb.ListFluxRuntimeObjectsResponse) + }{ + { + "no flux runtime", + []runtime.Object{ + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "ns1"}}, + }, + func(res *pb.ListFluxRuntimeObjectsResponse) { + g.Expect(res.Errors[0].Message).To(Equal(server.ErrFluxNamespaceNotFound.Error())) + g.Expect(res.Errors[0].Namespace).To(BeEmpty()) + g.Expect(res.Errors[0].ClusterName).To(Equal(cluster.DefaultCluster)) + }, + }, + { + "flux namespace label, with controllers", + []runtime.Object{ + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ + coretypes.PartOfLabel: server.FluxNamespacePartOf, + }}}, + newDeployment("random-flux-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), + newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), + }, + func(res *pb.ListFluxRuntimeObjectsResponse) { + g.Expect(res.Deployments).To(HaveLen(1), "expected deployments in the flux namespace to be returned") + g.Expect(res.Deployments[0].Name).To(Equal("random-flux-controller")) + }, + }, + { + "use flux-system namespace when no namespace label available", + []runtime.Object{ + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-system"}}, + newDeployment("random-flux-controller", "flux-system", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), + newDeployment("other-controller-in-flux-ns", "flux-system", map[string]string{}), + }, + func(res *pb.ListFluxRuntimeObjectsResponse) { + g.Expect(res.Deployments).To(HaveLen(1), "expected deployments in the default flux namespace to be returned") + g.Expect(res.Deployments[0].Name).To(Equal("random-flux-controller")) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + scheme, err := kube.CreateScheme() + g.Expect(err).To(BeNil()) + client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tt.objects...).Build() + cfg := makeServerConfig(client, t, "") + c := makeServer(cfg, t) + res, err := c.ListFluxRuntimeObjects(ctx, &pb.ListFluxRuntimeObjectsRequest{}) + g.Expect(err).NotTo(HaveOccurred()) + + tt.assertions(res) + }) + } +} + +func newDeployment(name, ns string, labels map[string]string) *appsv1.Deployment { + return &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + Labels: labels, + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + coretypes.AppLabel: name, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{coretypes.AppLabel: name}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Name: "nginx", + Image: "nginx", + }}, + }, + }, + }, + } +} + +func TestListFluxCrds(t *testing.T) { + g := NewGomegaWithT(t) + + ctx := context.Background() + + crd1 := &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ + Name: "crd1", + Labels: map[string]string{coretypes.PartOfLabel: "flux"}, + }, Spec: apiextensions.CustomResourceDefinitionSpec{ + Group: "group", + Names: apiextensions.CustomResourceDefinitionNames{Plural: "plural", Kind: "kind"}, + Versions: []apiextensions.CustomResourceDefinitionVersion{}, + }} + crd2 := &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ + Name: "crd2", + Labels: map[string]string{coretypes.PartOfLabel: "flux"}, + }, Spec: apiextensions.CustomResourceDefinitionSpec{ + Group: "group", + Versions: []apiextensions.CustomResourceDefinitionVersion{ + {Name: "0"}, + // "Active" version in etcd, use this one. + {Name: "1", Storage: true}, + }, + }} + scheme, err := kube.CreateScheme() + g.Expect(err).To(BeNil()) + + client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(crd1, crd2).Build() + cfg := makeServerConfig(client, t, "") + c := makeServer(cfg, t) + + res, err := c.ListFluxCrds(ctx, &pb.ListFluxCrdsRequest{}) + + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(res.Crds).To(HaveLen(2)) + + first := res.Crds[0] + g.Expect(first.Version).To(Equal("")) + g.Expect(first.Name.Plural).To(Equal("plural")) + g.Expect(first.Name.Group).To(Equal("group")) + g.Expect(first.Kind).To(Equal("kind")) + g.Expect(first.ClusterName).To(Equal(cluster.DefaultCluster)) + g.Expect(res.Crds[1].Version).To(Equal("1")) +} diff --git a/core/server/fluxruntime_test.go b/core/server/fluxruntime_test.go index 36c3e64dc1..a8e7d13cc3 100644 --- a/core/server/fluxruntime_test.go +++ b/core/server/fluxruntime_test.go @@ -1,465 +1,30 @@ -package server_test +package server import ( - "context" - "encoding/json" - "fmt" "testing" - kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" - . "github.com/onsi/gomega" - "github.com/weaveworks/weave-gitops/core/clustersmngr/cluster" - "github.com/weaveworks/weave-gitops/core/server" - coretypes "github.com/weaveworks/weave-gitops/core/server/types" - pb "github.com/weaveworks/weave-gitops/pkg/api/core" - "github.com/weaveworks/weave-gitops/pkg/kube" - "google.golang.org/grpc/metadata" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" - apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" + "github.com/google/go-cmp/cmp" ) -func TestGetReconciledObjects(t *testing.T) { - g := NewGomegaWithT(t) - - ctx := context.Background() - - c := makeGRPCServer(k8sEnv.Rest, t) - - scheme, err := kube.CreateScheme() - g.Expect(err).To(BeNil()) - - k, err := client.New(k8sEnv.Rest, client.Options{ - Scheme: scheme, - }) - g.Expect(err).NotTo(HaveOccurred()) - - automationName := "my-automation" - ns1 := newNamespace(ctx, k, g) - ns2 := newNamespace(ctx, k, g) - - reconciledObjs := []client.Object{ - &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-deployment", - Namespace: ns1.Name, - Labels: map[string]string{ - server.KustomizeNameKey: automationName, - server.KustomizeNamespaceKey: ns1.Name, - }, - }, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - coretypes.AppLabel: automationName, - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{coretypes.AppLabel: automationName}, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: "nginx", - Image: "nginx", - }}, - }, - }, - }, - }, - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-configmap", - Namespace: ns2.Name, - Labels: map[string]string{ - server.KustomizeNameKey: automationName, - server.KustomizeNamespaceKey: ns1.Name, - }, - }, - }, - } - - for _, obj := range reconciledObjs { - g.Expect(k.Create(ctx, obj)).Should(Succeed()) - } - - crb := rbacv1.RoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns1.Name, - Name: "ns-admin", - }, - RoleRef: rbacv1.RoleRef{ - APIGroup: rbacv1.SchemeGroupVersion.Group, - Kind: "ClusterRole", - Name: "cluster-admin", - }, - Subjects: []rbacv1.Subject{{ - APIGroup: rbacv1.SchemeGroupVersion.Group, - Kind: rbacv1.UserKind, - Name: "ns-admin", - }}, - } - g.Expect(k.Create(ctx, &crb)).Should((Succeed())) - - type objectAssertion struct { - kind string - name string - } - +func Test_getRuntimeLabels(t *testing.T) { tests := []struct { - name string - user string - group string - expectedLen int - expectedObjects []objectAssertion + name string + gitopsRuntimeFeatureFlag string + wantRuntimeLabels []string }{ { - name: "unknown user doesn't receive any objects", - user: "anne", - expectedLen: 0, - }, - { - name: "ns-admin sees only objects in their namespace", - user: "ns-admin", - expectedLen: 1, - expectedObjects: []objectAssertion{ - { - kind: "Deployment", - name: reconciledObjs[0].GetName(), - }, - }, - }, - { - name: "master user receives all objects", - user: "anne", - group: "system:masters", - expectedLen: 2, - expectedObjects: []objectAssertion{ - { - kind: "Deployment", - name: reconciledObjs[0].GetName(), - }, - { - kind: "ConfigMap", - name: reconciledObjs[1].GetName(), - }, + name: "should return flux if not feature flag exists", + wantRuntimeLabels: []string{ + FluxNamespacePartOf, }, }, } - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - g = NewGomegaWithT(t) - - md := metadata.Pairs(MetadataUserKey, tt.user, MetadataGroupsKey, tt.group) - outgoingCtx := metadata.NewOutgoingContext(ctx, md) - res, err := c.GetReconciledObjects(outgoingCtx, &pb.GetReconciledObjectsRequest{ - AutomationName: automationName, - Namespace: ns1.Name, - AutomationKind: kustomizev1.KustomizationKind, - Kinds: []*pb.GroupVersionKind{ - {Group: appsv1.SchemeGroupVersion.Group, Version: appsv1.SchemeGroupVersion.Version, Kind: "Deployment"}, - {Group: corev1.SchemeGroupVersion.Group, Version: corev1.SchemeGroupVersion.Version, Kind: "ConfigMap"}, - }, - ClusterName: cluster.DefaultCluster, - }) - - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(res.Objects).To(HaveLen(tt.expectedLen), "unexpected size of returned object list") - - actualObjs := make([]objectAssertion, len(res.Objects)) - - for idx, actualObj := range res.Objects { - var object map[string]interface{} - - g.Expect(json.Unmarshal([]byte(actualObj.Payload), &object)).To(Succeed(), "failed unmarshalling result object") - metadata, ok := object["metadata"].(map[string]interface{}) - g.Expect(ok).To(BeTrue(), "object has unexpected metadata type") - actualObjs[idx] = objectAssertion{ - kind: object["kind"].(string), - name: metadata["name"].(string), - } + gotLabels := getRuntimeLabels() + if diff := cmp.Diff(tt.wantRuntimeLabels, gotLabels); diff != "" { + t.Fatalf("unexpected labels:\n%s", diff) } - g.Expect(actualObjs).To(ContainElements(tt.expectedObjects)) - }) - } -} - -func TestGetReconciledObjectsWithSecret(t *testing.T) { - g := NewGomegaWithT(t) - - ctx := context.Background() - - c := makeGRPCServer(k8sEnv.Rest, t) - - scheme, err := kube.CreateScheme() - g.Expect(err).To(BeNil()) - - k, err := client.New(k8sEnv.Rest, client.Options{ - Scheme: scheme, - }) - g.Expect(err).NotTo(HaveOccurred()) - - automationName := "my-automation" - ns := newNamespace(ctx, k, g) - - reconciledObj := corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-secret", - Namespace: ns.Name, - UID: "this-is-not-an-uid", - Labels: map[string]string{ - server.KustomizeNameKey: automationName, - server.KustomizeNamespaceKey: ns.Name, - }, - }, - TypeMeta: metav1.TypeMeta{Kind: "Secret", APIVersion: "v1"}, - Data: map[string][]byte{"username": []byte("username"), "password": []byte("password")}, - } - - g.Expect(k.Create(ctx, &reconciledObj)).Should(Succeed()) - - md := metadata.Pairs(MetadataUserKey, "anne", MetadataGroupsKey, "system:masters") - outgoingCtx := metadata.NewOutgoingContext(ctx, md) - res, err := c.GetReconciledObjects(outgoingCtx, &pb.GetReconciledObjectsRequest{ - AutomationName: automationName, - Namespace: ns.Name, - AutomationKind: kustomizev1.KustomizationKind, - Kinds: []*pb.GroupVersionKind{{Group: "", Version: "v1", Kind: "Secret"}}, - ClusterName: cluster.DefaultCluster, - }) - - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(res.Objects).To(HaveLen(1)) - - first := res.Objects[0] - g.Expect(first.Payload).To(ContainSubstring("redacted")) -} - -func TestGetChildObjects(t *testing.T) { - g := NewGomegaWithT(t) - - ctx := context.Background() - - automationName := "my-automation" - - ns := corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-namespace", - }, - } - - deployment := &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-deployment", - Namespace: ns.Name, - UID: "this-is-not-an-uid", - }, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - coretypes.AppLabel: automationName, - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{coretypes.AppLabel: automationName}, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: "nginx", - Image: "nginx", - }}, - }, - }, - }, - } - - rs := &appsv1.ReplicaSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-123abcd", automationName), - Namespace: ns.Name, - }, - Spec: appsv1.ReplicaSetSpec{ - Template: deployment.Spec.Template, - Selector: deployment.Spec.Selector, - }, - Status: appsv1.ReplicaSetStatus{ - Replicas: 1, - }, - } - - rs.SetOwnerReferences([]metav1.OwnerReference{{ - UID: deployment.UID, - APIVersion: appsv1.SchemeGroupVersion.String(), - Kind: "Deployment", - Name: deployment.Name, - }}) - - scheme, err := kube.CreateScheme() - g.Expect(err).To(BeNil()) - - client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(&ns, deployment, rs).Build() - cfg := makeServerConfig(client, t, "") - c := makeServer(cfg, t) - - res, err := c.GetChildObjects(ctx, &pb.GetChildObjectsRequest{ - ParentUid: string(deployment.UID), - Namespace: ns.Name, - GroupVersionKind: &pb.GroupVersionKind{ - Group: "apps", - Version: "v1", - Kind: "ReplicaSet", - }, - ClusterName: cluster.DefaultCluster, - }) - - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(res.Objects).To(HaveLen(1)) - - first := res.Objects[0] - g.Expect(first.Payload).To(ContainSubstring("ReplicaSet")) - g.Expect(first.Payload).To(ContainSubstring(rs.Name)) -} - -func TestListFluxRuntimeObjects(t *testing.T) { - g := NewGomegaWithT(t) - - ctx := context.Background() - - tests := []struct { - description string - objects []runtime.Object - assertions func(*pb.ListFluxRuntimeObjectsResponse) - }{ - { - "no flux runtime", - []runtime.Object{ - &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "ns1"}}, - }, - func(res *pb.ListFluxRuntimeObjectsResponse) { - g.Expect(res.Errors[0].Message).To(Equal(server.ErrFluxNamespaceNotFound.Error())) - g.Expect(res.Errors[0].Namespace).To(BeEmpty()) - g.Expect(res.Errors[0].ClusterName).To(Equal(cluster.DefaultCluster)) - }, - }, - { - "flux namespace label, with controllers", - []runtime.Object{ - &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ - coretypes.PartOfLabel: server.FluxNamespacePartOf, - }}}, - newDeployment("random-flux-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), - newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), - }, - func(res *pb.ListFluxRuntimeObjectsResponse) { - g.Expect(res.Deployments).To(HaveLen(1), "expected deployments in the flux namespace to be returned") - g.Expect(res.Deployments[0].Name).To(Equal("random-flux-controller")) - }, - }, - { - "use flux-system namespace when no namespace label available", - []runtime.Object{ - &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-system"}}, - newDeployment("random-flux-controller", "flux-system", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), - newDeployment("other-controller-in-flux-ns", "flux-system", map[string]string{}), - }, - func(res *pb.ListFluxRuntimeObjectsResponse) { - g.Expect(res.Deployments).To(HaveLen(1), "expected deployments in the default flux namespace to be returned") - g.Expect(res.Deployments[0].Name).To(Equal("random-flux-controller")) - }, - }, - } - - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - scheme, err := kube.CreateScheme() - g.Expect(err).To(BeNil()) - client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tt.objects...).Build() - cfg := makeServerConfig(client, t, "") - c := makeServer(cfg, t) - res, err := c.ListFluxRuntimeObjects(ctx, &pb.ListFluxRuntimeObjectsRequest{}) - g.Expect(err).NotTo(HaveOccurred()) - - tt.assertions(res) }) } } - -func newDeployment(name, ns string, labels map[string]string) *appsv1.Deployment { - return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: ns, - Labels: labels, - }, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - coretypes.AppLabel: name, - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{coretypes.AppLabel: name}, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: "nginx", - Image: "nginx", - }}, - }, - }, - }, - } -} - -func TestListFluxCrds(t *testing.T) { - g := NewGomegaWithT(t) - - ctx := context.Background() - - crd1 := &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ - Name: "crd1", - Labels: map[string]string{coretypes.PartOfLabel: "flux"}, - }, Spec: apiextensions.CustomResourceDefinitionSpec{ - Group: "group", - Names: apiextensions.CustomResourceDefinitionNames{Plural: "plural", Kind: "kind"}, - Versions: []apiextensions.CustomResourceDefinitionVersion{}, - }} - crd2 := &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ - Name: "crd2", - Labels: map[string]string{coretypes.PartOfLabel: "flux"}, - }, Spec: apiextensions.CustomResourceDefinitionSpec{ - Group: "group", - Versions: []apiextensions.CustomResourceDefinitionVersion{ - {Name: "0"}, - // "Active" version in etcd, use this one. - {Name: "1", Storage: true}, - }, - }} - scheme, err := kube.CreateScheme() - g.Expect(err).To(BeNil()) - - client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(crd1, crd2).Build() - cfg := makeServerConfig(client, t, "") - c := makeServer(cfg, t) - - res, err := c.ListFluxCrds(ctx, &pb.ListFluxCrdsRequest{}) - - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(res.Crds).To(HaveLen(2)) - - first := res.Crds[0] - g.Expect(first.Version).To(Equal("")) - g.Expect(first.Name.Plural).To(Equal("plural")) - g.Expect(first.Name.Group).To(Equal("group")) - g.Expect(first.Kind).To(Equal("kind")) - g.Expect(first.ClusterName).To(Equal(cluster.DefaultCluster)) - g.Expect(res.Crds[1].Version).To(Equal("1")) -} From 3b9896f8cc35fff5c01b1c463a82cd9e1d5cf00b Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 15 Dec 2023 12:08:36 +0100 Subject: [PATCH 04/43] can set configuration via feature flag Signed-off-by: Eneko Fernandez --- core/server/fluxruntime.go | 21 ++++++++++++++------- core/server/fluxruntime_test.go | 30 ++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/core/server/fluxruntime.go b/core/server/fluxruntime.go index 93bb30098a..4dd0cf1abb 100644 --- a/core/server/fluxruntime.go +++ b/core/server/fluxruntime.go @@ -13,6 +13,7 @@ import ( "github.com/weaveworks/weave-gitops/core/logger" coretypes "github.com/weaveworks/weave-gitops/core/server/types" pb "github.com/weaveworks/weave-gitops/pkg/api/core" + "github.com/weaveworks/weave-gitops/pkg/featureflags" "github.com/weaveworks/weave-gitops/pkg/server/auth" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -28,8 +29,9 @@ import ( ) const ( - FluxNamespacePartOf = "flux" - PartOfWeaveGitops = "weave-gitops" + FluxNamespacePartOf = "flux" + PartOfWeaveGitops = "weave-gitops" + GitopsRuntimeFeatureFlag = "WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME" ) var ( @@ -46,7 +48,11 @@ var ( DefaultFluxNamespace = lookupEnv("WEAVE_GITOPS_FALLBACK_NAMESPACE", "flux-system") ) -var RuntimeLabels = []string{ +var FluxRuntimeLabels = []string{ + FluxNamespacePartOf, +} + +var WeaveGitopsRuntimeLabels = []string{ FluxNamespacePartOf, PartOfWeaveGitops, } @@ -74,9 +80,7 @@ func (cs *coreServer) ListFluxRuntimeObjects(ctx context.Context, msg *pb.ListFl var results []*pb.Deployment - getRuntimeLabels() - - for _, runtimeLabel := range RuntimeLabels { + for _, runtimeLabel := range getRuntimeLabels() { for clusterName, nss := range cs.clustersManager.GetClustersNamespaces() { fluxNamespaces := filterFluxNamespace(nss) if len(fluxNamespaces) == 0 { @@ -132,7 +136,10 @@ func (cs *coreServer) ListFluxRuntimeObjects(ctx context.Context, msg *pb.ListFl // getRuntimeLabels returns the labels that are used to identify the runtime objects based on // whether the user has enabled `WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME` or not func getRuntimeLabels() []string { - return []string{} + if featureflags.Get(GitopsRuntimeFeatureFlag) == "true" { + return WeaveGitopsRuntimeLabels + } + return FluxRuntimeLabels } func (cs *coreServer) ListFluxCrds(ctx context.Context, msg *pb.ListFluxCrdsRequest) (*pb.ListFluxCrdsResponse, error) { diff --git a/core/server/fluxruntime_test.go b/core/server/fluxruntime_test.go index a8e7d13cc3..45ed651ad8 100644 --- a/core/server/fluxruntime_test.go +++ b/core/server/fluxruntime_test.go @@ -1,9 +1,11 @@ package server import ( + "os" "testing" "github.com/google/go-cmp/cmp" + "github.com/weaveworks/weave-gitops/pkg/featureflags" ) func Test_getRuntimeLabels(t *testing.T) { @@ -13,14 +15,34 @@ func Test_getRuntimeLabels(t *testing.T) { wantRuntimeLabels []string }{ { - name: "should return flux if not feature flag exists", - wantRuntimeLabels: []string{ - FluxNamespacePartOf, - }, + name: "should return flux if not feature flag exists", + wantRuntimeLabels: FluxRuntimeLabels, + }, + { + name: "should return flux if feature flag exists but empty", + wantRuntimeLabels: FluxRuntimeLabels, + gitopsRuntimeFeatureFlag: "", + }, + { + name: "should return flux if feature flag exists and false", + wantRuntimeLabels: FluxRuntimeLabels, + gitopsRuntimeFeatureFlag: "false", + }, + { + name: "should return weave gitops if feature flag exists and true", + wantRuntimeLabels: WeaveGitopsRuntimeLabels, + gitopsRuntimeFeatureFlag: "true", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + if tt.gitopsRuntimeFeatureFlag != "" { + _ = os.Setenv(GitopsRuntimeFeatureFlag, tt.gitopsRuntimeFeatureFlag) + } + defer func() { + _ = os.Unsetenv(GitopsRuntimeFeatureFlag) + }() + featureflags.SetFromEnv(os.Environ()) gotLabels := getRuntimeLabels() if diff := cmp.Diff(tt.wantRuntimeLabels, gotLabels); diff != "" { t.Fatalf("unexpected labels:\n%s", diff) From 2943b3fa5b6d981a72a7521ec52ea954fc8519d2 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 15 Dec 2023 14:16:51 +0100 Subject: [PATCH 05/43] refactor file names Signed-off-by: Eneko Fernandez --- core/server/fluxruntime_integration_test.go | 465 ------------------- core/server/fluxruntime_internal_test.go | 52 +++ core/server/fluxruntime_test.go | 475 ++++++++++++++++++-- 3 files changed, 496 insertions(+), 496 deletions(-) delete mode 100644 core/server/fluxruntime_integration_test.go create mode 100644 core/server/fluxruntime_internal_test.go diff --git a/core/server/fluxruntime_integration_test.go b/core/server/fluxruntime_integration_test.go deleted file mode 100644 index 36c3e64dc1..0000000000 --- a/core/server/fluxruntime_integration_test.go +++ /dev/null @@ -1,465 +0,0 @@ -package server_test - -import ( - "context" - "encoding/json" - "fmt" - "testing" - - kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" - . "github.com/onsi/gomega" - "github.com/weaveworks/weave-gitops/core/clustersmngr/cluster" - "github.com/weaveworks/weave-gitops/core/server" - coretypes "github.com/weaveworks/weave-gitops/core/server/types" - pb "github.com/weaveworks/weave-gitops/pkg/api/core" - "github.com/weaveworks/weave-gitops/pkg/kube" - "google.golang.org/grpc/metadata" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" - apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -func TestGetReconciledObjects(t *testing.T) { - g := NewGomegaWithT(t) - - ctx := context.Background() - - c := makeGRPCServer(k8sEnv.Rest, t) - - scheme, err := kube.CreateScheme() - g.Expect(err).To(BeNil()) - - k, err := client.New(k8sEnv.Rest, client.Options{ - Scheme: scheme, - }) - g.Expect(err).NotTo(HaveOccurred()) - - automationName := "my-automation" - ns1 := newNamespace(ctx, k, g) - ns2 := newNamespace(ctx, k, g) - - reconciledObjs := []client.Object{ - &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-deployment", - Namespace: ns1.Name, - Labels: map[string]string{ - server.KustomizeNameKey: automationName, - server.KustomizeNamespaceKey: ns1.Name, - }, - }, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - coretypes.AppLabel: automationName, - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{coretypes.AppLabel: automationName}, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: "nginx", - Image: "nginx", - }}, - }, - }, - }, - }, - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-configmap", - Namespace: ns2.Name, - Labels: map[string]string{ - server.KustomizeNameKey: automationName, - server.KustomizeNamespaceKey: ns1.Name, - }, - }, - }, - } - - for _, obj := range reconciledObjs { - g.Expect(k.Create(ctx, obj)).Should(Succeed()) - } - - crb := rbacv1.RoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns1.Name, - Name: "ns-admin", - }, - RoleRef: rbacv1.RoleRef{ - APIGroup: rbacv1.SchemeGroupVersion.Group, - Kind: "ClusterRole", - Name: "cluster-admin", - }, - Subjects: []rbacv1.Subject{{ - APIGroup: rbacv1.SchemeGroupVersion.Group, - Kind: rbacv1.UserKind, - Name: "ns-admin", - }}, - } - g.Expect(k.Create(ctx, &crb)).Should((Succeed())) - - type objectAssertion struct { - kind string - name string - } - - tests := []struct { - name string - user string - group string - expectedLen int - expectedObjects []objectAssertion - }{ - { - name: "unknown user doesn't receive any objects", - user: "anne", - expectedLen: 0, - }, - { - name: "ns-admin sees only objects in their namespace", - user: "ns-admin", - expectedLen: 1, - expectedObjects: []objectAssertion{ - { - kind: "Deployment", - name: reconciledObjs[0].GetName(), - }, - }, - }, - { - name: "master user receives all objects", - user: "anne", - group: "system:masters", - expectedLen: 2, - expectedObjects: []objectAssertion{ - { - kind: "Deployment", - name: reconciledObjs[0].GetName(), - }, - { - kind: "ConfigMap", - name: reconciledObjs[1].GetName(), - }, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g = NewGomegaWithT(t) - - md := metadata.Pairs(MetadataUserKey, tt.user, MetadataGroupsKey, tt.group) - outgoingCtx := metadata.NewOutgoingContext(ctx, md) - res, err := c.GetReconciledObjects(outgoingCtx, &pb.GetReconciledObjectsRequest{ - AutomationName: automationName, - Namespace: ns1.Name, - AutomationKind: kustomizev1.KustomizationKind, - Kinds: []*pb.GroupVersionKind{ - {Group: appsv1.SchemeGroupVersion.Group, Version: appsv1.SchemeGroupVersion.Version, Kind: "Deployment"}, - {Group: corev1.SchemeGroupVersion.Group, Version: corev1.SchemeGroupVersion.Version, Kind: "ConfigMap"}, - }, - ClusterName: cluster.DefaultCluster, - }) - - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(res.Objects).To(HaveLen(tt.expectedLen), "unexpected size of returned object list") - - actualObjs := make([]objectAssertion, len(res.Objects)) - - for idx, actualObj := range res.Objects { - var object map[string]interface{} - - g.Expect(json.Unmarshal([]byte(actualObj.Payload), &object)).To(Succeed(), "failed unmarshalling result object") - metadata, ok := object["metadata"].(map[string]interface{}) - g.Expect(ok).To(BeTrue(), "object has unexpected metadata type") - actualObjs[idx] = objectAssertion{ - kind: object["kind"].(string), - name: metadata["name"].(string), - } - } - g.Expect(actualObjs).To(ContainElements(tt.expectedObjects)) - }) - } -} - -func TestGetReconciledObjectsWithSecret(t *testing.T) { - g := NewGomegaWithT(t) - - ctx := context.Background() - - c := makeGRPCServer(k8sEnv.Rest, t) - - scheme, err := kube.CreateScheme() - g.Expect(err).To(BeNil()) - - k, err := client.New(k8sEnv.Rest, client.Options{ - Scheme: scheme, - }) - g.Expect(err).NotTo(HaveOccurred()) - - automationName := "my-automation" - ns := newNamespace(ctx, k, g) - - reconciledObj := corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-secret", - Namespace: ns.Name, - UID: "this-is-not-an-uid", - Labels: map[string]string{ - server.KustomizeNameKey: automationName, - server.KustomizeNamespaceKey: ns.Name, - }, - }, - TypeMeta: metav1.TypeMeta{Kind: "Secret", APIVersion: "v1"}, - Data: map[string][]byte{"username": []byte("username"), "password": []byte("password")}, - } - - g.Expect(k.Create(ctx, &reconciledObj)).Should(Succeed()) - - md := metadata.Pairs(MetadataUserKey, "anne", MetadataGroupsKey, "system:masters") - outgoingCtx := metadata.NewOutgoingContext(ctx, md) - res, err := c.GetReconciledObjects(outgoingCtx, &pb.GetReconciledObjectsRequest{ - AutomationName: automationName, - Namespace: ns.Name, - AutomationKind: kustomizev1.KustomizationKind, - Kinds: []*pb.GroupVersionKind{{Group: "", Version: "v1", Kind: "Secret"}}, - ClusterName: cluster.DefaultCluster, - }) - - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(res.Objects).To(HaveLen(1)) - - first := res.Objects[0] - g.Expect(first.Payload).To(ContainSubstring("redacted")) -} - -func TestGetChildObjects(t *testing.T) { - g := NewGomegaWithT(t) - - ctx := context.Background() - - automationName := "my-automation" - - ns := corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-namespace", - }, - } - - deployment := &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-deployment", - Namespace: ns.Name, - UID: "this-is-not-an-uid", - }, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - coretypes.AppLabel: automationName, - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{coretypes.AppLabel: automationName}, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: "nginx", - Image: "nginx", - }}, - }, - }, - }, - } - - rs := &appsv1.ReplicaSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-123abcd", automationName), - Namespace: ns.Name, - }, - Spec: appsv1.ReplicaSetSpec{ - Template: deployment.Spec.Template, - Selector: deployment.Spec.Selector, - }, - Status: appsv1.ReplicaSetStatus{ - Replicas: 1, - }, - } - - rs.SetOwnerReferences([]metav1.OwnerReference{{ - UID: deployment.UID, - APIVersion: appsv1.SchemeGroupVersion.String(), - Kind: "Deployment", - Name: deployment.Name, - }}) - - scheme, err := kube.CreateScheme() - g.Expect(err).To(BeNil()) - - client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(&ns, deployment, rs).Build() - cfg := makeServerConfig(client, t, "") - c := makeServer(cfg, t) - - res, err := c.GetChildObjects(ctx, &pb.GetChildObjectsRequest{ - ParentUid: string(deployment.UID), - Namespace: ns.Name, - GroupVersionKind: &pb.GroupVersionKind{ - Group: "apps", - Version: "v1", - Kind: "ReplicaSet", - }, - ClusterName: cluster.DefaultCluster, - }) - - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(res.Objects).To(HaveLen(1)) - - first := res.Objects[0] - g.Expect(first.Payload).To(ContainSubstring("ReplicaSet")) - g.Expect(first.Payload).To(ContainSubstring(rs.Name)) -} - -func TestListFluxRuntimeObjects(t *testing.T) { - g := NewGomegaWithT(t) - - ctx := context.Background() - - tests := []struct { - description string - objects []runtime.Object - assertions func(*pb.ListFluxRuntimeObjectsResponse) - }{ - { - "no flux runtime", - []runtime.Object{ - &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "ns1"}}, - }, - func(res *pb.ListFluxRuntimeObjectsResponse) { - g.Expect(res.Errors[0].Message).To(Equal(server.ErrFluxNamespaceNotFound.Error())) - g.Expect(res.Errors[0].Namespace).To(BeEmpty()) - g.Expect(res.Errors[0].ClusterName).To(Equal(cluster.DefaultCluster)) - }, - }, - { - "flux namespace label, with controllers", - []runtime.Object{ - &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ - coretypes.PartOfLabel: server.FluxNamespacePartOf, - }}}, - newDeployment("random-flux-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), - newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), - }, - func(res *pb.ListFluxRuntimeObjectsResponse) { - g.Expect(res.Deployments).To(HaveLen(1), "expected deployments in the flux namespace to be returned") - g.Expect(res.Deployments[0].Name).To(Equal("random-flux-controller")) - }, - }, - { - "use flux-system namespace when no namespace label available", - []runtime.Object{ - &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-system"}}, - newDeployment("random-flux-controller", "flux-system", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), - newDeployment("other-controller-in-flux-ns", "flux-system", map[string]string{}), - }, - func(res *pb.ListFluxRuntimeObjectsResponse) { - g.Expect(res.Deployments).To(HaveLen(1), "expected deployments in the default flux namespace to be returned") - g.Expect(res.Deployments[0].Name).To(Equal("random-flux-controller")) - }, - }, - } - - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - scheme, err := kube.CreateScheme() - g.Expect(err).To(BeNil()) - client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tt.objects...).Build() - cfg := makeServerConfig(client, t, "") - c := makeServer(cfg, t) - res, err := c.ListFluxRuntimeObjects(ctx, &pb.ListFluxRuntimeObjectsRequest{}) - g.Expect(err).NotTo(HaveOccurred()) - - tt.assertions(res) - }) - } -} - -func newDeployment(name, ns string, labels map[string]string) *appsv1.Deployment { - return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: ns, - Labels: labels, - }, - Spec: appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - coretypes.AppLabel: name, - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{coretypes.AppLabel: name}, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: "nginx", - Image: "nginx", - }}, - }, - }, - }, - } -} - -func TestListFluxCrds(t *testing.T) { - g := NewGomegaWithT(t) - - ctx := context.Background() - - crd1 := &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ - Name: "crd1", - Labels: map[string]string{coretypes.PartOfLabel: "flux"}, - }, Spec: apiextensions.CustomResourceDefinitionSpec{ - Group: "group", - Names: apiextensions.CustomResourceDefinitionNames{Plural: "plural", Kind: "kind"}, - Versions: []apiextensions.CustomResourceDefinitionVersion{}, - }} - crd2 := &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ - Name: "crd2", - Labels: map[string]string{coretypes.PartOfLabel: "flux"}, - }, Spec: apiextensions.CustomResourceDefinitionSpec{ - Group: "group", - Versions: []apiextensions.CustomResourceDefinitionVersion{ - {Name: "0"}, - // "Active" version in etcd, use this one. - {Name: "1", Storage: true}, - }, - }} - scheme, err := kube.CreateScheme() - g.Expect(err).To(BeNil()) - - client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(crd1, crd2).Build() - cfg := makeServerConfig(client, t, "") - c := makeServer(cfg, t) - - res, err := c.ListFluxCrds(ctx, &pb.ListFluxCrdsRequest{}) - - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(res.Crds).To(HaveLen(2)) - - first := res.Crds[0] - g.Expect(first.Version).To(Equal("")) - g.Expect(first.Name.Plural).To(Equal("plural")) - g.Expect(first.Name.Group).To(Equal("group")) - g.Expect(first.Kind).To(Equal("kind")) - g.Expect(first.ClusterName).To(Equal(cluster.DefaultCluster)) - g.Expect(res.Crds[1].Version).To(Equal("1")) -} diff --git a/core/server/fluxruntime_internal_test.go b/core/server/fluxruntime_internal_test.go new file mode 100644 index 0000000000..45ed651ad8 --- /dev/null +++ b/core/server/fluxruntime_internal_test.go @@ -0,0 +1,52 @@ +package server + +import ( + "os" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/weaveworks/weave-gitops/pkg/featureflags" +) + +func Test_getRuntimeLabels(t *testing.T) { + tests := []struct { + name string + gitopsRuntimeFeatureFlag string + wantRuntimeLabels []string + }{ + { + name: "should return flux if not feature flag exists", + wantRuntimeLabels: FluxRuntimeLabels, + }, + { + name: "should return flux if feature flag exists but empty", + wantRuntimeLabels: FluxRuntimeLabels, + gitopsRuntimeFeatureFlag: "", + }, + { + name: "should return flux if feature flag exists and false", + wantRuntimeLabels: FluxRuntimeLabels, + gitopsRuntimeFeatureFlag: "false", + }, + { + name: "should return weave gitops if feature flag exists and true", + wantRuntimeLabels: WeaveGitopsRuntimeLabels, + gitopsRuntimeFeatureFlag: "true", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.gitopsRuntimeFeatureFlag != "" { + _ = os.Setenv(GitopsRuntimeFeatureFlag, tt.gitopsRuntimeFeatureFlag) + } + defer func() { + _ = os.Unsetenv(GitopsRuntimeFeatureFlag) + }() + featureflags.SetFromEnv(os.Environ()) + gotLabels := getRuntimeLabels() + if diff := cmp.Diff(tt.wantRuntimeLabels, gotLabels); diff != "" { + t.Fatalf("unexpected labels:\n%s", diff) + } + }) + } +} diff --git a/core/server/fluxruntime_test.go b/core/server/fluxruntime_test.go index 45ed651ad8..36c3e64dc1 100644 --- a/core/server/fluxruntime_test.go +++ b/core/server/fluxruntime_test.go @@ -1,52 +1,465 @@ -package server +package server_test import ( - "os" + "context" + "encoding/json" + "fmt" "testing" - "github.com/google/go-cmp/cmp" - "github.com/weaveworks/weave-gitops/pkg/featureflags" + kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" + . "github.com/onsi/gomega" + "github.com/weaveworks/weave-gitops/core/clustersmngr/cluster" + "github.com/weaveworks/weave-gitops/core/server" + coretypes "github.com/weaveworks/weave-gitops/core/server/types" + pb "github.com/weaveworks/weave-gitops/pkg/api/core" + "github.com/weaveworks/weave-gitops/pkg/kube" + "google.golang.org/grpc/metadata" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" ) -func Test_getRuntimeLabels(t *testing.T) { +func TestGetReconciledObjects(t *testing.T) { + g := NewGomegaWithT(t) + + ctx := context.Background() + + c := makeGRPCServer(k8sEnv.Rest, t) + + scheme, err := kube.CreateScheme() + g.Expect(err).To(BeNil()) + + k, err := client.New(k8sEnv.Rest, client.Options{ + Scheme: scheme, + }) + g.Expect(err).NotTo(HaveOccurred()) + + automationName := "my-automation" + ns1 := newNamespace(ctx, k, g) + ns2 := newNamespace(ctx, k, g) + + reconciledObjs := []client.Object{ + &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-deployment", + Namespace: ns1.Name, + Labels: map[string]string{ + server.KustomizeNameKey: automationName, + server.KustomizeNamespaceKey: ns1.Name, + }, + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + coretypes.AppLabel: automationName, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{coretypes.AppLabel: automationName}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Name: "nginx", + Image: "nginx", + }}, + }, + }, + }, + }, + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-configmap", + Namespace: ns2.Name, + Labels: map[string]string{ + server.KustomizeNameKey: automationName, + server.KustomizeNamespaceKey: ns1.Name, + }, + }, + }, + } + + for _, obj := range reconciledObjs { + g.Expect(k.Create(ctx, obj)).Should(Succeed()) + } + + crb := rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns1.Name, + Name: "ns-admin", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.SchemeGroupVersion.Group, + Kind: "ClusterRole", + Name: "cluster-admin", + }, + Subjects: []rbacv1.Subject{{ + APIGroup: rbacv1.SchemeGroupVersion.Group, + Kind: rbacv1.UserKind, + Name: "ns-admin", + }}, + } + g.Expect(k.Create(ctx, &crb)).Should((Succeed())) + + type objectAssertion struct { + kind string + name string + } + tests := []struct { - name string - gitopsRuntimeFeatureFlag string - wantRuntimeLabels []string + name string + user string + group string + expectedLen int + expectedObjects []objectAssertion }{ { - name: "should return flux if not feature flag exists", - wantRuntimeLabels: FluxRuntimeLabels, + name: "unknown user doesn't receive any objects", + user: "anne", + expectedLen: 0, }, { - name: "should return flux if feature flag exists but empty", - wantRuntimeLabels: FluxRuntimeLabels, - gitopsRuntimeFeatureFlag: "", + name: "ns-admin sees only objects in their namespace", + user: "ns-admin", + expectedLen: 1, + expectedObjects: []objectAssertion{ + { + kind: "Deployment", + name: reconciledObjs[0].GetName(), + }, + }, }, { - name: "should return flux if feature flag exists and false", - wantRuntimeLabels: FluxRuntimeLabels, - gitopsRuntimeFeatureFlag: "false", - }, - { - name: "should return weave gitops if feature flag exists and true", - wantRuntimeLabels: WeaveGitopsRuntimeLabels, - gitopsRuntimeFeatureFlag: "true", + name: "master user receives all objects", + user: "anne", + group: "system:masters", + expectedLen: 2, + expectedObjects: []objectAssertion{ + { + kind: "Deployment", + name: reconciledObjs[0].GetName(), + }, + { + kind: "ConfigMap", + name: reconciledObjs[1].GetName(), + }, + }, }, } + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if tt.gitopsRuntimeFeatureFlag != "" { - _ = os.Setenv(GitopsRuntimeFeatureFlag, tt.gitopsRuntimeFeatureFlag) - } - defer func() { - _ = os.Unsetenv(GitopsRuntimeFeatureFlag) - }() - featureflags.SetFromEnv(os.Environ()) - gotLabels := getRuntimeLabels() - if diff := cmp.Diff(tt.wantRuntimeLabels, gotLabels); diff != "" { - t.Fatalf("unexpected labels:\n%s", diff) + g = NewGomegaWithT(t) + + md := metadata.Pairs(MetadataUserKey, tt.user, MetadataGroupsKey, tt.group) + outgoingCtx := metadata.NewOutgoingContext(ctx, md) + res, err := c.GetReconciledObjects(outgoingCtx, &pb.GetReconciledObjectsRequest{ + AutomationName: automationName, + Namespace: ns1.Name, + AutomationKind: kustomizev1.KustomizationKind, + Kinds: []*pb.GroupVersionKind{ + {Group: appsv1.SchemeGroupVersion.Group, Version: appsv1.SchemeGroupVersion.Version, Kind: "Deployment"}, + {Group: corev1.SchemeGroupVersion.Group, Version: corev1.SchemeGroupVersion.Version, Kind: "ConfigMap"}, + }, + ClusterName: cluster.DefaultCluster, + }) + + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(res.Objects).To(HaveLen(tt.expectedLen), "unexpected size of returned object list") + + actualObjs := make([]objectAssertion, len(res.Objects)) + + for idx, actualObj := range res.Objects { + var object map[string]interface{} + + g.Expect(json.Unmarshal([]byte(actualObj.Payload), &object)).To(Succeed(), "failed unmarshalling result object") + metadata, ok := object["metadata"].(map[string]interface{}) + g.Expect(ok).To(BeTrue(), "object has unexpected metadata type") + actualObjs[idx] = objectAssertion{ + kind: object["kind"].(string), + name: metadata["name"].(string), + } } + g.Expect(actualObjs).To(ContainElements(tt.expectedObjects)) + }) + } +} + +func TestGetReconciledObjectsWithSecret(t *testing.T) { + g := NewGomegaWithT(t) + + ctx := context.Background() + + c := makeGRPCServer(k8sEnv.Rest, t) + + scheme, err := kube.CreateScheme() + g.Expect(err).To(BeNil()) + + k, err := client.New(k8sEnv.Rest, client.Options{ + Scheme: scheme, + }) + g.Expect(err).NotTo(HaveOccurred()) + + automationName := "my-automation" + ns := newNamespace(ctx, k, g) + + reconciledObj := corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-secret", + Namespace: ns.Name, + UID: "this-is-not-an-uid", + Labels: map[string]string{ + server.KustomizeNameKey: automationName, + server.KustomizeNamespaceKey: ns.Name, + }, + }, + TypeMeta: metav1.TypeMeta{Kind: "Secret", APIVersion: "v1"}, + Data: map[string][]byte{"username": []byte("username"), "password": []byte("password")}, + } + + g.Expect(k.Create(ctx, &reconciledObj)).Should(Succeed()) + + md := metadata.Pairs(MetadataUserKey, "anne", MetadataGroupsKey, "system:masters") + outgoingCtx := metadata.NewOutgoingContext(ctx, md) + res, err := c.GetReconciledObjects(outgoingCtx, &pb.GetReconciledObjectsRequest{ + AutomationName: automationName, + Namespace: ns.Name, + AutomationKind: kustomizev1.KustomizationKind, + Kinds: []*pb.GroupVersionKind{{Group: "", Version: "v1", Kind: "Secret"}}, + ClusterName: cluster.DefaultCluster, + }) + + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(res.Objects).To(HaveLen(1)) + + first := res.Objects[0] + g.Expect(first.Payload).To(ContainSubstring("redacted")) +} + +func TestGetChildObjects(t *testing.T) { + g := NewGomegaWithT(t) + + ctx := context.Background() + + automationName := "my-automation" + + ns := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + }, + } + + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-deployment", + Namespace: ns.Name, + UID: "this-is-not-an-uid", + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + coretypes.AppLabel: automationName, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{coretypes.AppLabel: automationName}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Name: "nginx", + Image: "nginx", + }}, + }, + }, + }, + } + + rs := &appsv1.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-123abcd", automationName), + Namespace: ns.Name, + }, + Spec: appsv1.ReplicaSetSpec{ + Template: deployment.Spec.Template, + Selector: deployment.Spec.Selector, + }, + Status: appsv1.ReplicaSetStatus{ + Replicas: 1, + }, + } + + rs.SetOwnerReferences([]metav1.OwnerReference{{ + UID: deployment.UID, + APIVersion: appsv1.SchemeGroupVersion.String(), + Kind: "Deployment", + Name: deployment.Name, + }}) + + scheme, err := kube.CreateScheme() + g.Expect(err).To(BeNil()) + + client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(&ns, deployment, rs).Build() + cfg := makeServerConfig(client, t, "") + c := makeServer(cfg, t) + + res, err := c.GetChildObjects(ctx, &pb.GetChildObjectsRequest{ + ParentUid: string(deployment.UID), + Namespace: ns.Name, + GroupVersionKind: &pb.GroupVersionKind{ + Group: "apps", + Version: "v1", + Kind: "ReplicaSet", + }, + ClusterName: cluster.DefaultCluster, + }) + + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(res.Objects).To(HaveLen(1)) + + first := res.Objects[0] + g.Expect(first.Payload).To(ContainSubstring("ReplicaSet")) + g.Expect(first.Payload).To(ContainSubstring(rs.Name)) +} + +func TestListFluxRuntimeObjects(t *testing.T) { + g := NewGomegaWithT(t) + + ctx := context.Background() + + tests := []struct { + description string + objects []runtime.Object + assertions func(*pb.ListFluxRuntimeObjectsResponse) + }{ + { + "no flux runtime", + []runtime.Object{ + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "ns1"}}, + }, + func(res *pb.ListFluxRuntimeObjectsResponse) { + g.Expect(res.Errors[0].Message).To(Equal(server.ErrFluxNamespaceNotFound.Error())) + g.Expect(res.Errors[0].Namespace).To(BeEmpty()) + g.Expect(res.Errors[0].ClusterName).To(Equal(cluster.DefaultCluster)) + }, + }, + { + "flux namespace label, with controllers", + []runtime.Object{ + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ + coretypes.PartOfLabel: server.FluxNamespacePartOf, + }}}, + newDeployment("random-flux-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), + newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), + }, + func(res *pb.ListFluxRuntimeObjectsResponse) { + g.Expect(res.Deployments).To(HaveLen(1), "expected deployments in the flux namespace to be returned") + g.Expect(res.Deployments[0].Name).To(Equal("random-flux-controller")) + }, + }, + { + "use flux-system namespace when no namespace label available", + []runtime.Object{ + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-system"}}, + newDeployment("random-flux-controller", "flux-system", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), + newDeployment("other-controller-in-flux-ns", "flux-system", map[string]string{}), + }, + func(res *pb.ListFluxRuntimeObjectsResponse) { + g.Expect(res.Deployments).To(HaveLen(1), "expected deployments in the default flux namespace to be returned") + g.Expect(res.Deployments[0].Name).To(Equal("random-flux-controller")) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + scheme, err := kube.CreateScheme() + g.Expect(err).To(BeNil()) + client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tt.objects...).Build() + cfg := makeServerConfig(client, t, "") + c := makeServer(cfg, t) + res, err := c.ListFluxRuntimeObjects(ctx, &pb.ListFluxRuntimeObjectsRequest{}) + g.Expect(err).NotTo(HaveOccurred()) + + tt.assertions(res) }) } } + +func newDeployment(name, ns string, labels map[string]string) *appsv1.Deployment { + return &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + Labels: labels, + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + coretypes.AppLabel: name, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{coretypes.AppLabel: name}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Name: "nginx", + Image: "nginx", + }}, + }, + }, + }, + } +} + +func TestListFluxCrds(t *testing.T) { + g := NewGomegaWithT(t) + + ctx := context.Background() + + crd1 := &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ + Name: "crd1", + Labels: map[string]string{coretypes.PartOfLabel: "flux"}, + }, Spec: apiextensions.CustomResourceDefinitionSpec{ + Group: "group", + Names: apiextensions.CustomResourceDefinitionNames{Plural: "plural", Kind: "kind"}, + Versions: []apiextensions.CustomResourceDefinitionVersion{}, + }} + crd2 := &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ + Name: "crd2", + Labels: map[string]string{coretypes.PartOfLabel: "flux"}, + }, Spec: apiextensions.CustomResourceDefinitionSpec{ + Group: "group", + Versions: []apiextensions.CustomResourceDefinitionVersion{ + {Name: "0"}, + // "Active" version in etcd, use this one. + {Name: "1", Storage: true}, + }, + }} + scheme, err := kube.CreateScheme() + g.Expect(err).To(BeNil()) + + client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(crd1, crd2).Build() + cfg := makeServerConfig(client, t, "") + c := makeServer(cfg, t) + + res, err := c.ListFluxCrds(ctx, &pb.ListFluxCrdsRequest{}) + + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(res.Crds).To(HaveLen(2)) + + first := res.Crds[0] + g.Expect(first.Version).To(Equal("")) + g.Expect(first.Name.Plural).To(Equal("plural")) + g.Expect(first.Name.Group).To(Equal("group")) + g.Expect(first.Kind).To(Equal("kind")) + g.Expect(first.ClusterName).To(Equal(cluster.DefaultCluster)) + g.Expect(res.Crds[1].Version).To(Equal("1")) +} From 5e8f10602e0bdb37f9a56380616aba802d74a54b Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 15 Dec 2023 14:35:58 +0100 Subject: [PATCH 06/43] added integration for flux runtime api Signed-off-by: Eneko Fernandez --- core/server/fluxruntime_test.go | 51 ++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/core/server/fluxruntime_test.go b/core/server/fluxruntime_test.go index 36c3e64dc1..ab680cba70 100644 --- a/core/server/fluxruntime_test.go +++ b/core/server/fluxruntime_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "os" "testing" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" @@ -12,6 +13,7 @@ import ( "github.com/weaveworks/weave-gitops/core/server" coretypes "github.com/weaveworks/weave-gitops/core/server/types" pb "github.com/weaveworks/weave-gitops/pkg/api/core" + "github.com/weaveworks/weave-gitops/pkg/featureflags" "github.com/weaveworks/weave-gitops/pkg/kube" "google.golang.org/grpc/metadata" appsv1 "k8s.io/api/apps/v1" @@ -333,15 +335,17 @@ func TestListFluxRuntimeObjects(t *testing.T) { ctx := context.Background() tests := []struct { - description string - objects []runtime.Object - assertions func(*pb.ListFluxRuntimeObjectsResponse) + description string + objects []runtime.Object + gitopsRuntimeFeatureFlag string + assertions func(*pb.ListFluxRuntimeObjectsResponse) }{ { "no flux runtime", []runtime.Object{ &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "ns1"}}, }, + "", func(res *pb.ListFluxRuntimeObjectsResponse) { g.Expect(res.Errors[0].Message).To(Equal(server.ErrFluxNamespaceNotFound.Error())) g.Expect(res.Errors[0].Namespace).To(BeEmpty()) @@ -349,7 +353,20 @@ func TestListFluxRuntimeObjects(t *testing.T) { }, }, { - "flux namespace label, with controllers", + "use flux-system namespace when no namespace label available", + []runtime.Object{ + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-system"}}, + newDeployment("random-flux-controller", "flux-system", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), + newDeployment("other-controller-in-flux-ns", "flux-system", map[string]string{}), + }, + "", + func(res *pb.ListFluxRuntimeObjectsResponse) { + g.Expect(res.Deployments).To(HaveLen(1), "expected deployments in the default flux namespace to be returned") + g.Expect(res.Deployments[0].Name).To(Equal("random-flux-controller")) + }, + }, + { + "return flux runtime without WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME", []runtime.Object{ &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ coretypes.PartOfLabel: server.FluxNamespacePartOf, @@ -357,27 +374,39 @@ func TestListFluxRuntimeObjects(t *testing.T) { newDeployment("random-flux-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), }, + "false", func(res *pb.ListFluxRuntimeObjectsResponse) { g.Expect(res.Deployments).To(HaveLen(1), "expected deployments in the flux namespace to be returned") g.Expect(res.Deployments[0].Name).To(Equal("random-flux-controller")) }, }, { - "use flux-system namespace when no namespace label available", + "return weave gitops runtime with WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME enabled", []runtime.Object{ - &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-system"}}, - newDeployment("random-flux-controller", "flux-system", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), - newDeployment("other-controller-in-flux-ns", "flux-system", map[string]string{}), + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ + coretypes.PartOfLabel: server.FluxNamespacePartOf, + }}}, + newDeployment("kustomize-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), + newDeployment("weave-gitops-enterprise-mccp-cluster-service", "flux-ns", map[string]string{coretypes.PartOfLabel: server.PartOfWeaveGitops}), + newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), }, + "true", func(res *pb.ListFluxRuntimeObjectsResponse) { - g.Expect(res.Deployments).To(HaveLen(1), "expected deployments in the default flux namespace to be returned") - g.Expect(res.Deployments[0].Name).To(Equal("random-flux-controller")) + g.Expect(res.Deployments).To(HaveLen(2), "expected deployments in the flux namespace to be returned") + g.Expect(res.Deployments[0].Name).To(Equal("kustomize-controller")) + g.Expect(res.Deployments[1].Name).To(Equal("weave-gitops-enterprise-mccp-cluster-service")) }, }, } - for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { + if tt.gitopsRuntimeFeatureFlag != "" { + _ = os.Setenv(server.GitopsRuntimeFeatureFlag, tt.gitopsRuntimeFeatureFlag) + } + defer func() { + _ = os.Unsetenv(server.GitopsRuntimeFeatureFlag) + }() + featureflags.SetFromEnv(os.Environ()) scheme, err := kube.CreateScheme() g.Expect(err).To(BeNil()) client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tt.objects...).Build() From 7f5e979ed68439c04284416e7e14be85052ae534 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 15 Dec 2023 14:50:19 +0100 Subject: [PATCH 07/43] added to listcrds Signed-off-by: Eneko Fernandez --- core/server/fluxruntime.go | 37 +++---- core/server/fluxruntime_test.go | 145 +++++++++++++++++++-------- core/server/gitopsruntime_test.go | 4 +- core/server/session_logs.go | 5 +- pkg/run/install/install_flux_test.go | 4 +- 5 files changed, 130 insertions(+), 65 deletions(-) diff --git a/core/server/fluxruntime.go b/core/server/fluxruntime.go index 4dd0cf1abb..7f7bdc7cfe 100644 --- a/core/server/fluxruntime.go +++ b/core/server/fluxruntime.go @@ -29,7 +29,7 @@ import ( ) const ( - FluxNamespacePartOf = "flux" + PartOfFlux = "flux" PartOfWeaveGitops = "weave-gitops" GitopsRuntimeFeatureFlag = "WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME" ) @@ -49,11 +49,11 @@ var ( ) var FluxRuntimeLabels = []string{ - FluxNamespacePartOf, + PartOfFlux, } var WeaveGitopsRuntimeLabels = []string{ - FluxNamespacePartOf, PartOfWeaveGitops, + PartOfFlux, PartOfWeaveGitops, } func lookupEnv(envVar, fallback string) string { @@ -154,22 +154,23 @@ func (cs *coreServer) ListFluxCrds(ctx context.Context, msg *pb.ListFluxCrdsRequ respErrors := []*pb.ListError{} - opts := client.MatchingLabels{ - coretypes.PartOfLabel: FluxNamespacePartOf, - } - - if err := clustersClient.ClusteredList(ctx, clist, false, opts); err != nil { - var errs clustersmngr.ClusteredListError - - if !errors.As(err, &errs) { - return nil, fmt.Errorf("CRDs clustered list: %w", errs) + for _, runtimeLabel := range getRuntimeLabels() { + opts := client.MatchingLabels{ + coretypes.PartOfLabel: runtimeLabel, } + if err := clustersClient.ClusteredList(ctx, clist, false, opts); err != nil { + var errs clustersmngr.ClusteredListError - for _, e := range errs.Errors { - respErrors = append(respErrors, &pb.ListError{ - ClusterName: e.Cluster, - Message: e.Err.Error(), - }) + if !errors.As(err, &errs) { + return nil, fmt.Errorf("CRDs clustered list: %w", errs) + } + + for _, e := range errs.Errors { + respErrors = append(respErrors, &pb.ListError{ + ClusterName: e.Cluster, + Message: e.Err.Error(), + }) + } } } @@ -216,7 +217,7 @@ func filterFluxNamespace(nss []v1.Namespace) []v1.Namespace { fluxSystem := []v1.Namespace{} for _, ns := range nss { - if val, ok := ns.Labels[coretypes.PartOfLabel]; ok && val == FluxNamespacePartOf { + if val, ok := ns.Labels[coretypes.PartOfLabel]; ok && val == PartOfFlux { fluxSystem = append(fluxSystem, ns) continue } diff --git a/core/server/fluxruntime_test.go b/core/server/fluxruntime_test.go index ab680cba70..383f97de8e 100644 --- a/core/server/fluxruntime_test.go +++ b/core/server/fluxruntime_test.go @@ -356,7 +356,7 @@ func TestListFluxRuntimeObjects(t *testing.T) { "use flux-system namespace when no namespace label available", []runtime.Object{ &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-system"}}, - newDeployment("random-flux-controller", "flux-system", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), + newDeployment("random-flux-controller", "flux-system", map[string]string{coretypes.PartOfLabel: server.PartOfFlux}), newDeployment("other-controller-in-flux-ns", "flux-system", map[string]string{}), }, "", @@ -369,9 +369,9 @@ func TestListFluxRuntimeObjects(t *testing.T) { "return flux runtime without WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME", []runtime.Object{ &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ - coretypes.PartOfLabel: server.FluxNamespacePartOf, + coretypes.PartOfLabel: server.PartOfFlux, }}}, - newDeployment("random-flux-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), + newDeployment("random-flux-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.PartOfFlux}), newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), }, "false", @@ -384,9 +384,9 @@ func TestListFluxRuntimeObjects(t *testing.T) { "return weave gitops runtime with WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME enabled", []runtime.Object{ &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ - coretypes.PartOfLabel: server.FluxNamespacePartOf, + coretypes.PartOfLabel: server.PartOfFlux, }}}, - newDeployment("kustomize-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), + newDeployment("kustomize-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.PartOfFlux}), newDeployment("weave-gitops-enterprise-mccp-cluster-service", "flux-ns", map[string]string{coretypes.PartOfLabel: server.PartOfWeaveGitops}), newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), }, @@ -450,45 +450,108 @@ func newDeployment(name, ns string, labels map[string]string) *appsv1.Deployment func TestListFluxCrds(t *testing.T) { g := NewGomegaWithT(t) - ctx := context.Background() - crd1 := &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ - Name: "crd1", - Labels: map[string]string{coretypes.PartOfLabel: "flux"}, - }, Spec: apiextensions.CustomResourceDefinitionSpec{ - Group: "group", - Names: apiextensions.CustomResourceDefinitionNames{Plural: "plural", Kind: "kind"}, - Versions: []apiextensions.CustomResourceDefinitionVersion{}, - }} - crd2 := &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ - Name: "crd2", - Labels: map[string]string{coretypes.PartOfLabel: "flux"}, - }, Spec: apiextensions.CustomResourceDefinitionSpec{ - Group: "group", - Versions: []apiextensions.CustomResourceDefinitionVersion{ - {Name: "0"}, - // "Active" version in etcd, use this one. - {Name: "1", Storage: true}, + tests := []struct { + description string + objects []runtime.Object + gitopsRuntimeFeatureFlag string + assertions func(*pb.ListFluxCrdsResponse) + }{ + { + "return weave gitops runtime without WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME enabled", + []runtime.Object{ + &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ + Name: "crd1", + Labels: map[string]string{coretypes.PartOfLabel: "flux"}, + }, Spec: apiextensions.CustomResourceDefinitionSpec{ + Group: "group", + Names: apiextensions.CustomResourceDefinitionNames{Plural: "plural", Kind: "kind"}, + Versions: []apiextensions.CustomResourceDefinitionVersion{}, + }}, + &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ + Name: "crd2", + Labels: map[string]string{coretypes.PartOfLabel: "flux"}, + }, Spec: apiextensions.CustomResourceDefinitionSpec{ + Group: "group", + Versions: []apiextensions.CustomResourceDefinitionVersion{ + {Name: "0"}, + // "Active" version in etcd, use this one. + {Name: "1", Storage: true}, + }, + }}, + }, + "false", + func(res *pb.ListFluxCrdsResponse) { + g.Expect(res.Crds).To(HaveLen(2)) + first := res.Crds[0] + g.Expect(first.Version).To(Equal("")) + g.Expect(first.Name.Plural).To(Equal("plural")) + g.Expect(first.Name.Group).To(Equal("group")) + g.Expect(first.Kind).To(Equal("kind")) + g.Expect(first.ClusterName).To(Equal(cluster.DefaultCluster)) + g.Expect(res.Crds[1].Version).To(Equal("1")) + }, }, - }} - scheme, err := kube.CreateScheme() - g.Expect(err).To(BeNil()) - - client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(crd1, crd2).Build() - cfg := makeServerConfig(client, t, "") - c := makeServer(cfg, t) + { + "return weave gitops runtime with WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME enabled", + []runtime.Object{ + &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ + Name: "crd1", + Labels: map[string]string{coretypes.PartOfLabel: server.PartOfFlux}, + }, Spec: apiextensions.CustomResourceDefinitionSpec{ + Group: "group", + Names: apiextensions.CustomResourceDefinitionNames{Plural: "plural", Kind: "kind"}, + Versions: []apiextensions.CustomResourceDefinitionVersion{}, + }}, + &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ + Name: "crd2", + Labels: map[string]string{coretypes.PartOfLabel: server.PartOfFlux}, + }, Spec: apiextensions.CustomResourceDefinitionSpec{ + Group: "group", + Versions: []apiextensions.CustomResourceDefinitionVersion{ + {Name: "0"}, + // "Active" version in etcd, use this one. + {Name: "1", Storage: true}, + }, + }}, + &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ + Name: "crd3", + Labels: map[string]string{coretypes.PartOfLabel: server.PartOfWeaveGitops}, + }, Spec: apiextensions.CustomResourceDefinitionSpec{ + Group: "group", + Versions: []apiextensions.CustomResourceDefinitionVersion{ + {Name: "0"}, + // "Active" version in etcd, use this one. + {Name: "1", Storage: true}, + }, + }}, + }, + "true", + func(res *pb.ListFluxCrdsResponse) { + g.Expect(res.Crds).To(HaveLen(3)) + }, + }, + } + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + if tt.gitopsRuntimeFeatureFlag != "" { + _ = os.Setenv(server.GitopsRuntimeFeatureFlag, tt.gitopsRuntimeFeatureFlag) + } + defer func() { + _ = os.Unsetenv(server.GitopsRuntimeFeatureFlag) + }() + featureflags.SetFromEnv(os.Environ()) + scheme, err := kube.CreateScheme() + g.Expect(err).To(BeNil()) + client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tt.objects...).Build() + cfg := makeServerConfig(client, t, "") + c := makeServer(cfg, t) - res, err := c.ListFluxCrds(ctx, &pb.ListFluxCrdsRequest{}) + res, err := c.ListFluxCrds(ctx, &pb.ListFluxCrdsRequest{}) + g.Expect(err).NotTo(HaveOccurred()) - g.Expect(err).NotTo(HaveOccurred()) - g.Expect(res.Crds).To(HaveLen(2)) - - first := res.Crds[0] - g.Expect(first.Version).To(Equal("")) - g.Expect(first.Name.Plural).To(Equal("plural")) - g.Expect(first.Name.Group).To(Equal("group")) - g.Expect(first.Kind).To(Equal("kind")) - g.Expect(first.ClusterName).To(Equal(cluster.DefaultCluster)) - g.Expect(res.Crds[1].Version).To(Equal("1")) + tt.assertions(res) + }) + } } diff --git a/core/server/gitopsruntime_test.go b/core/server/gitopsruntime_test.go index 91d470e889..c4546ac84e 100644 --- a/core/server/gitopsruntime_test.go +++ b/core/server/gitopsruntime_test.go @@ -41,9 +41,9 @@ func TestListGitopsRuntimeObjects(t *testing.T) { "flux namespace label, with controllers", []runtime.Object{ &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ - coretypes.PartOfLabel: server.FluxNamespacePartOf, + coretypes.PartOfLabel: server.PartOfFlux, }}}, - newDeployment("kustomize-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.FluxNamespacePartOf}), + newDeployment("kustomize-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.PartOfFlux}), newDeployment("weave-gitops-enterprise-mccp-cluster-service", "flux-ns", map[string]string{coretypes.PartOfLabel: server.PartOfWeaveGitops}), newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), }, diff --git a/core/server/session_logs.go b/core/server/session_logs.go index 21f36566c0..cefe14a08f 100644 --- a/core/server/session_logs.go +++ b/core/server/session_logs.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2" "io" "regexp" "sort" @@ -12,6 +11,8 @@ import ( "strings" "time" + sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2" + "github.com/weaveworks/weave-gitops/pkg/compositehash" "github.com/minio/minio-go/v7" @@ -63,7 +64,7 @@ type bucketConnectionInfo struct { func (cs *coreServer) getFluxNamespace(ctx context.Context, k8sClient client.Client) (string, error) { namespaceList := corev1.NamespaceList{} opts := client.MatchingLabels{ - coretypes.PartOfLabel: FluxNamespacePartOf, + coretypes.PartOfLabel: PartOfFlux, } var ns *corev1.Namespace diff --git a/pkg/run/install/install_flux_test.go b/pkg/run/install/install_flux_test.go index 9aa8abfeb3..bea5384e94 100644 --- a/pkg/run/install/install_flux_test.go +++ b/pkg/run/install/install_flux_test.go @@ -40,7 +40,7 @@ var _ = Describe("GetFluxVersion", func() { fluxNs := &v1.Namespace{} fluxNs.Name = "flux-system" fluxNs.Labels = map[string]string{ - coretypes.PartOfLabel: server.FluxNamespacePartOf, + coretypes.PartOfLabel: server.PartOfFlux, } Expect(kubeClient.Create(ctx, fluxNs)).To(Succeed()) @@ -101,7 +101,7 @@ var _ = Describe("GetFluxVersion", func() { fluxNs := &v1.Namespace{} fluxNs.Name = "flux-ns-test" fluxNs.Labels = map[string]string{ - coretypes.PartOfLabel: server.FluxNamespacePartOf, + coretypes.PartOfLabel: server.PartOfFlux, coretypes.VersionLabel: testVersion, } From 0dcceb4ce64db6150f66ab892a441ffbe55b87cb Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 15 Dec 2023 14:52:19 +0100 Subject: [PATCH 08/43] not required Signed-off-by: Eneko Fernandez --- core/server/gitopsruntime_test.go | 70 ------------------------------- 1 file changed, 70 deletions(-) delete mode 100644 core/server/gitopsruntime_test.go diff --git a/core/server/gitopsruntime_test.go b/core/server/gitopsruntime_test.go deleted file mode 100644 index c4546ac84e..0000000000 --- a/core/server/gitopsruntime_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package server_test - -import ( - "context" - "testing" - - . "github.com/onsi/gomega" - "github.com/weaveworks/weave-gitops/core/clustersmngr/cluster" - "github.com/weaveworks/weave-gitops/core/server" - coretypes "github.com/weaveworks/weave-gitops/core/server/types" - pb "github.com/weaveworks/weave-gitops/pkg/api/core" - "github.com/weaveworks/weave-gitops/pkg/kube" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -func TestListGitopsRuntimeObjects(t *testing.T) { - g := NewGomegaWithT(t) - - ctx := context.Background() - - tests := []struct { - description string - objects []runtime.Object - assertions func(*pb.ListFluxRuntimeObjectsResponse) - }{ - { - "no gitops runtime", - []runtime.Object{ - &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "ns1"}}, - }, - func(res *pb.ListFluxRuntimeObjectsResponse) { - g.Expect(res.Errors[0].Message).To(Equal(server.ErrFluxNamespaceNotFound.Error())) - g.Expect(res.Errors[0].Namespace).To(BeEmpty()) - g.Expect(res.Errors[0].ClusterName).To(Equal(cluster.DefaultCluster)) - }, - }, - { - "flux namespace label, with controllers", - []runtime.Object{ - &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ - coretypes.PartOfLabel: server.PartOfFlux, - }}}, - newDeployment("kustomize-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.PartOfFlux}), - newDeployment("weave-gitops-enterprise-mccp-cluster-service", "flux-ns", map[string]string{coretypes.PartOfLabel: server.PartOfWeaveGitops}), - newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), - }, - func(res *pb.ListFluxRuntimeObjectsResponse) { - g.Expect(res.Deployments).To(HaveLen(2), "expected deployments in the flux namespace to be returned") - g.Expect(res.Deployments[0].Name).To(Equal("kustomize-controller")) - g.Expect(res.Deployments[1].Name).To(Equal("weave-gitops-enterprise-mccp-cluster-service")) - }, - }, - } - - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - scheme, err := kube.CreateScheme() - g.Expect(err).To(BeNil()) - client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tt.objects...).Build() - cfg := makeServerConfig(client, t, "") - c := makeServer(cfg, t) - res, err := c.ListFluxRuntimeObjects(ctx, &pb.ListFluxRuntimeObjectsRequest{}) - g.Expect(err).NotTo(HaveOccurred()) - tt.assertions(res) - }) - } -} From cefdf0ba1802bea26422ff1ec154e661ac25d327 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 15 Dec 2023 14:53:16 +0100 Subject: [PATCH 09/43] refactored label value Signed-off-by: Eneko Fernandez --- core/server/fluxruntime.go | 10 +++++----- core/server/fluxruntime_test.go | 18 +++++++++--------- core/server/session_logs.go | 2 +- pkg/run/install/install_flux_test.go | 4 ++-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/core/server/fluxruntime.go b/core/server/fluxruntime.go index 7f7bdc7cfe..e3ed7e8a8a 100644 --- a/core/server/fluxruntime.go +++ b/core/server/fluxruntime.go @@ -29,8 +29,8 @@ import ( ) const ( - PartOfFlux = "flux" - PartOfWeaveGitops = "weave-gitops" + Flux = "flux" + WeaveGitops = "weave-gitops" GitopsRuntimeFeatureFlag = "WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME" ) @@ -49,11 +49,11 @@ var ( ) var FluxRuntimeLabels = []string{ - PartOfFlux, + Flux, } var WeaveGitopsRuntimeLabels = []string{ - PartOfFlux, PartOfWeaveGitops, + Flux, WeaveGitops, } func lookupEnv(envVar, fallback string) string { @@ -217,7 +217,7 @@ func filterFluxNamespace(nss []v1.Namespace) []v1.Namespace { fluxSystem := []v1.Namespace{} for _, ns := range nss { - if val, ok := ns.Labels[coretypes.PartOfLabel]; ok && val == PartOfFlux { + if val, ok := ns.Labels[coretypes.PartOfLabel]; ok && val == Flux { fluxSystem = append(fluxSystem, ns) continue } diff --git a/core/server/fluxruntime_test.go b/core/server/fluxruntime_test.go index 383f97de8e..ae840377ca 100644 --- a/core/server/fluxruntime_test.go +++ b/core/server/fluxruntime_test.go @@ -356,7 +356,7 @@ func TestListFluxRuntimeObjects(t *testing.T) { "use flux-system namespace when no namespace label available", []runtime.Object{ &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-system"}}, - newDeployment("random-flux-controller", "flux-system", map[string]string{coretypes.PartOfLabel: server.PartOfFlux}), + newDeployment("random-flux-controller", "flux-system", map[string]string{coretypes.PartOfLabel: server.Flux}), newDeployment("other-controller-in-flux-ns", "flux-system", map[string]string{}), }, "", @@ -369,9 +369,9 @@ func TestListFluxRuntimeObjects(t *testing.T) { "return flux runtime without WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME", []runtime.Object{ &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ - coretypes.PartOfLabel: server.PartOfFlux, + coretypes.PartOfLabel: server.Flux, }}}, - newDeployment("random-flux-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.PartOfFlux}), + newDeployment("random-flux-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.Flux}), newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), }, "false", @@ -384,10 +384,10 @@ func TestListFluxRuntimeObjects(t *testing.T) { "return weave gitops runtime with WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME enabled", []runtime.Object{ &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ - coretypes.PartOfLabel: server.PartOfFlux, + coretypes.PartOfLabel: server.Flux, }}}, - newDeployment("kustomize-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.PartOfFlux}), - newDeployment("weave-gitops-enterprise-mccp-cluster-service", "flux-ns", map[string]string{coretypes.PartOfLabel: server.PartOfWeaveGitops}), + newDeployment("kustomize-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.Flux}), + newDeployment("weave-gitops-enterprise-mccp-cluster-service", "flux-ns", map[string]string{coretypes.PartOfLabel: server.WeaveGitops}), newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), }, "true", @@ -498,7 +498,7 @@ func TestListFluxCrds(t *testing.T) { []runtime.Object{ &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ Name: "crd1", - Labels: map[string]string{coretypes.PartOfLabel: server.PartOfFlux}, + Labels: map[string]string{coretypes.PartOfLabel: server.Flux}, }, Spec: apiextensions.CustomResourceDefinitionSpec{ Group: "group", Names: apiextensions.CustomResourceDefinitionNames{Plural: "plural", Kind: "kind"}, @@ -506,7 +506,7 @@ func TestListFluxCrds(t *testing.T) { }}, &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ Name: "crd2", - Labels: map[string]string{coretypes.PartOfLabel: server.PartOfFlux}, + Labels: map[string]string{coretypes.PartOfLabel: server.Flux}, }, Spec: apiextensions.CustomResourceDefinitionSpec{ Group: "group", Versions: []apiextensions.CustomResourceDefinitionVersion{ @@ -517,7 +517,7 @@ func TestListFluxCrds(t *testing.T) { }}, &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ Name: "crd3", - Labels: map[string]string{coretypes.PartOfLabel: server.PartOfWeaveGitops}, + Labels: map[string]string{coretypes.PartOfLabel: server.WeaveGitops}, }, Spec: apiextensions.CustomResourceDefinitionSpec{ Group: "group", Versions: []apiextensions.CustomResourceDefinitionVersion{ diff --git a/core/server/session_logs.go b/core/server/session_logs.go index cefe14a08f..5a807db768 100644 --- a/core/server/session_logs.go +++ b/core/server/session_logs.go @@ -64,7 +64,7 @@ type bucketConnectionInfo struct { func (cs *coreServer) getFluxNamespace(ctx context.Context, k8sClient client.Client) (string, error) { namespaceList := corev1.NamespaceList{} opts := client.MatchingLabels{ - coretypes.PartOfLabel: PartOfFlux, + coretypes.PartOfLabel: Flux, } var ns *corev1.Namespace diff --git a/pkg/run/install/install_flux_test.go b/pkg/run/install/install_flux_test.go index bea5384e94..689961a410 100644 --- a/pkg/run/install/install_flux_test.go +++ b/pkg/run/install/install_flux_test.go @@ -40,7 +40,7 @@ var _ = Describe("GetFluxVersion", func() { fluxNs := &v1.Namespace{} fluxNs.Name = "flux-system" fluxNs.Labels = map[string]string{ - coretypes.PartOfLabel: server.PartOfFlux, + coretypes.PartOfLabel: server.Flux, } Expect(kubeClient.Create(ctx, fluxNs)).To(Succeed()) @@ -101,7 +101,7 @@ var _ = Describe("GetFluxVersion", func() { fluxNs := &v1.Namespace{} fluxNs.Name = "flux-ns-test" fluxNs.Labels = map[string]string{ - coretypes.PartOfLabel: server.PartOfFlux, + coretypes.PartOfLabel: server.Flux, coretypes.VersionLabel: testVersion, } From a2ad0eaf1d9c1f7d3a153efe45d9f0bb6b15eb31 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 15 Dec 2023 14:54:14 +0100 Subject: [PATCH 10/43] not required Signed-off-by: Eneko Fernandez --- core/server/suite_test.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/server/suite_test.go b/core/server/suite_test.go index 3f7f2efaa5..d0e89d854f 100644 --- a/core/server/suite_test.go +++ b/core/server/suite_test.go @@ -36,11 +36,6 @@ var nsChecker nsaccessfakes.FakeChecker func TestMain(m *testing.M) { var err error - - // setup testEnvironment - envTestPath := fmt.Sprintf("%s/tools/bin/envtest", "/Users/enekofb/projects/github.com/weaveworks/weave-gitops-enterprise") - os.Setenv("KUBEBUILDER_ASSETS", envTestPath) - k8sEnv, err = testutils.StartK8sTestEnvironment([]string{ "../../manifests/crds", "../../tools/testcrds", From 0234159c5b67b0d9bd7e6a6d042228745ca06c77 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 15 Dec 2023 15:15:59 +0100 Subject: [PATCH 11/43] linted Signed-off-by: Eneko Fernandez --- core/server/fluxruntime.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core/server/fluxruntime.go b/core/server/fluxruntime.go index e3ed7e8a8a..c594295bee 100644 --- a/core/server/fluxruntime.go +++ b/core/server/fluxruntime.go @@ -127,7 +127,6 @@ func (cs *coreServer) ListFluxRuntimeObjects(ctx context.Context, msg *pb.ListFl } } } - } return &pb.ListFluxRuntimeObjectsResponse{Deployments: results, Errors: respErrors}, nil From e52013105da7f4ff294236058865c20a7ad711e3 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 15 Dec 2023 15:41:18 +0100 Subject: [PATCH 12/43] gitops runtime visible in the ui and flux version only renders flux Signed-off-by: Eneko Fernandez --- tools/helm-values-dev.yaml | 3 ++- ui/components/FluxRuntime.tsx | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/helm-values-dev.yaml b/tools/helm-values-dev.yaml index b15f3d5895..26ad61eeda 100644 --- a/tools/helm-values-dev.yaml +++ b/tools/helm-values-dev.yaml @@ -29,7 +29,8 @@ envVars: value: "Login with SSO" - name: WEAVE_GITOPS_FEATURE_DEV_MODE value: "true" - + - name: WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME + value: "true" # Run the UI and API under /wego # additionalArgs: diff --git a/ui/components/FluxRuntime.tsx b/ui/components/FluxRuntime.tsx index f68f08151b..df4806bf07 100644 --- a/ui/components/FluxRuntime.tsx +++ b/ui/components/FluxRuntime.tsx @@ -27,7 +27,10 @@ type Props = { crds?: Crd[]; }; const fluxVersionLabel = "app.kubernetes.io/version"; +const partOfLabel = "app.kubernetes.io/part-of"; +const fluxLabel = "flux"; +// FIXME: make it generic enough so it fits for both FluxRuntime or WeaveGitopsRuntime function FluxRuntime({ className, deployments, crds }: Props) { const { path } = useRouteMatch(); const tabs: Array = [ @@ -49,7 +52,7 @@ function FluxRuntime({ className, deployments, crds }: Props) { }, ]; const fluxVersions: { [key: string]: FluxVersion } = {}; - deployments.forEach((d) => { + deployments.filter( (d) => d.labels[partOfLabel] == fluxLabel ).forEach((d) => { const fv = d.labels[fluxVersionLabel]; const k = `${fv}${d.clusterName}${d.namespace}`; if (!fluxVersions[k]) { From 25fd56546ea3445c4a7244c2e1162fb4947bb326 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 15 Dec 2023 16:01:18 +0100 Subject: [PATCH 13/43] renamed flux runtime to runtime to be able to accomodate both flux and weave gitops controllers Signed-off-by: Eneko Fernandez --- ui/App.tsx | 2 +- ui/components/FluxRuntime.tsx | 3 +-- ui/lib/nav.ts | 2 +- ui/pages/v2/FluxRuntime.tsx | 11 +++++++++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ui/App.tsx b/ui/App.tsx index c38b2c77a8..1efd6e5aa5 100644 --- a/ui/App.tsx +++ b/ui/App.tsx @@ -87,7 +87,7 @@ const navItems: NavItem[] = [ icon: IconType.PoliciesIcon, }, { - label: "Flux Runtime", + label: "Runtime", link: { value: V2Routes.FluxRuntime }, icon: IconType.FluxIcon, }, diff --git a/ui/components/FluxRuntime.tsx b/ui/components/FluxRuntime.tsx index df4806bf07..ad18bc97b8 100644 --- a/ui/components/FluxRuntime.tsx +++ b/ui/components/FluxRuntime.tsx @@ -64,8 +64,7 @@ function FluxRuntime({ className, deployments, crds }: Props) { } }); - const supportMultipleFlux = - Object.keys(fluxVersions).length > 1 ? true : false; + const supportMultipleFlux = true; if (supportMultipleFlux) { tabs.unshift({ diff --git a/ui/lib/nav.ts b/ui/lib/nav.ts index 53699f7d70..ab783b52c9 100644 --- a/ui/lib/nav.ts +++ b/ui/lib/nav.ts @@ -113,7 +113,7 @@ export const getParentNavRouteValue = ( const pageTitles = { [V2Routes.Automations]: "Applications", [V2Routes.Sources]: "Sources", - [V2Routes.FluxRuntime]: "Flux Runtime", + [V2Routes.FluxRuntime]: "Runtime", [V2Routes.Notifications]: "Notifications", [V2Routes.ImageAutomation]: "Image Automations", [V2Routes.ImagePolicies]: "Image Policies", diff --git a/ui/pages/v2/FluxRuntime.tsx b/ui/pages/v2/FluxRuntime.tsx index ac57059df7..742ae561ac 100644 --- a/ui/pages/v2/FluxRuntime.tsx +++ b/ui/pages/v2/FluxRuntime.tsx @@ -1,7 +1,10 @@ import * as React from "react"; +import {useContext} from "react"; import styled from "styled-components"; import FluxRuntimeComponent from "../../components/FluxRuntime"; import Page from "../../components/Page"; +import {CoreClientContext} from "../../contexts/CoreClientContext"; +import { useFeatureFlags } from "../../hooks/featureflags"; import { useListFluxCrds, useListFluxRuntimeObjects } from "../../hooks/flux"; type Props = { @@ -15,12 +18,16 @@ function FluxRuntime({ className }: Props) { isLoading: crdsLoading, error: crdsError, } = useListFluxCrds(); + const { featureFlags: flags } = useContext(CoreClientContext); + const { isFlagEnabled } = useFeatureFlags(); + return ( - From 2e9707fd3c31610ec7dae5e463631dd9a5b4ad51 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 15 Dec 2023 16:07:55 +0100 Subject: [PATCH 14/43] linted Signed-off-by: Eneko Fernandez --- ui/pages/v2/FluxRuntime.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui/pages/v2/FluxRuntime.tsx b/ui/pages/v2/FluxRuntime.tsx index 742ae561ac..778a186556 100644 --- a/ui/pages/v2/FluxRuntime.tsx +++ b/ui/pages/v2/FluxRuntime.tsx @@ -18,9 +18,6 @@ function FluxRuntime({ className }: Props) { isLoading: crdsLoading, error: crdsError, } = useListFluxCrds(); - const { featureFlags: flags } = useContext(CoreClientContext); - const { isFlagEnabled } = useFeatureFlags(); - return ( Date: Mon, 18 Dec 2023 15:37:14 +0100 Subject: [PATCH 15/43] extending api with listruntime* operations Signed-off-by: Eneko Fernandez --- api/core/core.proto | 37 ++ api/core/core.swagger.json | 103 ++- api/core/types.swagger.json | 2 +- pkg/api/core/core.pb.go | 1162 +++++++++++++++++++++------------- pkg/api/core/core.pb.gw.go | 166 +++++ pkg/api/core/core_grpc.pb.go | 76 +++ ui/lib/api/core/core.pb.ts | 25 + 7 files changed, 1145 insertions(+), 426 deletions(-) diff --git a/api/core/core.proto b/api/core/core.proto index c69a90f195..a6552978b4 100644 --- a/api/core/core.proto +++ b/api/core/core.proto @@ -64,6 +64,24 @@ service Core { }; } + /* + * ListRuntimeObjects lists Weave GitOps runtime components from a clusters. Weave GitOps runtime is composed of Flux runtime + * but also the other components in the ecosystem like TF-controller or Policy Agent. + */ + rpc ListRuntimeObjects(ListRuntimeObjectsRequest) + returns (ListRuntimeObjectsResponse) { + option (google.api.http) = { + get: "/v1/runtime_objects" + }; + } + + rpc ListRuntimeCrds(ListRuntimeCrdsRequest) + returns (ListRuntimeCrdsResponse) { + option (google.api.http) = { + get: "/v1/runtime_crds" + }; + } + /* * GetReconciledObjects returns a list of objects that were created * as a result of reconciling a Flux automation. @@ -318,6 +336,16 @@ message ListFluxRuntimeObjectsResponse { repeated ListError errors = 2; } +message ListRuntimeObjectsRequest { + string namespace = 1; + string clusterName = 2; +} + +message ListRuntimeObjectsResponse { + repeated Deployment deployments = 1; + repeated ListError errors = 2; +} + message ListFluxCrdsRequest { string clusterName = 1; } @@ -327,6 +355,15 @@ message ListFluxCrdsResponse { repeated ListError errors = 2; } +message ListRuntimeCrdsRequest { + string clusterName = 1; +} + +message ListRuntimeCrdsResponse { + repeated Crd crds = 1; + repeated ListError errors = 2; +} + message GetObjectRequest { string name = 1; string namespace = 2; diff --git a/api/core/core.swagger.json b/api/core/core.swagger.json index 671a957b47..70febc2ac4 100644 --- a/api/core/core.swagger.json +++ b/api/core/core.swagger.json @@ -602,6 +602,73 @@ ] } }, + "/v1/runtime_crds": { + "get": { + "operationId": "Core_ListRuntimeCrds", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1ListRuntimeCrdsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "clusterName", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "Core" + ] + } + }, + "/v1/runtime_objects": { + "get": { + "summary": "ListRuntimeObjects lists Weave GitOps runtime components from a clusters. Weave GitOps runtime is composed of Flux runtime\nbut also the other components in the ecosystem like TF-controller or Policy Agent.", + "operationId": "Core_ListRuntimeObjects", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/v1ListRuntimeObjectsResponse" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "namespace", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "clusterName", + "in": "query", + "required": false, + "type": "string" + } + ], + "tags": [ + "Core" + ] + } + }, "/v1/session_logs": { "post": { "summary": "GetSessionLogs returns the logs for a given session", @@ -746,7 +813,7 @@ } }, "additionalProperties": {}, - "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" + "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\nExample 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\nExample 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" }, "rpcStatus": { "type": "object", @@ -1319,6 +1386,40 @@ } } }, + "v1ListRuntimeCrdsResponse": { + "type": "object", + "properties": { + "crds": { + "type": "array", + "items": { + "$ref": "#/definitions/v1Crd" + } + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/definitions/v1ListError" + } + } + } + }, + "v1ListRuntimeObjectsResponse": { + "type": "object", + "properties": { + "deployments": { + "type": "array", + "items": { + "$ref": "#/definitions/v1Deployment" + } + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/definitions/v1ListError" + } + } + } + }, "v1LogEntry": { "type": "object", "properties": { diff --git a/api/core/types.swagger.json b/api/core/types.swagger.json index 3bea52f4d3..07751266f1 100644 --- a/api/core/types.swagger.json +++ b/api/core/types.swagger.json @@ -21,7 +21,7 @@ } }, "additionalProperties": {}, - "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" + "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\nExample 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\nExample 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" }, "rpcStatus": { "type": "object", diff --git a/pkg/api/core/core.pb.go b/pkg/api/core/core.pb.go index 505a4de55a..28b19be586 100644 --- a/pkg/api/core/core.pb.go +++ b/pkg/api/core/core.pb.go @@ -1005,6 +1005,116 @@ func (x *ListFluxRuntimeObjectsResponse) GetErrors() []*ListError { return nil } +type ListRuntimeObjectsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` + ClusterName string `protobuf:"bytes,2,opt,name=clusterName,proto3" json:"clusterName,omitempty"` +} + +func (x *ListRuntimeObjectsRequest) Reset() { + *x = ListRuntimeObjectsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_core_core_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListRuntimeObjectsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListRuntimeObjectsRequest) ProtoMessage() {} + +func (x *ListRuntimeObjectsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_core_core_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListRuntimeObjectsRequest.ProtoReflect.Descriptor instead. +func (*ListRuntimeObjectsRequest) Descriptor() ([]byte, []int) { + return file_api_core_core_proto_rawDescGZIP(), []int{14} +} + +func (x *ListRuntimeObjectsRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *ListRuntimeObjectsRequest) GetClusterName() string { + if x != nil { + return x.ClusterName + } + return "" +} + +type ListRuntimeObjectsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Deployments []*Deployment `protobuf:"bytes,1,rep,name=deployments,proto3" json:"deployments,omitempty"` + Errors []*ListError `protobuf:"bytes,2,rep,name=errors,proto3" json:"errors,omitempty"` +} + +func (x *ListRuntimeObjectsResponse) Reset() { + *x = ListRuntimeObjectsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_core_core_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListRuntimeObjectsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListRuntimeObjectsResponse) ProtoMessage() {} + +func (x *ListRuntimeObjectsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_core_core_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListRuntimeObjectsResponse.ProtoReflect.Descriptor instead. +func (*ListRuntimeObjectsResponse) Descriptor() ([]byte, []int) { + return file_api_core_core_proto_rawDescGZIP(), []int{15} +} + +func (x *ListRuntimeObjectsResponse) GetDeployments() []*Deployment { + if x != nil { + return x.Deployments + } + return nil +} + +func (x *ListRuntimeObjectsResponse) GetErrors() []*ListError { + if x != nil { + return x.Errors + } + return nil +} + type ListFluxCrdsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1016,7 +1126,7 @@ type ListFluxCrdsRequest struct { func (x *ListFluxCrdsRequest) Reset() { *x = ListFluxCrdsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[14] + mi := &file_api_core_core_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1029,7 +1139,7 @@ func (x *ListFluxCrdsRequest) String() string { func (*ListFluxCrdsRequest) ProtoMessage() {} func (x *ListFluxCrdsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[14] + mi := &file_api_core_core_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1042,7 +1152,7 @@ func (x *ListFluxCrdsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListFluxCrdsRequest.ProtoReflect.Descriptor instead. func (*ListFluxCrdsRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{14} + return file_api_core_core_proto_rawDescGZIP(), []int{16} } func (x *ListFluxCrdsRequest) GetClusterName() string { @@ -1064,7 +1174,7 @@ type ListFluxCrdsResponse struct { func (x *ListFluxCrdsResponse) Reset() { *x = ListFluxCrdsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[15] + mi := &file_api_core_core_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1077,7 +1187,7 @@ func (x *ListFluxCrdsResponse) String() string { func (*ListFluxCrdsResponse) ProtoMessage() {} func (x *ListFluxCrdsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[15] + mi := &file_api_core_core_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1090,7 +1200,7 @@ func (x *ListFluxCrdsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListFluxCrdsResponse.ProtoReflect.Descriptor instead. func (*ListFluxCrdsResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{15} + return file_api_core_core_proto_rawDescGZIP(), []int{17} } func (x *ListFluxCrdsResponse) GetCrds() []*Crd { @@ -1107,6 +1217,108 @@ func (x *ListFluxCrdsResponse) GetErrors() []*ListError { return nil } +type ListRuntimeCrdsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClusterName string `protobuf:"bytes,1,opt,name=clusterName,proto3" json:"clusterName,omitempty"` +} + +func (x *ListRuntimeCrdsRequest) Reset() { + *x = ListRuntimeCrdsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_core_core_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListRuntimeCrdsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListRuntimeCrdsRequest) ProtoMessage() {} + +func (x *ListRuntimeCrdsRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_core_core_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListRuntimeCrdsRequest.ProtoReflect.Descriptor instead. +func (*ListRuntimeCrdsRequest) Descriptor() ([]byte, []int) { + return file_api_core_core_proto_rawDescGZIP(), []int{18} +} + +func (x *ListRuntimeCrdsRequest) GetClusterName() string { + if x != nil { + return x.ClusterName + } + return "" +} + +type ListRuntimeCrdsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Crds []*Crd `protobuf:"bytes,1,rep,name=crds,proto3" json:"crds,omitempty"` + Errors []*ListError `protobuf:"bytes,2,rep,name=errors,proto3" json:"errors,omitempty"` +} + +func (x *ListRuntimeCrdsResponse) Reset() { + *x = ListRuntimeCrdsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_core_core_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListRuntimeCrdsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListRuntimeCrdsResponse) ProtoMessage() {} + +func (x *ListRuntimeCrdsResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_core_core_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListRuntimeCrdsResponse.ProtoReflect.Descriptor instead. +func (*ListRuntimeCrdsResponse) Descriptor() ([]byte, []int) { + return file_api_core_core_proto_rawDescGZIP(), []int{19} +} + +func (x *ListRuntimeCrdsResponse) GetCrds() []*Crd { + if x != nil { + return x.Crds + } + return nil +} + +func (x *ListRuntimeCrdsResponse) GetErrors() []*ListError { + if x != nil { + return x.Errors + } + return nil +} + type GetObjectRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1121,7 +1333,7 @@ type GetObjectRequest struct { func (x *GetObjectRequest) Reset() { *x = GetObjectRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[16] + mi := &file_api_core_core_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1134,7 +1346,7 @@ func (x *GetObjectRequest) String() string { func (*GetObjectRequest) ProtoMessage() {} func (x *GetObjectRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[16] + mi := &file_api_core_core_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1147,7 +1359,7 @@ func (x *GetObjectRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetObjectRequest.ProtoReflect.Descriptor instead. func (*GetObjectRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{16} + return file_api_core_core_proto_rawDescGZIP(), []int{20} } func (x *GetObjectRequest) GetName() string { @@ -1189,7 +1401,7 @@ type GetObjectResponse struct { func (x *GetObjectResponse) Reset() { *x = GetObjectResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[17] + mi := &file_api_core_core_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1202,7 +1414,7 @@ func (x *GetObjectResponse) String() string { func (*GetObjectResponse) ProtoMessage() {} func (x *GetObjectResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[17] + mi := &file_api_core_core_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1215,7 +1427,7 @@ func (x *GetObjectResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetObjectResponse.ProtoReflect.Descriptor instead. func (*GetObjectResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{17} + return file_api_core_core_proto_rawDescGZIP(), []int{21} } func (x *GetObjectResponse) GetObject() *Object { @@ -1239,7 +1451,7 @@ type ListObjectsRequest struct { func (x *ListObjectsRequest) Reset() { *x = ListObjectsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[18] + mi := &file_api_core_core_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1252,7 +1464,7 @@ func (x *ListObjectsRequest) String() string { func (*ListObjectsRequest) ProtoMessage() {} func (x *ListObjectsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[18] + mi := &file_api_core_core_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1265,7 +1477,7 @@ func (x *ListObjectsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListObjectsRequest.ProtoReflect.Descriptor instead. func (*ListObjectsRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{18} + return file_api_core_core_proto_rawDescGZIP(), []int{22} } func (x *ListObjectsRequest) GetNamespace() string { @@ -1308,7 +1520,7 @@ type ClusterNamespaceList struct { func (x *ClusterNamespaceList) Reset() { *x = ClusterNamespaceList{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[19] + mi := &file_api_core_core_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1321,7 +1533,7 @@ func (x *ClusterNamespaceList) String() string { func (*ClusterNamespaceList) ProtoMessage() {} func (x *ClusterNamespaceList) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[19] + mi := &file_api_core_core_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1334,7 +1546,7 @@ func (x *ClusterNamespaceList) ProtoReflect() protoreflect.Message { // Deprecated: Use ClusterNamespaceList.ProtoReflect.Descriptor instead. func (*ClusterNamespaceList) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{19} + return file_api_core_core_proto_rawDescGZIP(), []int{23} } func (x *ClusterNamespaceList) GetClusterName() string { @@ -1364,7 +1576,7 @@ type ListObjectsResponse struct { func (x *ListObjectsResponse) Reset() { *x = ListObjectsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[20] + mi := &file_api_core_core_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1377,7 +1589,7 @@ func (x *ListObjectsResponse) String() string { func (*ListObjectsResponse) ProtoMessage() {} func (x *ListObjectsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[20] + mi := &file_api_core_core_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1390,7 +1602,7 @@ func (x *ListObjectsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListObjectsResponse.ProtoReflect.Descriptor instead. func (*ListObjectsResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{20} + return file_api_core_core_proto_rawDescGZIP(), []int{24} } func (x *ListObjectsResponse) GetObjects() []*Object { @@ -1429,7 +1641,7 @@ type GetReconciledObjectsRequest struct { func (x *GetReconciledObjectsRequest) Reset() { *x = GetReconciledObjectsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[21] + mi := &file_api_core_core_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1442,7 +1654,7 @@ func (x *GetReconciledObjectsRequest) String() string { func (*GetReconciledObjectsRequest) ProtoMessage() {} func (x *GetReconciledObjectsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[21] + mi := &file_api_core_core_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1455,7 +1667,7 @@ func (x *GetReconciledObjectsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetReconciledObjectsRequest.ProtoReflect.Descriptor instead. func (*GetReconciledObjectsRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{21} + return file_api_core_core_proto_rawDescGZIP(), []int{25} } func (x *GetReconciledObjectsRequest) GetAutomationName() string { @@ -1504,7 +1716,7 @@ type GetReconciledObjectsResponse struct { func (x *GetReconciledObjectsResponse) Reset() { *x = GetReconciledObjectsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[22] + mi := &file_api_core_core_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1517,7 +1729,7 @@ func (x *GetReconciledObjectsResponse) String() string { func (*GetReconciledObjectsResponse) ProtoMessage() {} func (x *GetReconciledObjectsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[22] + mi := &file_api_core_core_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1530,7 +1742,7 @@ func (x *GetReconciledObjectsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetReconciledObjectsResponse.ProtoReflect.Descriptor instead. func (*GetReconciledObjectsResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{22} + return file_api_core_core_proto_rawDescGZIP(), []int{26} } func (x *GetReconciledObjectsResponse) GetObjects() []*Object { @@ -1554,7 +1766,7 @@ type GetChildObjectsRequest struct { func (x *GetChildObjectsRequest) Reset() { *x = GetChildObjectsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[23] + mi := &file_api_core_core_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1567,7 +1779,7 @@ func (x *GetChildObjectsRequest) String() string { func (*GetChildObjectsRequest) ProtoMessage() {} func (x *GetChildObjectsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[23] + mi := &file_api_core_core_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1580,7 +1792,7 @@ func (x *GetChildObjectsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetChildObjectsRequest.ProtoReflect.Descriptor instead. func (*GetChildObjectsRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{23} + return file_api_core_core_proto_rawDescGZIP(), []int{27} } func (x *GetChildObjectsRequest) GetGroupVersionKind() *GroupVersionKind { @@ -1622,7 +1834,7 @@ type GetChildObjectsResponse struct { func (x *GetChildObjectsResponse) Reset() { *x = GetChildObjectsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[24] + mi := &file_api_core_core_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1635,7 +1847,7 @@ func (x *GetChildObjectsResponse) String() string { func (*GetChildObjectsResponse) ProtoMessage() {} func (x *GetChildObjectsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[24] + mi := &file_api_core_core_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1648,7 +1860,7 @@ func (x *GetChildObjectsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetChildObjectsResponse.ProtoReflect.Descriptor instead. func (*GetChildObjectsResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{24} + return file_api_core_core_proto_rawDescGZIP(), []int{28} } func (x *GetChildObjectsResponse) GetObjects() []*Object { @@ -1667,7 +1879,7 @@ type GetFluxNamespaceRequest struct { func (x *GetFluxNamespaceRequest) Reset() { *x = GetFluxNamespaceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[25] + mi := &file_api_core_core_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1680,7 +1892,7 @@ func (x *GetFluxNamespaceRequest) String() string { func (*GetFluxNamespaceRequest) ProtoMessage() {} func (x *GetFluxNamespaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[25] + mi := &file_api_core_core_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1693,7 +1905,7 @@ func (x *GetFluxNamespaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetFluxNamespaceRequest.ProtoReflect.Descriptor instead. func (*GetFluxNamespaceRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{25} + return file_api_core_core_proto_rawDescGZIP(), []int{29} } type GetFluxNamespaceResponse struct { @@ -1707,7 +1919,7 @@ type GetFluxNamespaceResponse struct { func (x *GetFluxNamespaceResponse) Reset() { *x = GetFluxNamespaceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[26] + mi := &file_api_core_core_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1720,7 +1932,7 @@ func (x *GetFluxNamespaceResponse) String() string { func (*GetFluxNamespaceResponse) ProtoMessage() {} func (x *GetFluxNamespaceResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[26] + mi := &file_api_core_core_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1733,7 +1945,7 @@ func (x *GetFluxNamespaceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetFluxNamespaceResponse.ProtoReflect.Descriptor instead. func (*GetFluxNamespaceResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{26} + return file_api_core_core_proto_rawDescGZIP(), []int{30} } func (x *GetFluxNamespaceResponse) GetName() string { @@ -1752,7 +1964,7 @@ type ListNamespacesRequest struct { func (x *ListNamespacesRequest) Reset() { *x = ListNamespacesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[27] + mi := &file_api_core_core_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1765,7 +1977,7 @@ func (x *ListNamespacesRequest) String() string { func (*ListNamespacesRequest) ProtoMessage() {} func (x *ListNamespacesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[27] + mi := &file_api_core_core_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1778,7 +1990,7 @@ func (x *ListNamespacesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListNamespacesRequest.ProtoReflect.Descriptor instead. func (*ListNamespacesRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{27} + return file_api_core_core_proto_rawDescGZIP(), []int{31} } type ListNamespacesResponse struct { @@ -1792,7 +2004,7 @@ type ListNamespacesResponse struct { func (x *ListNamespacesResponse) Reset() { *x = ListNamespacesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[28] + mi := &file_api_core_core_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1805,7 +2017,7 @@ func (x *ListNamespacesResponse) String() string { func (*ListNamespacesResponse) ProtoMessage() {} func (x *ListNamespacesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[28] + mi := &file_api_core_core_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1818,7 +2030,7 @@ func (x *ListNamespacesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListNamespacesResponse.ProtoReflect.Descriptor instead. func (*ListNamespacesResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{28} + return file_api_core_core_proto_rawDescGZIP(), []int{32} } func (x *ListNamespacesResponse) GetNamespaces() []*Namespace { @@ -1839,7 +2051,7 @@ type ListEventsRequest struct { func (x *ListEventsRequest) Reset() { *x = ListEventsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[29] + mi := &file_api_core_core_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1852,7 +2064,7 @@ func (x *ListEventsRequest) String() string { func (*ListEventsRequest) ProtoMessage() {} func (x *ListEventsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[29] + mi := &file_api_core_core_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1865,7 +2077,7 @@ func (x *ListEventsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListEventsRequest.ProtoReflect.Descriptor instead. func (*ListEventsRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{29} + return file_api_core_core_proto_rawDescGZIP(), []int{33} } func (x *ListEventsRequest) GetInvolvedObject() *ObjectRef { @@ -1886,7 +2098,7 @@ type ListEventsResponse struct { func (x *ListEventsResponse) Reset() { *x = ListEventsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[30] + mi := &file_api_core_core_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1899,7 +2111,7 @@ func (x *ListEventsResponse) String() string { func (*ListEventsResponse) ProtoMessage() {} func (x *ListEventsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[30] + mi := &file_api_core_core_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1912,7 +2124,7 @@ func (x *ListEventsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListEventsResponse.ProtoReflect.Descriptor instead. func (*ListEventsResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{30} + return file_api_core_core_proto_rawDescGZIP(), []int{34} } func (x *ListEventsResponse) GetEvents() []*Event { @@ -1934,7 +2146,7 @@ type SyncFluxObjectRequest struct { func (x *SyncFluxObjectRequest) Reset() { *x = SyncFluxObjectRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[31] + mi := &file_api_core_core_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1947,7 +2159,7 @@ func (x *SyncFluxObjectRequest) String() string { func (*SyncFluxObjectRequest) ProtoMessage() {} func (x *SyncFluxObjectRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[31] + mi := &file_api_core_core_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1960,7 +2172,7 @@ func (x *SyncFluxObjectRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncFluxObjectRequest.ProtoReflect.Descriptor instead. func (*SyncFluxObjectRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{31} + return file_api_core_core_proto_rawDescGZIP(), []int{35} } func (x *SyncFluxObjectRequest) GetObjects() []*ObjectRef { @@ -1986,7 +2198,7 @@ type SyncFluxObjectResponse struct { func (x *SyncFluxObjectResponse) Reset() { *x = SyncFluxObjectResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[32] + mi := &file_api_core_core_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1999,7 +2211,7 @@ func (x *SyncFluxObjectResponse) String() string { func (*SyncFluxObjectResponse) ProtoMessage() {} func (x *SyncFluxObjectResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[32] + mi := &file_api_core_core_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2012,7 +2224,7 @@ func (x *SyncFluxObjectResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncFluxObjectResponse.ProtoReflect.Descriptor instead. func (*SyncFluxObjectResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{32} + return file_api_core_core_proto_rawDescGZIP(), []int{36} } type GetVersionRequest struct { @@ -2024,7 +2236,7 @@ type GetVersionRequest struct { func (x *GetVersionRequest) Reset() { *x = GetVersionRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[33] + mi := &file_api_core_core_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2037,7 +2249,7 @@ func (x *GetVersionRequest) String() string { func (*GetVersionRequest) ProtoMessage() {} func (x *GetVersionRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[33] + mi := &file_api_core_core_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2050,7 +2262,7 @@ func (x *GetVersionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetVersionRequest.ProtoReflect.Descriptor instead. func (*GetVersionRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{33} + return file_api_core_core_proto_rawDescGZIP(), []int{37} } type GetVersionResponse struct { @@ -2068,7 +2280,7 @@ type GetVersionResponse struct { func (x *GetVersionResponse) Reset() { *x = GetVersionResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[34] + mi := &file_api_core_core_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2081,7 +2293,7 @@ func (x *GetVersionResponse) String() string { func (*GetVersionResponse) ProtoMessage() {} func (x *GetVersionResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[34] + mi := &file_api_core_core_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2094,7 +2306,7 @@ func (x *GetVersionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetVersionResponse.ProtoReflect.Descriptor instead. func (*GetVersionResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{34} + return file_api_core_core_proto_rawDescGZIP(), []int{38} } func (x *GetVersionResponse) GetSemver() string { @@ -2141,7 +2353,7 @@ type GetFeatureFlagsRequest struct { func (x *GetFeatureFlagsRequest) Reset() { *x = GetFeatureFlagsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[35] + mi := &file_api_core_core_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2154,7 +2366,7 @@ func (x *GetFeatureFlagsRequest) String() string { func (*GetFeatureFlagsRequest) ProtoMessage() {} func (x *GetFeatureFlagsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[35] + mi := &file_api_core_core_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2167,7 +2379,7 @@ func (x *GetFeatureFlagsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetFeatureFlagsRequest.ProtoReflect.Descriptor instead. func (*GetFeatureFlagsRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{35} + return file_api_core_core_proto_rawDescGZIP(), []int{39} } type GetFeatureFlagsResponse struct { @@ -2181,7 +2393,7 @@ type GetFeatureFlagsResponse struct { func (x *GetFeatureFlagsResponse) Reset() { *x = GetFeatureFlagsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[36] + mi := &file_api_core_core_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2194,7 +2406,7 @@ func (x *GetFeatureFlagsResponse) String() string { func (*GetFeatureFlagsResponse) ProtoMessage() {} func (x *GetFeatureFlagsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[36] + mi := &file_api_core_core_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2207,7 +2419,7 @@ func (x *GetFeatureFlagsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetFeatureFlagsResponse.ProtoReflect.Descriptor instead. func (*GetFeatureFlagsResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{36} + return file_api_core_core_proto_rawDescGZIP(), []int{40} } func (x *GetFeatureFlagsResponse) GetFlags() map[string]string { @@ -2230,7 +2442,7 @@ type ToggleSuspendResourceRequest struct { func (x *ToggleSuspendResourceRequest) Reset() { *x = ToggleSuspendResourceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[37] + mi := &file_api_core_core_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2243,7 +2455,7 @@ func (x *ToggleSuspendResourceRequest) String() string { func (*ToggleSuspendResourceRequest) ProtoMessage() {} func (x *ToggleSuspendResourceRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[37] + mi := &file_api_core_core_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2256,7 +2468,7 @@ func (x *ToggleSuspendResourceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ToggleSuspendResourceRequest.ProtoReflect.Descriptor instead. func (*ToggleSuspendResourceRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{37} + return file_api_core_core_proto_rawDescGZIP(), []int{41} } func (x *ToggleSuspendResourceRequest) GetObjects() []*ObjectRef { @@ -2289,7 +2501,7 @@ type ToggleSuspendResourceResponse struct { func (x *ToggleSuspendResourceResponse) Reset() { *x = ToggleSuspendResourceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[38] + mi := &file_api_core_core_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2302,7 +2514,7 @@ func (x *ToggleSuspendResourceResponse) String() string { func (*ToggleSuspendResourceResponse) ProtoMessage() {} func (x *ToggleSuspendResourceResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[38] + mi := &file_api_core_core_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2315,7 +2527,7 @@ func (x *ToggleSuspendResourceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ToggleSuspendResourceResponse.ProtoReflect.Descriptor instead. func (*ToggleSuspendResourceResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{38} + return file_api_core_core_proto_rawDescGZIP(), []int{42} } type GetSessionLogsRequest struct { @@ -2333,7 +2545,7 @@ type GetSessionLogsRequest struct { func (x *GetSessionLogsRequest) Reset() { *x = GetSessionLogsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[39] + mi := &file_api_core_core_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2346,7 +2558,7 @@ func (x *GetSessionLogsRequest) String() string { func (*GetSessionLogsRequest) ProtoMessage() {} func (x *GetSessionLogsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[39] + mi := &file_api_core_core_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2359,7 +2571,7 @@ func (x *GetSessionLogsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSessionLogsRequest.ProtoReflect.Descriptor instead. func (*GetSessionLogsRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{39} + return file_api_core_core_proto_rawDescGZIP(), []int{43} } func (x *GetSessionLogsRequest) GetSessionNamespace() string { @@ -2412,7 +2624,7 @@ type LogEntry struct { func (x *LogEntry) Reset() { *x = LogEntry{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[40] + mi := &file_api_core_core_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2425,7 +2637,7 @@ func (x *LogEntry) String() string { func (*LogEntry) ProtoMessage() {} func (x *LogEntry) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[40] + mi := &file_api_core_core_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2438,7 +2650,7 @@ func (x *LogEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use LogEntry.ProtoReflect.Descriptor instead. func (*LogEntry) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{40} + return file_api_core_core_proto_rawDescGZIP(), []int{44} } func (x *LogEntry) GetTimestamp() string { @@ -2490,7 +2702,7 @@ type GetSessionLogsResponse struct { func (x *GetSessionLogsResponse) Reset() { *x = GetSessionLogsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[41] + mi := &file_api_core_core_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2503,7 +2715,7 @@ func (x *GetSessionLogsResponse) String() string { func (*GetSessionLogsResponse) ProtoMessage() {} func (x *GetSessionLogsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[41] + mi := &file_api_core_core_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2516,7 +2728,7 @@ func (x *GetSessionLogsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSessionLogsResponse.ProtoReflect.Descriptor instead. func (*GetSessionLogsResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{41} + return file_api_core_core_proto_rawDescGZIP(), []int{45} } func (x *GetSessionLogsResponse) GetLogs() []*LogEntry { @@ -2558,7 +2770,7 @@ type IsCRDAvailableRequest struct { func (x *IsCRDAvailableRequest) Reset() { *x = IsCRDAvailableRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[42] + mi := &file_api_core_core_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2571,7 +2783,7 @@ func (x *IsCRDAvailableRequest) String() string { func (*IsCRDAvailableRequest) ProtoMessage() {} func (x *IsCRDAvailableRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[42] + mi := &file_api_core_core_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2584,7 +2796,7 @@ func (x *IsCRDAvailableRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use IsCRDAvailableRequest.ProtoReflect.Descriptor instead. func (*IsCRDAvailableRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{42} + return file_api_core_core_proto_rawDescGZIP(), []int{46} } func (x *IsCRDAvailableRequest) GetName() string { @@ -2605,7 +2817,7 @@ type IsCRDAvailableResponse struct { func (x *IsCRDAvailableResponse) Reset() { *x = IsCRDAvailableResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[43] + mi := &file_api_core_core_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2618,7 +2830,7 @@ func (x *IsCRDAvailableResponse) String() string { func (*IsCRDAvailableResponse) ProtoMessage() {} func (x *IsCRDAvailableResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[43] + mi := &file_api_core_core_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2631,7 +2843,7 @@ func (x *IsCRDAvailableResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use IsCRDAvailableResponse.ProtoReflect.Descriptor instead. func (*IsCRDAvailableResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{43} + return file_api_core_core_proto_rawDescGZIP(), []int{47} } func (x *IsCRDAvailableResponse) GetClusters() map[string]bool { @@ -2653,7 +2865,7 @@ type ListPoliciesRequest struct { func (x *ListPoliciesRequest) Reset() { *x = ListPoliciesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[44] + mi := &file_api_core_core_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2666,7 +2878,7 @@ func (x *ListPoliciesRequest) String() string { func (*ListPoliciesRequest) ProtoMessage() {} func (x *ListPoliciesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[44] + mi := &file_api_core_core_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2679,7 +2891,7 @@ func (x *ListPoliciesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListPoliciesRequest.ProtoReflect.Descriptor instead. func (*ListPoliciesRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{44} + return file_api_core_core_proto_rawDescGZIP(), []int{48} } func (x *ListPoliciesRequest) GetClusterName() string { @@ -2710,7 +2922,7 @@ type ListPoliciesResponse struct { func (x *ListPoliciesResponse) Reset() { *x = ListPoliciesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[45] + mi := &file_api_core_core_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2723,7 +2935,7 @@ func (x *ListPoliciesResponse) String() string { func (*ListPoliciesResponse) ProtoMessage() {} func (x *ListPoliciesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[45] + mi := &file_api_core_core_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2736,7 +2948,7 @@ func (x *ListPoliciesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListPoliciesResponse.ProtoReflect.Descriptor instead. func (*ListPoliciesResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{45} + return file_api_core_core_proto_rawDescGZIP(), []int{49} } func (x *ListPoliciesResponse) GetPolicies() []*PolicyObj { @@ -2779,7 +2991,7 @@ type GetPolicyRequest struct { func (x *GetPolicyRequest) Reset() { *x = GetPolicyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[46] + mi := &file_api_core_core_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2792,7 +3004,7 @@ func (x *GetPolicyRequest) String() string { func (*GetPolicyRequest) ProtoMessage() {} func (x *GetPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[46] + mi := &file_api_core_core_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2805,7 +3017,7 @@ func (x *GetPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetPolicyRequest.ProtoReflect.Descriptor instead. func (*GetPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{46} + return file_api_core_core_proto_rawDescGZIP(), []int{50} } func (x *GetPolicyRequest) GetPolicyName() string { @@ -2834,7 +3046,7 @@ type GetPolicyResponse struct { func (x *GetPolicyResponse) Reset() { *x = GetPolicyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[47] + mi := &file_api_core_core_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2847,7 +3059,7 @@ func (x *GetPolicyResponse) String() string { func (*GetPolicyResponse) ProtoMessage() {} func (x *GetPolicyResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[47] + mi := &file_api_core_core_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2860,7 +3072,7 @@ func (x *GetPolicyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetPolicyResponse.ProtoReflect.Descriptor instead. func (*GetPolicyResponse) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{47} + return file_api_core_core_proto_rawDescGZIP(), []int{51} } func (x *GetPolicyResponse) GetPolicy() *PolicyObj { @@ -2903,7 +3115,7 @@ type PolicyObj struct { func (x *PolicyObj) Reset() { *x = PolicyObj{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[48] + mi := &file_api_core_core_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2916,7 +3128,7 @@ func (x *PolicyObj) String() string { func (*PolicyObj) ProtoMessage() {} func (x *PolicyObj) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[48] + mi := &file_api_core_core_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2929,7 +3141,7 @@ func (x *PolicyObj) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyObj.ProtoReflect.Descriptor instead. func (*PolicyObj) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{48} + return file_api_core_core_proto_rawDescGZIP(), []int{52} } func (x *PolicyObj) GetName() string { @@ -3056,7 +3268,7 @@ type PolicyStandard struct { func (x *PolicyStandard) Reset() { *x = PolicyStandard{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[49] + mi := &file_api_core_core_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3069,7 +3281,7 @@ func (x *PolicyStandard) String() string { func (*PolicyStandard) ProtoMessage() {} func (x *PolicyStandard) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[49] + mi := &file_api_core_core_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3082,7 +3294,7 @@ func (x *PolicyStandard) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyStandard.ProtoReflect.Descriptor instead. func (*PolicyStandard) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{49} + return file_api_core_core_proto_rawDescGZIP(), []int{53} } func (x *PolicyStandard) GetId() string { @@ -3115,7 +3327,7 @@ type PolicyParam struct { func (x *PolicyParam) Reset() { *x = PolicyParam{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[50] + mi := &file_api_core_core_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3128,7 +3340,7 @@ func (x *PolicyParam) String() string { func (*PolicyParam) ProtoMessage() {} func (x *PolicyParam) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[50] + mi := &file_api_core_core_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3141,7 +3353,7 @@ func (x *PolicyParam) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyParam.ProtoReflect.Descriptor instead. func (*PolicyParam) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{50} + return file_api_core_core_proto_rawDescGZIP(), []int{54} } func (x *PolicyParam) GetName() string { @@ -3185,7 +3397,7 @@ type PolicyTargets struct { func (x *PolicyTargets) Reset() { *x = PolicyTargets{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[51] + mi := &file_api_core_core_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3198,7 +3410,7 @@ func (x *PolicyTargets) String() string { func (*PolicyTargets) ProtoMessage() {} func (x *PolicyTargets) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[51] + mi := &file_api_core_core_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3211,7 +3423,7 @@ func (x *PolicyTargets) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyTargets.ProtoReflect.Descriptor instead. func (*PolicyTargets) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{51} + return file_api_core_core_proto_rawDescGZIP(), []int{55} } func (x *PolicyTargets) GetKinds() []string { @@ -3246,7 +3458,7 @@ type PolicyTargetLabel struct { func (x *PolicyTargetLabel) Reset() { *x = PolicyTargetLabel{} if protoimpl.UnsafeEnabled { - mi := &file_api_core_core_proto_msgTypes[52] + mi := &file_api_core_core_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3259,7 +3471,7 @@ func (x *PolicyTargetLabel) String() string { func (*PolicyTargetLabel) ProtoMessage() {} func (x *PolicyTargetLabel) ProtoReflect() protoreflect.Message { - mi := &file_api_core_core_proto_msgTypes[52] + mi := &file_api_core_core_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3272,7 +3484,7 @@ func (x *PolicyTargetLabel) ProtoReflect() protoreflect.Message { // Deprecated: Use PolicyTargetLabel.ProtoReflect.Descriptor instead. func (*PolicyTargetLabel) Descriptor() ([]byte, []int) { - return file_api_core_core_proto_rawDescGZIP(), []int{52} + return file_api_core_core_proto_rawDescGZIP(), []int{56} } func (x *PolicyTargetLabel) GetValues() map[string]string { @@ -3437,11 +3649,37 @@ var file_api_core_core_proto_rawDesc = []byte{ 0x74, 0x73, 0x12, 0x31, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x06, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x37, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x6c, 0x75, - 0x78, 0x43, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, - 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x72, - 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x6c, 0x75, 0x78, 0x43, 0x72, 0x64, 0x73, 0x52, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x5b, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, + 0x6d, 0x65, 0x22, 0x8d, 0x01, 0x0a, 0x1a, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, + 0x6d, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3c, 0x0a, 0x0b, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, + 0x6e, 0x74, 0x52, 0x0b, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, + 0x31, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x73, 0x22, 0x37, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x6c, 0x75, 0x78, 0x43, 0x72, + 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x72, 0x0a, 0x14, 0x4c, + 0x69, 0x73, 0x74, 0x46, 0x6c, 0x75, 0x78, 0x43, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x04, 0x63, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x72, 0x64, 0x52, 0x04, 0x63, 0x72, 0x64, 0x73, 0x12, 0x31, 0x0a, 0x06, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, + 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, + 0x3a, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x72, + 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6c, 0x75, + 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x75, 0x0a, 0x17, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x04, 0x63, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x64, 0x52, 0x04, 0x63, 0x72, 0x64, 0x73, 0x12, @@ -3733,7 +3971,7 @@ var file_api_core_core_proto_rawDesc = []byte{ 0x61, 0x6c, 0x75, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xee, 0x13, 0x0a, 0x04, 0x43, 0x6f, 0x72, 0x65, 0x12, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xf7, 0x15, 0x0a, 0x04, 0x43, 0x6f, 0x72, 0x65, 0x12, 0x6b, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, @@ -3764,146 +4002,162 @@ var file_api_core_core_proto_rawDesc = []byte{ 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x6c, 0x75, 0x78, 0x43, 0x72, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, - 0x2f, 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x63, 0x72, 0x64, 0x73, 0x12, 0x94, 0x01, 0x0a, 0x14, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, - 0x63, 0x74, 0x73, 0x12, 0x2b, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, - 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2c, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x64, 0x4f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x3a, 0x01, 0x2a, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x72, - 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, - 0x73, 0x12, 0x80, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x4f, 0x62, - 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x4f, - 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, - 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, - 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6f, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x73, 0x12, 0x84, 0x01, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x46, 0x6c, 0x75, 0x78, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x27, 0x2e, 0x67, 0x69, 0x74, 0x6f, - 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6c, - 0x75, 0x78, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, + 0x2f, 0x66, 0x6c, 0x75, 0x78, 0x5f, 0x63, 0x72, 0x64, 0x73, 0x12, 0x88, 0x01, 0x0a, 0x12, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x73, 0x12, 0x29, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x67, + 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, + 0x12, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x7c, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x43, 0x72, 0x64, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, + 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x72, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x27, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x43, 0x72, 0x64, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x63, + 0x72, 0x64, 0x73, 0x12, 0x94, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x6e, + 0x63, 0x69, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x2b, 0x2e, 0x67, + 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x67, 0x69, 0x74, 0x6f, + 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, 0x65, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x3a, + 0x01, 0x2a, 0x22, 0x16, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x63, 0x6f, 0x6e, 0x63, 0x69, 0x6c, + 0x65, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x80, 0x01, 0x0a, 0x0f, 0x47, + 0x65, 0x74, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x26, + 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x69, 0x6c, 0x64, + 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x3a, 0x01, 0x2a, 0x22, 0x11, 0x2f, 0x76, 0x31, 0x2f, + 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x84, 0x01, + 0x0a, 0x10, 0x47, 0x65, 0x74, 0x46, 0x6c, 0x75, 0x78, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x12, 0x27, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6c, 0x75, 0x78, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x17, 0x3a, 0x01, 0x2a, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x66, 0x6c, 0x75, 0x78, 0x12, 0x77, 0x0a, 0x0e, 0x4c, - 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x25, 0x2e, + 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x67, 0x69, + 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x46, 0x6c, 0x75, 0x78, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x3a, 0x01, 0x2a, + 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, + 0x66, 0x6c, 0x75, 0x78, 0x12, 0x77, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, + 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x73, 0x12, 0x67, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x74, 0x0a, - 0x0e, 0x53, 0x79, 0x6e, 0x63, 0x46, 0x6c, 0x75, 0x78, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, - 0x25, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, + 0x69, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, + 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x67, 0x0a, + 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x2e, 0x67, 0x69, + 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, + 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, 0x2f, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x74, 0x0a, 0x0e, 0x53, 0x79, 0x6e, 0x63, 0x46, 0x6c, + 0x75, 0x78, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, + 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x46, 0x6c, + 0x75, 0x78, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x26, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x46, 0x6c, 0x75, 0x78, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, - 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x46, 0x6c, 0x75, 0x78, - 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x3a, 0x01, 0x2a, 0x22, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x73, - 0x79, 0x6e, 0x63, 0x12, 0x68, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x21, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, - 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, - 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x7c, 0x0a, - 0x0f, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, - 0x12, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, - 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x66, - 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x8c, 0x01, 0x0a, 0x15, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x3a, + 0x01, 0x2a, 0x22, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x68, 0x0a, 0x0a, + 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x67, 0x69, 0x74, + 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, + 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x7c, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x6f, + 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x27, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x46, 0x6c, 0x61, + 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x12, 0x12, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x66, + 0x6c, 0x61, 0x67, 0x73, 0x12, 0x8c, 0x01, 0x0a, 0x15, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x53, + 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2c, + 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x53, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x2c, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, - 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x53, 0x75, 0x73, - 0x70, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x53, 0x75, 0x73, 0x70, 0x65, - 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, 0x01, 0x2a, 0x22, 0x0b, 0x2f, - 0x76, 0x31, 0x2f, 0x73, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x12, 0x7c, 0x0a, 0x0e, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x25, 0x2e, 0x67, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x67, + 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x6f, + 0x67, 0x67, 0x6c, 0x65, 0x53, 0x75, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x10, 0x3a, 0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x75, 0x73, 0x70, + 0x65, 0x6e, 0x64, 0x12, 0x7c, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, - 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x15, 0x3a, 0x01, 0x2a, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x7d, 0x0a, 0x0e, 0x49, 0x73, 0x43, 0x52, - 0x44, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x69, 0x74, + 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x6f, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, 0x01, 0x2a, 0x22, + 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x6f, 0x67, + 0x73, 0x12, 0x7d, 0x0a, 0x0e, 0x49, 0x73, 0x43, 0x52, 0x44, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x12, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x73, 0x43, 0x52, 0x44, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x73, 0x43, 0x52, - 0x44, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x49, 0x73, 0x43, 0x52, 0x44, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x72, 0x64, 0x2f, 0x69, 0x73, 0x5f, 0x61, 0x76, - 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x70, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x49, 0x6e, - 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x23, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, - 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x76, 0x65, - 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, - 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, - 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x6f, 0x0a, 0x0c, 0x4c, 0x69, 0x73, - 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x67, 0x69, 0x74, 0x6f, - 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, - 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0e, 0x12, 0x0c, 0x2f, 0x76, - 0x31, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x73, 0x0a, 0x09, 0x47, 0x65, - 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, - 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x69, 0x74, 0x6f, - 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, - 0x65, 0x73, 0x2f, 0x7b, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x7d, 0x12, - 0x96, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2c, 0x2e, 0x67, 0x69, 0x74, 0x6f, - 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, + 0x44, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x2f, + 0x63, 0x72, 0x64, 0x2f, 0x69, 0x73, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x12, 0x70, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, + 0x12, 0x23, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, + 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, + 0x72, 0x79, 0x12, 0x6f, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, + 0x65, 0x73, 0x12, 0x23, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, - 0x2a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x9c, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x2a, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x67, - 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x0e, 0x12, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x69, 0x65, 0x73, 0x12, 0x73, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x12, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, + 0x76, 0x31, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x2f, 0x7b, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x7d, 0x12, 0x96, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x26, 0x12, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x7d, 0x42, 0xa4, 0x01, 0x92, 0x41, 0x74, 0x12, 0x4e, 0x0a, - 0x15, 0x57, 0x65, 0x61, 0x76, 0x65, 0x20, 0x47, 0x69, 0x74, 0x4f, 0x70, 0x73, 0x20, 0x43, 0x6f, - 0x72, 0x65, 0x20, 0x41, 0x50, 0x49, 0x12, 0x30, 0x54, 0x68, 0x65, 0x20, 0x41, 0x50, 0x49, 0x20, - 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x57, 0x65, 0x61, 0x76, 0x65, 0x20, 0x47, 0x69, 0x74, - 0x4f, 0x70, 0x73, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x32, 0x10, 0x61, - 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, - 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, - 0x6e, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x77, 0x65, - 0x61, 0x76, 0x65, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x2f, 0x77, 0x65, 0x61, 0x76, 0x65, 0x2d, 0x67, - 0x69, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x73, 0x12, 0x2c, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2d, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x12, 0x9c, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x2e, 0x67, 0x69, 0x74, 0x6f, + 0x70, 0x73, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x5f, 0x63, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x2c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x12, 0x24, 0x2f, 0x76, 0x31, 0x2f, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2f, 0x7b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x7d, + 0x42, 0xa4, 0x01, 0x92, 0x41, 0x74, 0x12, 0x4e, 0x0a, 0x15, 0x57, 0x65, 0x61, 0x76, 0x65, 0x20, + 0x47, 0x69, 0x74, 0x4f, 0x70, 0x73, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x41, 0x50, 0x49, 0x12, + 0x30, 0x54, 0x68, 0x65, 0x20, 0x41, 0x50, 0x49, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, + 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x57, 0x65, 0x61, 0x76, 0x65, 0x20, 0x47, 0x69, 0x74, 0x4f, 0x70, 0x73, 0x20, 0x43, 0x6f, 0x72, + 0x65, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x77, 0x65, 0x61, 0x76, 0x65, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x2f, 0x77, 0x65, 0x61, 0x76, 0x65, 0x2d, 0x67, 0x69, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x63, + 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -3918,7 +4172,7 @@ func file_api_core_core_proto_rawDescGZIP() []byte { return file_api_core_core_proto_rawDescData } -var file_api_core_core_proto_msgTypes = make([]protoimpl.MessageInfo, 57) +var file_api_core_core_proto_msgTypes = make([]protoimpl.MessageInfo, 61) var file_api_core_core_proto_goTypes = []interface{}{ (*GetInventoryRequest)(nil), // 0: gitops_core.v1.GetInventoryRequest (*GetInventoryResponse)(nil), // 1: gitops_core.v1.GetInventoryResponse @@ -3934,144 +4188,156 @@ var file_api_core_core_proto_goTypes = []interface{}{ (*ListError)(nil), // 11: gitops_core.v1.ListError (*ListFluxRuntimeObjectsRequest)(nil), // 12: gitops_core.v1.ListFluxRuntimeObjectsRequest (*ListFluxRuntimeObjectsResponse)(nil), // 13: gitops_core.v1.ListFluxRuntimeObjectsResponse - (*ListFluxCrdsRequest)(nil), // 14: gitops_core.v1.ListFluxCrdsRequest - (*ListFluxCrdsResponse)(nil), // 15: gitops_core.v1.ListFluxCrdsResponse - (*GetObjectRequest)(nil), // 16: gitops_core.v1.GetObjectRequest - (*GetObjectResponse)(nil), // 17: gitops_core.v1.GetObjectResponse - (*ListObjectsRequest)(nil), // 18: gitops_core.v1.ListObjectsRequest - (*ClusterNamespaceList)(nil), // 19: gitops_core.v1.ClusterNamespaceList - (*ListObjectsResponse)(nil), // 20: gitops_core.v1.ListObjectsResponse - (*GetReconciledObjectsRequest)(nil), // 21: gitops_core.v1.GetReconciledObjectsRequest - (*GetReconciledObjectsResponse)(nil), // 22: gitops_core.v1.GetReconciledObjectsResponse - (*GetChildObjectsRequest)(nil), // 23: gitops_core.v1.GetChildObjectsRequest - (*GetChildObjectsResponse)(nil), // 24: gitops_core.v1.GetChildObjectsResponse - (*GetFluxNamespaceRequest)(nil), // 25: gitops_core.v1.GetFluxNamespaceRequest - (*GetFluxNamespaceResponse)(nil), // 26: gitops_core.v1.GetFluxNamespaceResponse - (*ListNamespacesRequest)(nil), // 27: gitops_core.v1.ListNamespacesRequest - (*ListNamespacesResponse)(nil), // 28: gitops_core.v1.ListNamespacesResponse - (*ListEventsRequest)(nil), // 29: gitops_core.v1.ListEventsRequest - (*ListEventsResponse)(nil), // 30: gitops_core.v1.ListEventsResponse - (*SyncFluxObjectRequest)(nil), // 31: gitops_core.v1.SyncFluxObjectRequest - (*SyncFluxObjectResponse)(nil), // 32: gitops_core.v1.SyncFluxObjectResponse - (*GetVersionRequest)(nil), // 33: gitops_core.v1.GetVersionRequest - (*GetVersionResponse)(nil), // 34: gitops_core.v1.GetVersionResponse - (*GetFeatureFlagsRequest)(nil), // 35: gitops_core.v1.GetFeatureFlagsRequest - (*GetFeatureFlagsResponse)(nil), // 36: gitops_core.v1.GetFeatureFlagsResponse - (*ToggleSuspendResourceRequest)(nil), // 37: gitops_core.v1.ToggleSuspendResourceRequest - (*ToggleSuspendResourceResponse)(nil), // 38: gitops_core.v1.ToggleSuspendResourceResponse - (*GetSessionLogsRequest)(nil), // 39: gitops_core.v1.GetSessionLogsRequest - (*LogEntry)(nil), // 40: gitops_core.v1.LogEntry - (*GetSessionLogsResponse)(nil), // 41: gitops_core.v1.GetSessionLogsResponse - (*IsCRDAvailableRequest)(nil), // 42: gitops_core.v1.IsCRDAvailableRequest - (*IsCRDAvailableResponse)(nil), // 43: gitops_core.v1.IsCRDAvailableResponse - (*ListPoliciesRequest)(nil), // 44: gitops_core.v1.ListPoliciesRequest - (*ListPoliciesResponse)(nil), // 45: gitops_core.v1.ListPoliciesResponse - (*GetPolicyRequest)(nil), // 46: gitops_core.v1.GetPolicyRequest - (*GetPolicyResponse)(nil), // 47: gitops_core.v1.GetPolicyResponse - (*PolicyObj)(nil), // 48: gitops_core.v1.PolicyObj - (*PolicyStandard)(nil), // 49: gitops_core.v1.PolicyStandard - (*PolicyParam)(nil), // 50: gitops_core.v1.PolicyParam - (*PolicyTargets)(nil), // 51: gitops_core.v1.PolicyTargets - (*PolicyTargetLabel)(nil), // 52: gitops_core.v1.PolicyTargetLabel - nil, // 53: gitops_core.v1.ListObjectsRequest.LabelsEntry - nil, // 54: gitops_core.v1.GetFeatureFlagsResponse.FlagsEntry - nil, // 55: gitops_core.v1.IsCRDAvailableResponse.ClustersEntry - nil, // 56: gitops_core.v1.PolicyTargetLabel.ValuesEntry - (*InventoryEntry)(nil), // 57: gitops_core.v1.InventoryEntry - (*anypb.Any)(nil), // 58: google.protobuf.Any - (*Deployment)(nil), // 59: gitops_core.v1.Deployment - (*Crd)(nil), // 60: gitops_core.v1.Crd - (*Object)(nil), // 61: gitops_core.v1.Object - (*GroupVersionKind)(nil), // 62: gitops_core.v1.GroupVersionKind - (*Namespace)(nil), // 63: gitops_core.v1.Namespace - (*ObjectRef)(nil), // 64: gitops_core.v1.ObjectRef - (*Event)(nil), // 65: gitops_core.v1.Event + (*ListRuntimeObjectsRequest)(nil), // 14: gitops_core.v1.ListRuntimeObjectsRequest + (*ListRuntimeObjectsResponse)(nil), // 15: gitops_core.v1.ListRuntimeObjectsResponse + (*ListFluxCrdsRequest)(nil), // 16: gitops_core.v1.ListFluxCrdsRequest + (*ListFluxCrdsResponse)(nil), // 17: gitops_core.v1.ListFluxCrdsResponse + (*ListRuntimeCrdsRequest)(nil), // 18: gitops_core.v1.ListRuntimeCrdsRequest + (*ListRuntimeCrdsResponse)(nil), // 19: gitops_core.v1.ListRuntimeCrdsResponse + (*GetObjectRequest)(nil), // 20: gitops_core.v1.GetObjectRequest + (*GetObjectResponse)(nil), // 21: gitops_core.v1.GetObjectResponse + (*ListObjectsRequest)(nil), // 22: gitops_core.v1.ListObjectsRequest + (*ClusterNamespaceList)(nil), // 23: gitops_core.v1.ClusterNamespaceList + (*ListObjectsResponse)(nil), // 24: gitops_core.v1.ListObjectsResponse + (*GetReconciledObjectsRequest)(nil), // 25: gitops_core.v1.GetReconciledObjectsRequest + (*GetReconciledObjectsResponse)(nil), // 26: gitops_core.v1.GetReconciledObjectsResponse + (*GetChildObjectsRequest)(nil), // 27: gitops_core.v1.GetChildObjectsRequest + (*GetChildObjectsResponse)(nil), // 28: gitops_core.v1.GetChildObjectsResponse + (*GetFluxNamespaceRequest)(nil), // 29: gitops_core.v1.GetFluxNamespaceRequest + (*GetFluxNamespaceResponse)(nil), // 30: gitops_core.v1.GetFluxNamespaceResponse + (*ListNamespacesRequest)(nil), // 31: gitops_core.v1.ListNamespacesRequest + (*ListNamespacesResponse)(nil), // 32: gitops_core.v1.ListNamespacesResponse + (*ListEventsRequest)(nil), // 33: gitops_core.v1.ListEventsRequest + (*ListEventsResponse)(nil), // 34: gitops_core.v1.ListEventsResponse + (*SyncFluxObjectRequest)(nil), // 35: gitops_core.v1.SyncFluxObjectRequest + (*SyncFluxObjectResponse)(nil), // 36: gitops_core.v1.SyncFluxObjectResponse + (*GetVersionRequest)(nil), // 37: gitops_core.v1.GetVersionRequest + (*GetVersionResponse)(nil), // 38: gitops_core.v1.GetVersionResponse + (*GetFeatureFlagsRequest)(nil), // 39: gitops_core.v1.GetFeatureFlagsRequest + (*GetFeatureFlagsResponse)(nil), // 40: gitops_core.v1.GetFeatureFlagsResponse + (*ToggleSuspendResourceRequest)(nil), // 41: gitops_core.v1.ToggleSuspendResourceRequest + (*ToggleSuspendResourceResponse)(nil), // 42: gitops_core.v1.ToggleSuspendResourceResponse + (*GetSessionLogsRequest)(nil), // 43: gitops_core.v1.GetSessionLogsRequest + (*LogEntry)(nil), // 44: gitops_core.v1.LogEntry + (*GetSessionLogsResponse)(nil), // 45: gitops_core.v1.GetSessionLogsResponse + (*IsCRDAvailableRequest)(nil), // 46: gitops_core.v1.IsCRDAvailableRequest + (*IsCRDAvailableResponse)(nil), // 47: gitops_core.v1.IsCRDAvailableResponse + (*ListPoliciesRequest)(nil), // 48: gitops_core.v1.ListPoliciesRequest + (*ListPoliciesResponse)(nil), // 49: gitops_core.v1.ListPoliciesResponse + (*GetPolicyRequest)(nil), // 50: gitops_core.v1.GetPolicyRequest + (*GetPolicyResponse)(nil), // 51: gitops_core.v1.GetPolicyResponse + (*PolicyObj)(nil), // 52: gitops_core.v1.PolicyObj + (*PolicyStandard)(nil), // 53: gitops_core.v1.PolicyStandard + (*PolicyParam)(nil), // 54: gitops_core.v1.PolicyParam + (*PolicyTargets)(nil), // 55: gitops_core.v1.PolicyTargets + (*PolicyTargetLabel)(nil), // 56: gitops_core.v1.PolicyTargetLabel + nil, // 57: gitops_core.v1.ListObjectsRequest.LabelsEntry + nil, // 58: gitops_core.v1.GetFeatureFlagsResponse.FlagsEntry + nil, // 59: gitops_core.v1.IsCRDAvailableResponse.ClustersEntry + nil, // 60: gitops_core.v1.PolicyTargetLabel.ValuesEntry + (*InventoryEntry)(nil), // 61: gitops_core.v1.InventoryEntry + (*anypb.Any)(nil), // 62: google.protobuf.Any + (*Deployment)(nil), // 63: gitops_core.v1.Deployment + (*Crd)(nil), // 64: gitops_core.v1.Crd + (*Object)(nil), // 65: gitops_core.v1.Object + (*GroupVersionKind)(nil), // 66: gitops_core.v1.GroupVersionKind + (*Namespace)(nil), // 67: gitops_core.v1.Namespace + (*ObjectRef)(nil), // 68: gitops_core.v1.ObjectRef + (*Event)(nil), // 69: gitops_core.v1.Event } var file_api_core_core_proto_depIdxs = []int32{ - 57, // 0: gitops_core.v1.GetInventoryResponse.entries:type_name -> gitops_core.v1.InventoryEntry + 61, // 0: gitops_core.v1.GetInventoryResponse.entries:type_name -> gitops_core.v1.InventoryEntry 7, // 1: gitops_core.v1.PolicyValidation.occurrences:type_name -> gitops_core.v1.PolicyValidationOccurrence 8, // 2: gitops_core.v1.PolicyValidation.parameters:type_name -> gitops_core.v1.PolicyValidationParam 10, // 3: gitops_core.v1.ListPolicyValidationsRequest.pagination:type_name -> gitops_core.v1.Pagination 2, // 4: gitops_core.v1.ListPolicyValidationsResponse.violations:type_name -> gitops_core.v1.PolicyValidation 11, // 5: gitops_core.v1.ListPolicyValidationsResponse.errors:type_name -> gitops_core.v1.ListError 2, // 6: gitops_core.v1.GetPolicyValidationResponse.validation:type_name -> gitops_core.v1.PolicyValidation - 58, // 7: gitops_core.v1.PolicyValidationParam.value:type_name -> google.protobuf.Any - 59, // 8: gitops_core.v1.ListFluxRuntimeObjectsResponse.deployments:type_name -> gitops_core.v1.Deployment + 62, // 7: gitops_core.v1.PolicyValidationParam.value:type_name -> google.protobuf.Any + 63, // 8: gitops_core.v1.ListFluxRuntimeObjectsResponse.deployments:type_name -> gitops_core.v1.Deployment 11, // 9: gitops_core.v1.ListFluxRuntimeObjectsResponse.errors:type_name -> gitops_core.v1.ListError - 60, // 10: gitops_core.v1.ListFluxCrdsResponse.crds:type_name -> gitops_core.v1.Crd - 11, // 11: gitops_core.v1.ListFluxCrdsResponse.errors:type_name -> gitops_core.v1.ListError - 61, // 12: gitops_core.v1.GetObjectResponse.object:type_name -> gitops_core.v1.Object - 53, // 13: gitops_core.v1.ListObjectsRequest.labels:type_name -> gitops_core.v1.ListObjectsRequest.LabelsEntry - 61, // 14: gitops_core.v1.ListObjectsResponse.objects:type_name -> gitops_core.v1.Object - 11, // 15: gitops_core.v1.ListObjectsResponse.errors:type_name -> gitops_core.v1.ListError - 19, // 16: gitops_core.v1.ListObjectsResponse.searchedNamespaces:type_name -> gitops_core.v1.ClusterNamespaceList - 62, // 17: gitops_core.v1.GetReconciledObjectsRequest.kinds:type_name -> gitops_core.v1.GroupVersionKind - 61, // 18: gitops_core.v1.GetReconciledObjectsResponse.objects:type_name -> gitops_core.v1.Object - 62, // 19: gitops_core.v1.GetChildObjectsRequest.groupVersionKind:type_name -> gitops_core.v1.GroupVersionKind - 61, // 20: gitops_core.v1.GetChildObjectsResponse.objects:type_name -> gitops_core.v1.Object - 63, // 21: gitops_core.v1.ListNamespacesResponse.namespaces:type_name -> gitops_core.v1.Namespace - 64, // 22: gitops_core.v1.ListEventsRequest.involvedObject:type_name -> gitops_core.v1.ObjectRef - 65, // 23: gitops_core.v1.ListEventsResponse.events:type_name -> gitops_core.v1.Event - 64, // 24: gitops_core.v1.SyncFluxObjectRequest.objects:type_name -> gitops_core.v1.ObjectRef - 54, // 25: gitops_core.v1.GetFeatureFlagsResponse.flags:type_name -> gitops_core.v1.GetFeatureFlagsResponse.FlagsEntry - 64, // 26: gitops_core.v1.ToggleSuspendResourceRequest.objects:type_name -> gitops_core.v1.ObjectRef - 40, // 27: gitops_core.v1.GetSessionLogsResponse.logs:type_name -> gitops_core.v1.LogEntry - 55, // 28: gitops_core.v1.IsCRDAvailableResponse.clusters:type_name -> gitops_core.v1.IsCRDAvailableResponse.ClustersEntry - 10, // 29: gitops_core.v1.ListPoliciesRequest.pagination:type_name -> gitops_core.v1.Pagination - 48, // 30: gitops_core.v1.ListPoliciesResponse.policies:type_name -> gitops_core.v1.PolicyObj - 11, // 31: gitops_core.v1.ListPoliciesResponse.errors:type_name -> gitops_core.v1.ListError - 48, // 32: gitops_core.v1.GetPolicyResponse.policy:type_name -> gitops_core.v1.PolicyObj - 49, // 33: gitops_core.v1.PolicyObj.standards:type_name -> gitops_core.v1.PolicyStandard - 50, // 34: gitops_core.v1.PolicyObj.parameters:type_name -> gitops_core.v1.PolicyParam - 51, // 35: gitops_core.v1.PolicyObj.targets:type_name -> gitops_core.v1.PolicyTargets - 58, // 36: gitops_core.v1.PolicyParam.value:type_name -> google.protobuf.Any - 52, // 37: gitops_core.v1.PolicyTargets.labels:type_name -> gitops_core.v1.PolicyTargetLabel - 56, // 38: gitops_core.v1.PolicyTargetLabel.values:type_name -> gitops_core.v1.PolicyTargetLabel.ValuesEntry - 16, // 39: gitops_core.v1.Core.GetObject:input_type -> gitops_core.v1.GetObjectRequest - 18, // 40: gitops_core.v1.Core.ListObjects:input_type -> gitops_core.v1.ListObjectsRequest - 12, // 41: gitops_core.v1.Core.ListFluxRuntimeObjects:input_type -> gitops_core.v1.ListFluxRuntimeObjectsRequest - 14, // 42: gitops_core.v1.Core.ListFluxCrds:input_type -> gitops_core.v1.ListFluxCrdsRequest - 21, // 43: gitops_core.v1.Core.GetReconciledObjects:input_type -> gitops_core.v1.GetReconciledObjectsRequest - 23, // 44: gitops_core.v1.Core.GetChildObjects:input_type -> gitops_core.v1.GetChildObjectsRequest - 25, // 45: gitops_core.v1.Core.GetFluxNamespace:input_type -> gitops_core.v1.GetFluxNamespaceRequest - 27, // 46: gitops_core.v1.Core.ListNamespaces:input_type -> gitops_core.v1.ListNamespacesRequest - 29, // 47: gitops_core.v1.Core.ListEvents:input_type -> gitops_core.v1.ListEventsRequest - 31, // 48: gitops_core.v1.Core.SyncFluxObject:input_type -> gitops_core.v1.SyncFluxObjectRequest - 33, // 49: gitops_core.v1.Core.GetVersion:input_type -> gitops_core.v1.GetVersionRequest - 35, // 50: gitops_core.v1.Core.GetFeatureFlags:input_type -> gitops_core.v1.GetFeatureFlagsRequest - 37, // 51: gitops_core.v1.Core.ToggleSuspendResource:input_type -> gitops_core.v1.ToggleSuspendResourceRequest - 39, // 52: gitops_core.v1.Core.GetSessionLogs:input_type -> gitops_core.v1.GetSessionLogsRequest - 42, // 53: gitops_core.v1.Core.IsCRDAvailable:input_type -> gitops_core.v1.IsCRDAvailableRequest - 0, // 54: gitops_core.v1.Core.GetInventory:input_type -> gitops_core.v1.GetInventoryRequest - 44, // 55: gitops_core.v1.Core.ListPolicies:input_type -> gitops_core.v1.ListPoliciesRequest - 46, // 56: gitops_core.v1.Core.GetPolicy:input_type -> gitops_core.v1.GetPolicyRequest - 3, // 57: gitops_core.v1.Core.ListPolicyValidations:input_type -> gitops_core.v1.ListPolicyValidationsRequest - 5, // 58: gitops_core.v1.Core.GetPolicyValidation:input_type -> gitops_core.v1.GetPolicyValidationRequest - 17, // 59: gitops_core.v1.Core.GetObject:output_type -> gitops_core.v1.GetObjectResponse - 20, // 60: gitops_core.v1.Core.ListObjects:output_type -> gitops_core.v1.ListObjectsResponse - 13, // 61: gitops_core.v1.Core.ListFluxRuntimeObjects:output_type -> gitops_core.v1.ListFluxRuntimeObjectsResponse - 15, // 62: gitops_core.v1.Core.ListFluxCrds:output_type -> gitops_core.v1.ListFluxCrdsResponse - 22, // 63: gitops_core.v1.Core.GetReconciledObjects:output_type -> gitops_core.v1.GetReconciledObjectsResponse - 24, // 64: gitops_core.v1.Core.GetChildObjects:output_type -> gitops_core.v1.GetChildObjectsResponse - 26, // 65: gitops_core.v1.Core.GetFluxNamespace:output_type -> gitops_core.v1.GetFluxNamespaceResponse - 28, // 66: gitops_core.v1.Core.ListNamespaces:output_type -> gitops_core.v1.ListNamespacesResponse - 30, // 67: gitops_core.v1.Core.ListEvents:output_type -> gitops_core.v1.ListEventsResponse - 32, // 68: gitops_core.v1.Core.SyncFluxObject:output_type -> gitops_core.v1.SyncFluxObjectResponse - 34, // 69: gitops_core.v1.Core.GetVersion:output_type -> gitops_core.v1.GetVersionResponse - 36, // 70: gitops_core.v1.Core.GetFeatureFlags:output_type -> gitops_core.v1.GetFeatureFlagsResponse - 38, // 71: gitops_core.v1.Core.ToggleSuspendResource:output_type -> gitops_core.v1.ToggleSuspendResourceResponse - 41, // 72: gitops_core.v1.Core.GetSessionLogs:output_type -> gitops_core.v1.GetSessionLogsResponse - 43, // 73: gitops_core.v1.Core.IsCRDAvailable:output_type -> gitops_core.v1.IsCRDAvailableResponse - 1, // 74: gitops_core.v1.Core.GetInventory:output_type -> gitops_core.v1.GetInventoryResponse - 45, // 75: gitops_core.v1.Core.ListPolicies:output_type -> gitops_core.v1.ListPoliciesResponse - 47, // 76: gitops_core.v1.Core.GetPolicy:output_type -> gitops_core.v1.GetPolicyResponse - 4, // 77: gitops_core.v1.Core.ListPolicyValidations:output_type -> gitops_core.v1.ListPolicyValidationsResponse - 6, // 78: gitops_core.v1.Core.GetPolicyValidation:output_type -> gitops_core.v1.GetPolicyValidationResponse - 59, // [59:79] is the sub-list for method output_type - 39, // [39:59] is the sub-list for method input_type - 39, // [39:39] is the sub-list for extension type_name - 39, // [39:39] is the sub-list for extension extendee - 0, // [0:39] is the sub-list for field type_name + 63, // 10: gitops_core.v1.ListRuntimeObjectsResponse.deployments:type_name -> gitops_core.v1.Deployment + 11, // 11: gitops_core.v1.ListRuntimeObjectsResponse.errors:type_name -> gitops_core.v1.ListError + 64, // 12: gitops_core.v1.ListFluxCrdsResponse.crds:type_name -> gitops_core.v1.Crd + 11, // 13: gitops_core.v1.ListFluxCrdsResponse.errors:type_name -> gitops_core.v1.ListError + 64, // 14: gitops_core.v1.ListRuntimeCrdsResponse.crds:type_name -> gitops_core.v1.Crd + 11, // 15: gitops_core.v1.ListRuntimeCrdsResponse.errors:type_name -> gitops_core.v1.ListError + 65, // 16: gitops_core.v1.GetObjectResponse.object:type_name -> gitops_core.v1.Object + 57, // 17: gitops_core.v1.ListObjectsRequest.labels:type_name -> gitops_core.v1.ListObjectsRequest.LabelsEntry + 65, // 18: gitops_core.v1.ListObjectsResponse.objects:type_name -> gitops_core.v1.Object + 11, // 19: gitops_core.v1.ListObjectsResponse.errors:type_name -> gitops_core.v1.ListError + 23, // 20: gitops_core.v1.ListObjectsResponse.searchedNamespaces:type_name -> gitops_core.v1.ClusterNamespaceList + 66, // 21: gitops_core.v1.GetReconciledObjectsRequest.kinds:type_name -> gitops_core.v1.GroupVersionKind + 65, // 22: gitops_core.v1.GetReconciledObjectsResponse.objects:type_name -> gitops_core.v1.Object + 66, // 23: gitops_core.v1.GetChildObjectsRequest.groupVersionKind:type_name -> gitops_core.v1.GroupVersionKind + 65, // 24: gitops_core.v1.GetChildObjectsResponse.objects:type_name -> gitops_core.v1.Object + 67, // 25: gitops_core.v1.ListNamespacesResponse.namespaces:type_name -> gitops_core.v1.Namespace + 68, // 26: gitops_core.v1.ListEventsRequest.involvedObject:type_name -> gitops_core.v1.ObjectRef + 69, // 27: gitops_core.v1.ListEventsResponse.events:type_name -> gitops_core.v1.Event + 68, // 28: gitops_core.v1.SyncFluxObjectRequest.objects:type_name -> gitops_core.v1.ObjectRef + 58, // 29: gitops_core.v1.GetFeatureFlagsResponse.flags:type_name -> gitops_core.v1.GetFeatureFlagsResponse.FlagsEntry + 68, // 30: gitops_core.v1.ToggleSuspendResourceRequest.objects:type_name -> gitops_core.v1.ObjectRef + 44, // 31: gitops_core.v1.GetSessionLogsResponse.logs:type_name -> gitops_core.v1.LogEntry + 59, // 32: gitops_core.v1.IsCRDAvailableResponse.clusters:type_name -> gitops_core.v1.IsCRDAvailableResponse.ClustersEntry + 10, // 33: gitops_core.v1.ListPoliciesRequest.pagination:type_name -> gitops_core.v1.Pagination + 52, // 34: gitops_core.v1.ListPoliciesResponse.policies:type_name -> gitops_core.v1.PolicyObj + 11, // 35: gitops_core.v1.ListPoliciesResponse.errors:type_name -> gitops_core.v1.ListError + 52, // 36: gitops_core.v1.GetPolicyResponse.policy:type_name -> gitops_core.v1.PolicyObj + 53, // 37: gitops_core.v1.PolicyObj.standards:type_name -> gitops_core.v1.PolicyStandard + 54, // 38: gitops_core.v1.PolicyObj.parameters:type_name -> gitops_core.v1.PolicyParam + 55, // 39: gitops_core.v1.PolicyObj.targets:type_name -> gitops_core.v1.PolicyTargets + 62, // 40: gitops_core.v1.PolicyParam.value:type_name -> google.protobuf.Any + 56, // 41: gitops_core.v1.PolicyTargets.labels:type_name -> gitops_core.v1.PolicyTargetLabel + 60, // 42: gitops_core.v1.PolicyTargetLabel.values:type_name -> gitops_core.v1.PolicyTargetLabel.ValuesEntry + 20, // 43: gitops_core.v1.Core.GetObject:input_type -> gitops_core.v1.GetObjectRequest + 22, // 44: gitops_core.v1.Core.ListObjects:input_type -> gitops_core.v1.ListObjectsRequest + 12, // 45: gitops_core.v1.Core.ListFluxRuntimeObjects:input_type -> gitops_core.v1.ListFluxRuntimeObjectsRequest + 16, // 46: gitops_core.v1.Core.ListFluxCrds:input_type -> gitops_core.v1.ListFluxCrdsRequest + 14, // 47: gitops_core.v1.Core.ListRuntimeObjects:input_type -> gitops_core.v1.ListRuntimeObjectsRequest + 18, // 48: gitops_core.v1.Core.ListRuntimeCrds:input_type -> gitops_core.v1.ListRuntimeCrdsRequest + 25, // 49: gitops_core.v1.Core.GetReconciledObjects:input_type -> gitops_core.v1.GetReconciledObjectsRequest + 27, // 50: gitops_core.v1.Core.GetChildObjects:input_type -> gitops_core.v1.GetChildObjectsRequest + 29, // 51: gitops_core.v1.Core.GetFluxNamespace:input_type -> gitops_core.v1.GetFluxNamespaceRequest + 31, // 52: gitops_core.v1.Core.ListNamespaces:input_type -> gitops_core.v1.ListNamespacesRequest + 33, // 53: gitops_core.v1.Core.ListEvents:input_type -> gitops_core.v1.ListEventsRequest + 35, // 54: gitops_core.v1.Core.SyncFluxObject:input_type -> gitops_core.v1.SyncFluxObjectRequest + 37, // 55: gitops_core.v1.Core.GetVersion:input_type -> gitops_core.v1.GetVersionRequest + 39, // 56: gitops_core.v1.Core.GetFeatureFlags:input_type -> gitops_core.v1.GetFeatureFlagsRequest + 41, // 57: gitops_core.v1.Core.ToggleSuspendResource:input_type -> gitops_core.v1.ToggleSuspendResourceRequest + 43, // 58: gitops_core.v1.Core.GetSessionLogs:input_type -> gitops_core.v1.GetSessionLogsRequest + 46, // 59: gitops_core.v1.Core.IsCRDAvailable:input_type -> gitops_core.v1.IsCRDAvailableRequest + 0, // 60: gitops_core.v1.Core.GetInventory:input_type -> gitops_core.v1.GetInventoryRequest + 48, // 61: gitops_core.v1.Core.ListPolicies:input_type -> gitops_core.v1.ListPoliciesRequest + 50, // 62: gitops_core.v1.Core.GetPolicy:input_type -> gitops_core.v1.GetPolicyRequest + 3, // 63: gitops_core.v1.Core.ListPolicyValidations:input_type -> gitops_core.v1.ListPolicyValidationsRequest + 5, // 64: gitops_core.v1.Core.GetPolicyValidation:input_type -> gitops_core.v1.GetPolicyValidationRequest + 21, // 65: gitops_core.v1.Core.GetObject:output_type -> gitops_core.v1.GetObjectResponse + 24, // 66: gitops_core.v1.Core.ListObjects:output_type -> gitops_core.v1.ListObjectsResponse + 13, // 67: gitops_core.v1.Core.ListFluxRuntimeObjects:output_type -> gitops_core.v1.ListFluxRuntimeObjectsResponse + 17, // 68: gitops_core.v1.Core.ListFluxCrds:output_type -> gitops_core.v1.ListFluxCrdsResponse + 15, // 69: gitops_core.v1.Core.ListRuntimeObjects:output_type -> gitops_core.v1.ListRuntimeObjectsResponse + 19, // 70: gitops_core.v1.Core.ListRuntimeCrds:output_type -> gitops_core.v1.ListRuntimeCrdsResponse + 26, // 71: gitops_core.v1.Core.GetReconciledObjects:output_type -> gitops_core.v1.GetReconciledObjectsResponse + 28, // 72: gitops_core.v1.Core.GetChildObjects:output_type -> gitops_core.v1.GetChildObjectsResponse + 30, // 73: gitops_core.v1.Core.GetFluxNamespace:output_type -> gitops_core.v1.GetFluxNamespaceResponse + 32, // 74: gitops_core.v1.Core.ListNamespaces:output_type -> gitops_core.v1.ListNamespacesResponse + 34, // 75: gitops_core.v1.Core.ListEvents:output_type -> gitops_core.v1.ListEventsResponse + 36, // 76: gitops_core.v1.Core.SyncFluxObject:output_type -> gitops_core.v1.SyncFluxObjectResponse + 38, // 77: gitops_core.v1.Core.GetVersion:output_type -> gitops_core.v1.GetVersionResponse + 40, // 78: gitops_core.v1.Core.GetFeatureFlags:output_type -> gitops_core.v1.GetFeatureFlagsResponse + 42, // 79: gitops_core.v1.Core.ToggleSuspendResource:output_type -> gitops_core.v1.ToggleSuspendResourceResponse + 45, // 80: gitops_core.v1.Core.GetSessionLogs:output_type -> gitops_core.v1.GetSessionLogsResponse + 47, // 81: gitops_core.v1.Core.IsCRDAvailable:output_type -> gitops_core.v1.IsCRDAvailableResponse + 1, // 82: gitops_core.v1.Core.GetInventory:output_type -> gitops_core.v1.GetInventoryResponse + 49, // 83: gitops_core.v1.Core.ListPolicies:output_type -> gitops_core.v1.ListPoliciesResponse + 51, // 84: gitops_core.v1.Core.GetPolicy:output_type -> gitops_core.v1.GetPolicyResponse + 4, // 85: gitops_core.v1.Core.ListPolicyValidations:output_type -> gitops_core.v1.ListPolicyValidationsResponse + 6, // 86: gitops_core.v1.Core.GetPolicyValidation:output_type -> gitops_core.v1.GetPolicyValidationResponse + 65, // [65:87] is the sub-list for method output_type + 43, // [43:65] is the sub-list for method input_type + 43, // [43:43] is the sub-list for extension type_name + 43, // [43:43] is the sub-list for extension extendee + 0, // [0:43] is the sub-list for field type_name } func init() { file_api_core_core_proto_init() } @@ -4250,7 +4516,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListFluxCrdsRequest); i { + switch v := v.(*ListRuntimeObjectsRequest); i { case 0: return &v.state case 1: @@ -4262,7 +4528,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListFluxCrdsResponse); i { + switch v := v.(*ListRuntimeObjectsResponse); i { case 0: return &v.state case 1: @@ -4274,7 +4540,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetObjectRequest); i { + switch v := v.(*ListFluxCrdsRequest); i { case 0: return &v.state case 1: @@ -4286,7 +4552,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetObjectResponse); i { + switch v := v.(*ListFluxCrdsResponse); i { case 0: return &v.state case 1: @@ -4298,7 +4564,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListObjectsRequest); i { + switch v := v.(*ListRuntimeCrdsRequest); i { case 0: return &v.state case 1: @@ -4310,7 +4576,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ClusterNamespaceList); i { + switch v := v.(*ListRuntimeCrdsResponse); i { case 0: return &v.state case 1: @@ -4322,7 +4588,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListObjectsResponse); i { + switch v := v.(*GetObjectRequest); i { case 0: return &v.state case 1: @@ -4334,7 +4600,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetReconciledObjectsRequest); i { + switch v := v.(*GetObjectResponse); i { case 0: return &v.state case 1: @@ -4346,7 +4612,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetReconciledObjectsResponse); i { + switch v := v.(*ListObjectsRequest); i { case 0: return &v.state case 1: @@ -4358,7 +4624,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetChildObjectsRequest); i { + switch v := v.(*ClusterNamespaceList); i { case 0: return &v.state case 1: @@ -4370,7 +4636,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetChildObjectsResponse); i { + switch v := v.(*ListObjectsResponse); i { case 0: return &v.state case 1: @@ -4382,7 +4648,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetFluxNamespaceRequest); i { + switch v := v.(*GetReconciledObjectsRequest); i { case 0: return &v.state case 1: @@ -4394,7 +4660,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetFluxNamespaceResponse); i { + switch v := v.(*GetReconciledObjectsResponse); i { case 0: return &v.state case 1: @@ -4406,7 +4672,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListNamespacesRequest); i { + switch v := v.(*GetChildObjectsRequest); i { case 0: return &v.state case 1: @@ -4418,7 +4684,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListNamespacesResponse); i { + switch v := v.(*GetChildObjectsResponse); i { case 0: return &v.state case 1: @@ -4430,7 +4696,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListEventsRequest); i { + switch v := v.(*GetFluxNamespaceRequest); i { case 0: return &v.state case 1: @@ -4442,7 +4708,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListEventsResponse); i { + switch v := v.(*GetFluxNamespaceResponse); i { case 0: return &v.state case 1: @@ -4454,7 +4720,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SyncFluxObjectRequest); i { + switch v := v.(*ListNamespacesRequest); i { case 0: return &v.state case 1: @@ -4466,7 +4732,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SyncFluxObjectResponse); i { + switch v := v.(*ListNamespacesResponse); i { case 0: return &v.state case 1: @@ -4478,7 +4744,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetVersionRequest); i { + switch v := v.(*ListEventsRequest); i { case 0: return &v.state case 1: @@ -4490,7 +4756,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetVersionResponse); i { + switch v := v.(*ListEventsResponse); i { case 0: return &v.state case 1: @@ -4502,7 +4768,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetFeatureFlagsRequest); i { + switch v := v.(*SyncFluxObjectRequest); i { case 0: return &v.state case 1: @@ -4514,7 +4780,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetFeatureFlagsResponse); i { + switch v := v.(*SyncFluxObjectResponse); i { case 0: return &v.state case 1: @@ -4526,7 +4792,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ToggleSuspendResourceRequest); i { + switch v := v.(*GetVersionRequest); i { case 0: return &v.state case 1: @@ -4538,7 +4804,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ToggleSuspendResourceResponse); i { + switch v := v.(*GetVersionResponse); i { case 0: return &v.state case 1: @@ -4550,7 +4816,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSessionLogsRequest); i { + switch v := v.(*GetFeatureFlagsRequest); i { case 0: return &v.state case 1: @@ -4562,7 +4828,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LogEntry); i { + switch v := v.(*GetFeatureFlagsResponse); i { case 0: return &v.state case 1: @@ -4574,7 +4840,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSessionLogsResponse); i { + switch v := v.(*ToggleSuspendResourceRequest); i { case 0: return &v.state case 1: @@ -4586,7 +4852,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IsCRDAvailableRequest); i { + switch v := v.(*ToggleSuspendResourceResponse); i { case 0: return &v.state case 1: @@ -4598,7 +4864,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*IsCRDAvailableResponse); i { + switch v := v.(*GetSessionLogsRequest); i { case 0: return &v.state case 1: @@ -4610,7 +4876,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListPoliciesRequest); i { + switch v := v.(*LogEntry); i { case 0: return &v.state case 1: @@ -4622,7 +4888,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListPoliciesResponse); i { + switch v := v.(*GetSessionLogsResponse); i { case 0: return &v.state case 1: @@ -4634,7 +4900,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPolicyRequest); i { + switch v := v.(*IsCRDAvailableRequest); i { case 0: return &v.state case 1: @@ -4646,7 +4912,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetPolicyResponse); i { + switch v := v.(*IsCRDAvailableResponse); i { case 0: return &v.state case 1: @@ -4658,7 +4924,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyObj); i { + switch v := v.(*ListPoliciesRequest); i { case 0: return &v.state case 1: @@ -4670,7 +4936,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyStandard); i { + switch v := v.(*ListPoliciesResponse); i { case 0: return &v.state case 1: @@ -4682,7 +4948,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyParam); i { + switch v := v.(*GetPolicyRequest); i { case 0: return &v.state case 1: @@ -4694,7 +4960,7 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PolicyTargets); i { + switch v := v.(*GetPolicyResponse); i { case 0: return &v.state case 1: @@ -4706,6 +4972,54 @@ func file_api_core_core_proto_init() { } } file_api_core_core_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PolicyObj); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_core_core_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PolicyStandard); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_core_core_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PolicyParam); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_core_core_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PolicyTargets); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_core_core_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PolicyTargetLabel); i { case 0: return &v.state @@ -4724,7 +5038,7 @@ func file_api_core_core_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_core_core_proto_rawDesc, NumEnums: 0, - NumMessages: 57, + NumMessages: 61, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/api/core/core.pb.gw.go b/pkg/api/core/core.pb.gw.go index c9dfdb2961..0175e38668 100644 --- a/pkg/api/core/core.pb.gw.go +++ b/pkg/api/core/core.pb.gw.go @@ -207,6 +207,78 @@ func local_request_Core_ListFluxCrds_0(ctx context.Context, marshaler runtime.Ma } +var ( + filter_Core_ListRuntimeObjects_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Core_ListRuntimeObjects_0(ctx context.Context, marshaler runtime.Marshaler, client CoreClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListRuntimeObjectsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Core_ListRuntimeObjects_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ListRuntimeObjects(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Core_ListRuntimeObjects_0(ctx context.Context, marshaler runtime.Marshaler, server CoreServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListRuntimeObjectsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Core_ListRuntimeObjects_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ListRuntimeObjects(ctx, &protoReq) + return msg, metadata, err + +} + +var ( + filter_Core_ListRuntimeCrds_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Core_ListRuntimeCrds_0(ctx context.Context, marshaler runtime.Marshaler, client CoreClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListRuntimeCrdsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Core_ListRuntimeCrds_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ListRuntimeCrds(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Core_ListRuntimeCrds_0(ctx context.Context, marshaler runtime.Marshaler, server CoreServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListRuntimeCrdsRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Core_ListRuntimeCrds_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ListRuntimeCrds(ctx, &protoReq) + return msg, metadata, err + +} + func request_Core_GetReconciledObjects_0(ctx context.Context, marshaler runtime.Marshaler, client CoreClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq GetReconciledObjectsRequest var metadata runtime.ServerMetadata @@ -881,6 +953,52 @@ func RegisterCoreHandlerServer(ctx context.Context, mux *runtime.ServeMux, serve }) + mux.Handle("GET", pattern_Core_ListRuntimeObjects_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gitops_core.v1.Core/ListRuntimeObjects", runtime.WithHTTPPathPattern("/v1/runtime_objects")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Core_ListRuntimeObjects_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Core_ListRuntimeObjects_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Core_ListRuntimeCrds_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/gitops_core.v1.Core/ListRuntimeCrds", runtime.WithHTTPPathPattern("/v1/runtime_crds")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Core_ListRuntimeCrds_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Core_ListRuntimeCrds_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_Core_GetReconciledObjects_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1370,6 +1488,46 @@ func RegisterCoreHandlerClient(ctx context.Context, mux *runtime.ServeMux, clien }) + mux.Handle("GET", pattern_Core_ListRuntimeObjects_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gitops_core.v1.Core/ListRuntimeObjects", runtime.WithHTTPPathPattern("/v1/runtime_objects")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Core_ListRuntimeObjects_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Core_ListRuntimeObjects_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_Core_ListRuntimeCrds_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req, "/gitops_core.v1.Core/ListRuntimeCrds", runtime.WithHTTPPathPattern("/v1/runtime_crds")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Core_ListRuntimeCrds_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Core_ListRuntimeCrds_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_Core_GetReconciledObjects_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -1702,6 +1860,10 @@ var ( pattern_Core_ListFluxCrds_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "flux_crds"}, "")) + pattern_Core_ListRuntimeObjects_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "runtime_objects"}, "")) + + pattern_Core_ListRuntimeCrds_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "runtime_crds"}, "")) + pattern_Core_GetReconciledObjects_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "reconciled_objects"}, "")) pattern_Core_GetChildObjects_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "child_objects"}, "")) @@ -1744,6 +1906,10 @@ var ( forward_Core_ListFluxCrds_0 = runtime.ForwardResponseMessage + forward_Core_ListRuntimeObjects_0 = runtime.ForwardResponseMessage + + forward_Core_ListRuntimeCrds_0 = runtime.ForwardResponseMessage + forward_Core_GetReconciledObjects_0 = runtime.ForwardResponseMessage forward_Core_GetChildObjects_0 = runtime.ForwardResponseMessage diff --git a/pkg/api/core/core_grpc.pb.go b/pkg/api/core/core_grpc.pb.go index d96c1bde8f..882b563705 100644 --- a/pkg/api/core/core_grpc.pb.go +++ b/pkg/api/core/core_grpc.pb.go @@ -25,6 +25,10 @@ type CoreClient interface { // ListFluxRuntimeObjects lists the flux runtime deployments from a cluster. ListFluxRuntimeObjects(ctx context.Context, in *ListFluxRuntimeObjectsRequest, opts ...grpc.CallOption) (*ListFluxRuntimeObjectsResponse, error) ListFluxCrds(ctx context.Context, in *ListFluxCrdsRequest, opts ...grpc.CallOption) (*ListFluxCrdsResponse, error) + // ListRuntimeObjects lists Weave GitOps runtime components from a clusters. Weave GitOps runtime is composed of Flux runtime + // but also the other components in the ecosystem like TF-controller or Policy Agent. + ListRuntimeObjects(ctx context.Context, in *ListRuntimeObjectsRequest, opts ...grpc.CallOption) (*ListRuntimeObjectsResponse, error) + ListRuntimeCrds(ctx context.Context, in *ListRuntimeCrdsRequest, opts ...grpc.CallOption) (*ListRuntimeCrdsResponse, error) // GetReconciledObjects returns a list of objects that were created // as a result of reconciling a Flux automation. // This list is derived by looking at the Kustomization or HelmRelease @@ -110,6 +114,24 @@ func (c *coreClient) ListFluxCrds(ctx context.Context, in *ListFluxCrdsRequest, return out, nil } +func (c *coreClient) ListRuntimeObjects(ctx context.Context, in *ListRuntimeObjectsRequest, opts ...grpc.CallOption) (*ListRuntimeObjectsResponse, error) { + out := new(ListRuntimeObjectsResponse) + err := c.cc.Invoke(ctx, "/gitops_core.v1.Core/ListRuntimeObjects", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *coreClient) ListRuntimeCrds(ctx context.Context, in *ListRuntimeCrdsRequest, opts ...grpc.CallOption) (*ListRuntimeCrdsResponse, error) { + out := new(ListRuntimeCrdsResponse) + err := c.cc.Invoke(ctx, "/gitops_core.v1.Core/ListRuntimeCrds", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *coreClient) GetReconciledObjects(ctx context.Context, in *GetReconciledObjectsRequest, opts ...grpc.CallOption) (*GetReconciledObjectsResponse, error) { out := new(GetReconciledObjectsResponse) err := c.cc.Invoke(ctx, "/gitops_core.v1.Core/GetReconciledObjects", in, out, opts...) @@ -265,6 +287,10 @@ type CoreServer interface { // ListFluxRuntimeObjects lists the flux runtime deployments from a cluster. ListFluxRuntimeObjects(context.Context, *ListFluxRuntimeObjectsRequest) (*ListFluxRuntimeObjectsResponse, error) ListFluxCrds(context.Context, *ListFluxCrdsRequest) (*ListFluxCrdsResponse, error) + // ListRuntimeObjects lists Weave GitOps runtime components from a clusters. Weave GitOps runtime is composed of Flux runtime + // but also the other components in the ecosystem like TF-controller or Policy Agent. + ListRuntimeObjects(context.Context, *ListRuntimeObjectsRequest) (*ListRuntimeObjectsResponse, error) + ListRuntimeCrds(context.Context, *ListRuntimeCrdsRequest) (*ListRuntimeCrdsResponse, error) // GetReconciledObjects returns a list of objects that were created // as a result of reconciling a Flux automation. // This list is derived by looking at the Kustomization or HelmRelease @@ -323,6 +349,12 @@ func (UnimplementedCoreServer) ListFluxRuntimeObjects(context.Context, *ListFlux func (UnimplementedCoreServer) ListFluxCrds(context.Context, *ListFluxCrdsRequest) (*ListFluxCrdsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ListFluxCrds not implemented") } +func (UnimplementedCoreServer) ListRuntimeObjects(context.Context, *ListRuntimeObjectsRequest) (*ListRuntimeObjectsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListRuntimeObjects not implemented") +} +func (UnimplementedCoreServer) ListRuntimeCrds(context.Context, *ListRuntimeCrdsRequest) (*ListRuntimeCrdsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListRuntimeCrds not implemented") +} func (UnimplementedCoreServer) GetReconciledObjects(context.Context, *GetReconciledObjectsRequest) (*GetReconciledObjectsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetReconciledObjects not implemented") } @@ -456,6 +488,42 @@ func _Core_ListFluxCrds_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } +func _Core_ListRuntimeObjects_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListRuntimeObjectsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CoreServer).ListRuntimeObjects(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gitops_core.v1.Core/ListRuntimeObjects", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CoreServer).ListRuntimeObjects(ctx, req.(*ListRuntimeObjectsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Core_ListRuntimeCrds_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListRuntimeCrdsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CoreServer).ListRuntimeCrds(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gitops_core.v1.Core/ListRuntimeCrds", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CoreServer).ListRuntimeCrds(ctx, req.(*ListRuntimeCrdsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Core_GetReconciledObjects_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetReconciledObjectsRequest) if err := dec(in); err != nil { @@ -767,6 +835,14 @@ var Core_ServiceDesc = grpc.ServiceDesc{ MethodName: "ListFluxCrds", Handler: _Core_ListFluxCrds_Handler, }, + { + MethodName: "ListRuntimeObjects", + Handler: _Core_ListRuntimeObjects_Handler, + }, + { + MethodName: "ListRuntimeCrds", + Handler: _Core_ListRuntimeCrds_Handler, + }, { MethodName: "GetReconciledObjects", Handler: _Core_GetReconciledObjects_Handler, diff --git a/ui/lib/api/core/core.pb.ts b/ui/lib/api/core/core.pb.ts index 0c969efcc0..ad12eeb7e9 100644 --- a/ui/lib/api/core/core.pb.ts +++ b/ui/lib/api/core/core.pb.ts @@ -103,6 +103,16 @@ export type ListFluxRuntimeObjectsResponse = { errors?: ListError[] } +export type ListRuntimeObjectsRequest = { + namespace?: string + clusterName?: string +} + +export type ListRuntimeObjectsResponse = { + deployments?: Gitops_coreV1Types.Deployment[] + errors?: ListError[] +} + export type ListFluxCrdsRequest = { clusterName?: string } @@ -112,6 +122,15 @@ export type ListFluxCrdsResponse = { errors?: ListError[] } +export type ListRuntimeCrdsRequest = { + clusterName?: string +} + +export type ListRuntimeCrdsResponse = { + crds?: Gitops_coreV1Types.Crd[] + errors?: ListError[] +} + export type GetObjectRequest = { name?: string namespace?: string @@ -328,6 +347,12 @@ export class Core { static ListFluxCrds(req: ListFluxCrdsRequest, initReq?: fm.InitReq): Promise { return fm.fetchReq(`/v1/flux_crds?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"}) } + static ListRuntimeObjects(req: ListRuntimeObjectsRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/runtime_objects?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"}) + } + static ListRuntimeCrds(req: ListRuntimeCrdsRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/runtime_crds?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"}) + } static GetReconciledObjects(req: GetReconciledObjectsRequest, initReq?: fm.InitReq): Promise { return fm.fetchReq(`/v1/reconciled_objects`, {...initReq, method: "POST", body: JSON.stringify(req)}) } From d62e8fcead9a9c052be656a00b227237b1a480e7 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Mon, 18 Dec 2023 16:22:16 +0100 Subject: [PATCH 16/43] implemented runtime api endpoint Signed-off-by: Eneko Fernandez --- core/server/fluxruntime.go | 50 ++++++++++----- core/server/fluxruntime_internal_test.go | 52 ---------------- core/server/fluxruntime_test.go | 77 +++++++++++++++++++++++- 3 files changed, 110 insertions(+), 69 deletions(-) delete mode 100644 core/server/fluxruntime_internal_test.go diff --git a/core/server/fluxruntime.go b/core/server/fluxruntime.go index c594295bee..a631884b35 100644 --- a/core/server/fluxruntime.go +++ b/core/server/fluxruntime.go @@ -13,7 +13,6 @@ import ( "github.com/weaveworks/weave-gitops/core/logger" coretypes "github.com/weaveworks/weave-gitops/core/server/types" pb "github.com/weaveworks/weave-gitops/pkg/api/core" - "github.com/weaveworks/weave-gitops/pkg/featureflags" "github.com/weaveworks/weave-gitops/pkg/server/auth" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -67,6 +66,20 @@ func lookupEnv(envVar, fallback string) string { func (cs *coreServer) ListFluxRuntimeObjects(ctx context.Context, msg *pb.ListFluxRuntimeObjectsRequest) (*pb.ListFluxRuntimeObjectsResponse, error) { respErrors := []*pb.ListError{} + respErrors, results := listRuntimeObjectsByLabels(ctx, cs, respErrors, FluxRuntimeLabels) + + return &pb.ListFluxRuntimeObjectsResponse{Deployments: results, Errors: respErrors}, nil +} + +func (cs *coreServer) ListRuntimeObjects(ctx context.Context, msg *pb.ListRuntimeObjectsRequest) (*pb.ListRuntimeObjectsResponse, error) { + respErrors := []*pb.ListError{} + + respErrors, results := listRuntimeObjectsByLabels(ctx, cs, respErrors, WeaveGitopsRuntimeLabels) + + return &pb.ListRuntimeObjectsResponse{Deployments: results, Errors: respErrors}, nil +} + +func listRuntimeObjectsByLabels(ctx context.Context, cs *coreServer, respErrors []*pb.ListError, labels []string) ([]*pb.ListError, []*pb.Deployment) { clustersClient, err := cs.clustersManager.GetImpersonatedClient(ctx, auth.Principal(ctx)) if err != nil { if merr, ok := err.(*multierror.Error); ok { @@ -80,7 +93,7 @@ func (cs *coreServer) ListFluxRuntimeObjects(ctx context.Context, msg *pb.ListFl var results []*pb.Deployment - for _, runtimeLabel := range getRuntimeLabels() { + for _, runtimeLabel := range labels { for clusterName, nss := range cs.clustersManager.GetClustersNamespaces() { fluxNamespaces := filterFluxNamespace(nss) if len(fluxNamespaces) == 0 { @@ -128,23 +141,31 @@ func (cs *coreServer) ListFluxRuntimeObjects(ctx context.Context, msg *pb.ListFl } } } + return respErrors, results +} + +func (cs *coreServer) ListFluxCrds(ctx context.Context, msg *pb.ListFluxCrdsRequest) (*pb.ListFluxCrdsResponse, error) { + respErrors, results, err2 := listRuntimeCrdsByLabel(ctx, cs, FluxRuntimeLabels) + if err2 != nil { + return nil, err2 + } - return &pb.ListFluxRuntimeObjectsResponse{Deployments: results, Errors: respErrors}, nil + return &pb.ListFluxCrdsResponse{Crds: results, Errors: respErrors}, nil } -// getRuntimeLabels returns the labels that are used to identify the runtime objects based on -// whether the user has enabled `WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME` or not -func getRuntimeLabels() []string { - if featureflags.Get(GitopsRuntimeFeatureFlag) == "true" { - return WeaveGitopsRuntimeLabels +func (cs *coreServer) ListRuntimeCrds(ctx context.Context, msg *pb.ListRuntimeCrdsRequest) (*pb.ListRuntimeCrdsResponse, error) { + respErrors, results, err2 := listRuntimeCrdsByLabel(ctx, cs, WeaveGitopsRuntimeLabels) + if err2 != nil { + return nil, err2 } - return FluxRuntimeLabels + + return &pb.ListRuntimeCrdsResponse{Crds: results, Errors: respErrors}, nil } -func (cs *coreServer) ListFluxCrds(ctx context.Context, msg *pb.ListFluxCrdsRequest) (*pb.ListFluxCrdsResponse, error) { +func listRuntimeCrdsByLabel(ctx context.Context, cs *coreServer, labels []string) ([]*pb.ListError, []*pb.Crd, error) { clustersClient, err := cs.clustersManager.GetImpersonatedClient(ctx, auth.Principal(ctx)) if err != nil { - return nil, fmt.Errorf("error getting impersonating client: %w", err) + return nil, nil, fmt.Errorf("error getting impersonating client: %w", err) } clist := clustersmngr.NewClusteredList(func() client.ObjectList { @@ -153,7 +174,7 @@ func (cs *coreServer) ListFluxCrds(ctx context.Context, msg *pb.ListFluxCrdsRequ respErrors := []*pb.ListError{} - for _, runtimeLabel := range getRuntimeLabels() { + for _, runtimeLabel := range labels { opts := client.MatchingLabels{ coretypes.PartOfLabel: runtimeLabel, } @@ -161,7 +182,7 @@ func (cs *coreServer) ListFluxCrds(ctx context.Context, msg *pb.ListFluxCrdsRequ var errs clustersmngr.ClusteredListError if !errors.As(err, &errs) { - return nil, fmt.Errorf("CRDs clustered list: %w", errs) + return nil, nil, fmt.Errorf("CRDs clustered list: %w", errs) } for _, e := range errs.Errors { @@ -208,8 +229,7 @@ func (cs *coreServer) ListFluxCrds(ctx context.Context, msg *pb.ListFluxCrdsRequ } } } - - return &pb.ListFluxCrdsResponse{Crds: results, Errors: respErrors}, nil + return respErrors, results, nil } func filterFluxNamespace(nss []v1.Namespace) []v1.Namespace { diff --git a/core/server/fluxruntime_internal_test.go b/core/server/fluxruntime_internal_test.go deleted file mode 100644 index 45ed651ad8..0000000000 --- a/core/server/fluxruntime_internal_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package server - -import ( - "os" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/weaveworks/weave-gitops/pkg/featureflags" -) - -func Test_getRuntimeLabels(t *testing.T) { - tests := []struct { - name string - gitopsRuntimeFeatureFlag string - wantRuntimeLabels []string - }{ - { - name: "should return flux if not feature flag exists", - wantRuntimeLabels: FluxRuntimeLabels, - }, - { - name: "should return flux if feature flag exists but empty", - wantRuntimeLabels: FluxRuntimeLabels, - gitopsRuntimeFeatureFlag: "", - }, - { - name: "should return flux if feature flag exists and false", - wantRuntimeLabels: FluxRuntimeLabels, - gitopsRuntimeFeatureFlag: "false", - }, - { - name: "should return weave gitops if feature flag exists and true", - wantRuntimeLabels: WeaveGitopsRuntimeLabels, - gitopsRuntimeFeatureFlag: "true", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.gitopsRuntimeFeatureFlag != "" { - _ = os.Setenv(GitopsRuntimeFeatureFlag, tt.gitopsRuntimeFeatureFlag) - } - defer func() { - _ = os.Unsetenv(GitopsRuntimeFeatureFlag) - }() - featureflags.SetFromEnv(os.Environ()) - gotLabels := getRuntimeLabels() - if diff := cmp.Diff(tt.wantRuntimeLabels, gotLabels); diff != "" { - t.Fatalf("unexpected labels:\n%s", diff) - } - }) - } -} diff --git a/core/server/fluxruntime_test.go b/core/server/fluxruntime_test.go index ae840377ca..80295a14f9 100644 --- a/core/server/fluxruntime_test.go +++ b/core/server/fluxruntime_test.go @@ -387,14 +387,14 @@ func TestListFluxRuntimeObjects(t *testing.T) { coretypes.PartOfLabel: server.Flux, }}}, newDeployment("kustomize-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.Flux}), - newDeployment("weave-gitops-enterprise-mccp-cluster-service", "flux-ns", map[string]string{coretypes.PartOfLabel: server.WeaveGitops}), + newDeployment("policy-agent", "flux-ns", map[string]string{coretypes.PartOfLabel: server.WeaveGitops}), newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), }, "true", func(res *pb.ListFluxRuntimeObjectsResponse) { g.Expect(res.Deployments).To(HaveLen(2), "expected deployments in the flux namespace to be returned") g.Expect(res.Deployments[0].Name).To(Equal("kustomize-controller")) - g.Expect(res.Deployments[1].Name).To(Equal("weave-gitops-enterprise-mccp-cluster-service")) + g.Expect(res.Deployments[1].Name).To(Equal("policy-agent")) }, }, } @@ -420,6 +420,79 @@ func TestListFluxRuntimeObjects(t *testing.T) { } } +func TestListRuntimeObjects(t *testing.T) { + g := NewGomegaWithT(t) + + ctx := context.Background() + + tests := []struct { + description string + objects []runtime.Object + assertions func(*pb.ListRuntimeObjectsResponse) + }{ + { + "no runtime", + []runtime.Object{ + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "ns1"}}, + }, + func(res *pb.ListRuntimeObjectsResponse) { + g.Expect(res.Errors[0].Message).To(Equal(server.ErrFluxNamespaceNotFound.Error())) + g.Expect(res.Errors[0].Namespace).To(BeEmpty()) + g.Expect(res.Errors[0].ClusterName).To(Equal(cluster.DefaultCluster)) + }, + }, + { + "return weave gitops", + []runtime.Object{ + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ + coretypes.PartOfLabel: server.Flux, + }}}, + newDeployment("kustomize-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.Flux}), + newDeployment("policy-agent", "flux-ns", map[string]string{coretypes.PartOfLabel: server.WeaveGitops}), + newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), + }, + func(res *pb.ListRuntimeObjectsResponse) { + g.Expect(res.Deployments).To(HaveLen(2), "expected deployments in the flux namespace to be returned") + g.Expect(res.Deployments[0].Name).To(Equal("kustomize-controller")) + g.Expect(res.Deployments[1].Name).To(Equal("policy-agent")) + }, + }, + { + "use flux-system namespace when no namespace label available", + []runtime.Object{ + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-system"}}, + newDeployment("kustomize-controller", "flux-system", map[string]string{coretypes.PartOfLabel: server.Flux}), + newDeployment("policy-agent", "flux-system", map[string]string{coretypes.PartOfLabel: server.WeaveGitops}), + newDeployment("other-controller-in-flux-ns", "flux-system", map[string]string{}), + }, + func(res *pb.ListRuntimeObjectsResponse) { + g.Expect(res.Deployments).To(HaveLen(2), "expected deployments in the default flux namespace to be returned") + g.Expect(res.Deployments[0].Name).To(Equal("kustomize-controller")) + g.Expect(res.Deployments[1].Name).To(Equal("policy-agent")) + }, + }, + } + for _, tt := range tests { + t.Run(tt.description, func(t *testing.T) { + _ = os.Setenv(server.GitopsRuntimeFeatureFlag, "true") + defer func() { + _ = os.Unsetenv(server.GitopsRuntimeFeatureFlag) + }() + featureflags.SetFromEnv(os.Environ()) + scheme, err := kube.CreateScheme() + g.Expect(err).To(BeNil()) + client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tt.objects...).Build() + cfg := makeServerConfig(client, t, "") + c := makeServer(cfg, t) + + res, err := c.ListRuntimeObjects(ctx, &pb.ListRuntimeObjectsRequest{}) + + g.Expect(err).NotTo(HaveOccurred()) + tt.assertions(res) + }) + } +} + func newDeployment(name, ns string, labels map[string]string) *appsv1.Deployment { return &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ From 45a2582d28cee93a24f4bb45bc870987f1fe9ccd Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Mon, 18 Dec 2023 16:23:53 +0100 Subject: [PATCH 17/43] restore flux runtime endpoint Signed-off-by: Eneko Fernandez --- core/server/fluxruntime_test.go | 49 ++++++++------------------------- 1 file changed, 11 insertions(+), 38 deletions(-) diff --git a/core/server/fluxruntime_test.go b/core/server/fluxruntime_test.go index 80295a14f9..76a8b8a39f 100644 --- a/core/server/fluxruntime_test.go +++ b/core/server/fluxruntime_test.go @@ -335,17 +335,15 @@ func TestListFluxRuntimeObjects(t *testing.T) { ctx := context.Background() tests := []struct { - description string - objects []runtime.Object - gitopsRuntimeFeatureFlag string - assertions func(*pb.ListFluxRuntimeObjectsResponse) + description string + objects []runtime.Object + assertions func(*pb.ListFluxRuntimeObjectsResponse) }{ { "no flux runtime", []runtime.Object{ &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "ns1"}}, }, - "", func(res *pb.ListFluxRuntimeObjectsResponse) { g.Expect(res.Errors[0].Message).To(Equal(server.ErrFluxNamespaceNotFound.Error())) g.Expect(res.Errors[0].Namespace).To(BeEmpty()) @@ -353,20 +351,7 @@ func TestListFluxRuntimeObjects(t *testing.T) { }, }, { - "use flux-system namespace when no namespace label available", - []runtime.Object{ - &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-system"}}, - newDeployment("random-flux-controller", "flux-system", map[string]string{coretypes.PartOfLabel: server.Flux}), - newDeployment("other-controller-in-flux-ns", "flux-system", map[string]string{}), - }, - "", - func(res *pb.ListFluxRuntimeObjectsResponse) { - g.Expect(res.Deployments).To(HaveLen(1), "expected deployments in the default flux namespace to be returned") - g.Expect(res.Deployments[0].Name).To(Equal("random-flux-controller")) - }, - }, - { - "return flux runtime without WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME", + "flux namespace label, with controllers", []runtime.Object{ &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ coretypes.PartOfLabel: server.Flux, @@ -374,39 +359,27 @@ func TestListFluxRuntimeObjects(t *testing.T) { newDeployment("random-flux-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.Flux}), newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), }, - "false", func(res *pb.ListFluxRuntimeObjectsResponse) { g.Expect(res.Deployments).To(HaveLen(1), "expected deployments in the flux namespace to be returned") g.Expect(res.Deployments[0].Name).To(Equal("random-flux-controller")) }, }, { - "return weave gitops runtime with WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME enabled", + "use flux-system namespace when no namespace label available", []runtime.Object{ - &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-ns", Labels: map[string]string{ - coretypes.PartOfLabel: server.Flux, - }}}, - newDeployment("kustomize-controller", "flux-ns", map[string]string{coretypes.PartOfLabel: server.Flux}), - newDeployment("policy-agent", "flux-ns", map[string]string{coretypes.PartOfLabel: server.WeaveGitops}), - newDeployment("other-controller-in-flux-ns", "flux-ns", map[string]string{}), + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "flux-system"}}, + newDeployment("random-flux-controller", "flux-system", map[string]string{coretypes.PartOfLabel: server.Flux}), + newDeployment("other-controller-in-flux-ns", "flux-system", map[string]string{}), }, - "true", func(res *pb.ListFluxRuntimeObjectsResponse) { - g.Expect(res.Deployments).To(HaveLen(2), "expected deployments in the flux namespace to be returned") - g.Expect(res.Deployments[0].Name).To(Equal("kustomize-controller")) - g.Expect(res.Deployments[1].Name).To(Equal("policy-agent")) + g.Expect(res.Deployments).To(HaveLen(1), "expected deployments in the default flux namespace to be returned") + g.Expect(res.Deployments[0].Name).To(Equal("random-flux-controller")) }, }, } + for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { - if tt.gitopsRuntimeFeatureFlag != "" { - _ = os.Setenv(server.GitopsRuntimeFeatureFlag, tt.gitopsRuntimeFeatureFlag) - } - defer func() { - _ = os.Unsetenv(server.GitopsRuntimeFeatureFlag) - }() - featureflags.SetFromEnv(os.Environ()) scheme, err := kube.CreateScheme() g.Expect(err).To(BeNil()) client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tt.objects...).Build() From 62ced64504864b40f891161013d8edc849c64738 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Mon, 18 Dec 2023 16:32:51 +0100 Subject: [PATCH 18/43] implemented runtime crds api handler Signed-off-by: Eneko Fernandez --- core/server/fluxruntime_test.go | 132 +++++++++++++++----------------- 1 file changed, 60 insertions(+), 72 deletions(-) diff --git a/core/server/fluxruntime_test.go b/core/server/fluxruntime_test.go index 76a8b8a39f..14f7ced6d6 100644 --- a/core/server/fluxruntime_test.go +++ b/core/server/fluxruntime_test.go @@ -496,105 +496,93 @@ func newDeployment(name, ns string, labels map[string]string) *appsv1.Deployment func TestListFluxCrds(t *testing.T) { g := NewGomegaWithT(t) + + ctx := context.Background() + + crd1 := &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ + Name: "crd1", + Labels: map[string]string{coretypes.PartOfLabel: "flux"}, + }, Spec: apiextensions.CustomResourceDefinitionSpec{ + Group: "group", + Names: apiextensions.CustomResourceDefinitionNames{Plural: "plural", Kind: "kind"}, + Versions: []apiextensions.CustomResourceDefinitionVersion{}, + }} + crd2 := &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ + Name: "crd2", + Labels: map[string]string{coretypes.PartOfLabel: "flux"}, + }, Spec: apiextensions.CustomResourceDefinitionSpec{ + Group: "group", + Versions: []apiextensions.CustomResourceDefinitionVersion{ + {Name: "0"}, + // "Active" version in etcd, use this one. + {Name: "1", Storage: true}, + }, + }} + scheme, err := kube.CreateScheme() + g.Expect(err).To(BeNil()) + + client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(crd1, crd2).Build() + cfg := makeServerConfig(client, t, "") + c := makeServer(cfg, t) + + res, err := c.ListFluxCrds(ctx, &pb.ListFluxCrdsRequest{}) + + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(res.Crds).To(HaveLen(2)) + + first := res.Crds[0] + g.Expect(first.Version).To(Equal("")) + g.Expect(first.Name.Plural).To(Equal("plural")) + g.Expect(first.Name.Group).To(Equal("group")) + g.Expect(first.Kind).To(Equal("kind")) + g.Expect(first.ClusterName).To(Equal(cluster.DefaultCluster)) + g.Expect(res.Crds[1].Version).To(Equal("1")) +} + +func TestListRuntimeCrds(t *testing.T) { + g := NewGomegaWithT(t) ctx := context.Background() tests := []struct { - description string - objects []runtime.Object - gitopsRuntimeFeatureFlag string - assertions func(*pb.ListFluxCrdsResponse) + description string + objects []runtime.Object + assertions func(*pb.ListRuntimeCrdsResponse) }{ { - "return weave gitops runtime without WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME enabled", - []runtime.Object{ - &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ - Name: "crd1", - Labels: map[string]string{coretypes.PartOfLabel: "flux"}, - }, Spec: apiextensions.CustomResourceDefinitionSpec{ - Group: "group", - Names: apiextensions.CustomResourceDefinitionNames{Plural: "plural", Kind: "kind"}, - Versions: []apiextensions.CustomResourceDefinitionVersion{}, - }}, - &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ - Name: "crd2", - Labels: map[string]string{coretypes.PartOfLabel: "flux"}, - }, Spec: apiextensions.CustomResourceDefinitionSpec{ - Group: "group", - Versions: []apiextensions.CustomResourceDefinitionVersion{ - {Name: "0"}, - // "Active" version in etcd, use this one. - {Name: "1", Storage: true}, - }, - }}, - }, - "false", - func(res *pb.ListFluxCrdsResponse) { - g.Expect(res.Crds).To(HaveLen(2)) - first := res.Crds[0] - g.Expect(first.Version).To(Equal("")) - g.Expect(first.Name.Plural).To(Equal("plural")) - g.Expect(first.Name.Group).To(Equal("group")) - g.Expect(first.Kind).To(Equal("kind")) - g.Expect(first.ClusterName).To(Equal(cluster.DefaultCluster)) - g.Expect(res.Crds[1].Version).To(Equal("1")) - }, - }, - { - "return weave gitops runtime with WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME enabled", + "return weave gitops runtime crds", []runtime.Object{ &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ - Name: "crd1", + Name: "helmrelease", Labels: map[string]string{coretypes.PartOfLabel: server.Flux}, }, Spec: apiextensions.CustomResourceDefinitionSpec{ Group: "group", - Names: apiextensions.CustomResourceDefinitionNames{Plural: "plural", Kind: "kind"}, + Names: apiextensions.CustomResourceDefinitionNames{Plural: "helmreleases", Kind: "kind"}, Versions: []apiextensions.CustomResourceDefinitionVersion{}, }}, &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ - Name: "crd2", - Labels: map[string]string{coretypes.PartOfLabel: server.Flux}, - }, Spec: apiextensions.CustomResourceDefinitionSpec{ - Group: "group", - Versions: []apiextensions.CustomResourceDefinitionVersion{ - {Name: "0"}, - // "Active" version in etcd, use this one. - {Name: "1", Storage: true}, - }, - }}, - &apiextensions.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{ - Name: "crd3", + Name: "policy", Labels: map[string]string{coretypes.PartOfLabel: server.WeaveGitops}, }, Spec: apiextensions.CustomResourceDefinitionSpec{ - Group: "group", - Versions: []apiextensions.CustomResourceDefinitionVersion{ - {Name: "0"}, - // "Active" version in etcd, use this one. - {Name: "1", Storage: true}, - }, - }}, + Group: "group", + Names: apiextensions.CustomResourceDefinitionNames{Plural: "policies", Kind: "kind"}, + Versions: []apiextensions.CustomResourceDefinitionVersion{}}}, }, - "true", - func(res *pb.ListFluxCrdsResponse) { - g.Expect(res.Crds).To(HaveLen(3)) + func(res *pb.ListRuntimeCrdsResponse) { + g.Expect(res.Crds).To(HaveLen(2)) + g.Expect(res.Crds[0].GetName().Plural).To(Equal("helmreleases")) + g.Expect(res.Crds[1].GetName().Plural).To(Equal("policies")) }, }, } for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { - if tt.gitopsRuntimeFeatureFlag != "" { - _ = os.Setenv(server.GitopsRuntimeFeatureFlag, tt.gitopsRuntimeFeatureFlag) - } - defer func() { - _ = os.Unsetenv(server.GitopsRuntimeFeatureFlag) - }() - featureflags.SetFromEnv(os.Environ()) scheme, err := kube.CreateScheme() g.Expect(err).To(BeNil()) client := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tt.objects...).Build() cfg := makeServerConfig(client, t, "") c := makeServer(cfg, t) - res, err := c.ListFluxCrds(ctx, &pb.ListFluxCrdsRequest{}) + res, err := c.ListRuntimeCrds(ctx, &pb.ListRuntimeCrdsRequest{}) g.Expect(err).NotTo(HaveOccurred()) tt.assertions(res) From 35239ac81408e18cb14699933306c6ff0d40b8c6 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Mon, 18 Dec 2023 18:14:39 +0100 Subject: [PATCH 19/43] added ui support to runtime Signed-off-by: Eneko Fernandez --- ui/App.tsx | 27 ++++++++++++++++++++++++--- ui/hooks/flux.ts | 35 +++++++++++++++++++++++++++++++---- ui/lib/types.ts | 1 + ui/pages/v2/Runtime.tsx | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 ui/pages/v2/Runtime.tsx diff --git a/ui/App.tsx b/ui/App.tsx index 1efd6e5aa5..b0aea99e59 100644 --- a/ui/App.tsx +++ b/ui/App.tsx @@ -40,7 +40,6 @@ import Error from "./pages/Error"; import SignIn from "./pages/SignIn"; import Automations from "./pages/v2/Automations"; import BucketDetail from "./pages/v2/BucketDetail"; -import FluxRuntime from "./pages/v2/FluxRuntime"; import GitRepositoryDetail from "./pages/v2/GitRepositoryDetail"; import HelmChartDetail from "./pages/v2/HelmChartDetail"; import HelmReleasePage from "./pages/v2/HelmReleasePage"; @@ -52,6 +51,7 @@ import OCIRepositoryPage from "./pages/v2/OCIRepositoryPage"; import PoliciesList from "./pages/v2/PoliciesList"; import PolicyDetailsPage from "./pages/v2/PolicyDetailsPage"; import ProviderPage from "./pages/v2/ProviderPage"; +import Runtime from "./pages/v2/Runtime"; import Sources from "./pages/v2/Sources"; import UserInfo from "./pages/v2/UserInfo"; @@ -65,6 +65,26 @@ function withSearchParams(Cmp) { }; } +// gets the right runtime navigation item based on the feature WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME +// function getRuntimeConfiguration() { +// const { isFlagEnabled } = useFeatureFlags(); +// +// if (isFlagEnabled("WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME")) { +// return { +// label: "Runtime", +// route: V2Routes.Runtime, +// component: {Runtime}, +// }; +// } +// return { +// label: "Flux Runtime", +// route: V2Routes.FluxRuntime, +// component: {FluxRuntime}, +// }; +// } + +// const runtimeConfiguration = getRuntimeConfiguration(); + const navItems: NavItem[] = [ { label: "Applications", @@ -88,7 +108,7 @@ const navItems: NavItem[] = [ }, { label: "Runtime", - link: { value: V2Routes.FluxRuntime }, + link: { value: V2Routes.Runtime }, icon: IconType.FluxIcon, }, { @@ -143,7 +163,8 @@ const App = () => { path={V2Routes.ImagePolicyDetails} component={withSearchParams(ImagePolicyDetails)} /> - + // TODO should be configurable based on feature flag + = { + retry: false, + refetchInterval: 5000, + } +) { + const { api } = useContext(CoreClientContext); + + return useQuery( + "runtime_objects", + () => api.ListRuntimeObjects({ namespace, clusterName }), + opts + ); +} + +export function useListRuntimeCrds(clusterName = DefaultCluster) { + const { api } = useContext(CoreClientContext); + + return useQuery( + "runtime_crds", + () => api.ListRuntimeCrds({ clusterName }), + { retry: false, refetchInterval: 5000 } + ); +} + export function flattenChildren(children: FluxObject[]) { return children.flatMap((child) => [child].concat(flattenChildren(child.children)) diff --git a/ui/lib/types.ts b/ui/lib/types.ts index 9b7c8aaa1d..e973a662c4 100644 --- a/ui/lib/types.ts +++ b/ui/lib/types.ts @@ -28,6 +28,7 @@ export enum V2Routes { Automations = "/applications", Sources = "/sources", FluxRuntime = "/flux_runtime", + Runtime = "/runtime", Kustomization = "/kustomization", HelmRelease = "/helm_release", HelmRepo = "/helm_repo", diff --git a/ui/pages/v2/Runtime.tsx b/ui/pages/v2/Runtime.tsx new file mode 100644 index 0000000000..f628afdb74 --- /dev/null +++ b/ui/pages/v2/Runtime.tsx @@ -0,0 +1,34 @@ +import * as React from "react"; +import {useContext} from "react"; +import styled from "styled-components"; +import FluxRuntimeComponent from "../../components/FluxRuntime"; +import Page from "../../components/Page"; +import {CoreClientContext} from "../../contexts/CoreClientContext"; +import { useFeatureFlags } from "../../hooks/featureflags"; +import {useListFluxCrds, useListFluxRuntimeObjects, useListRuntimeCrds, useListRuntimeObjects} from "../../hooks/flux"; + +type Props = { + className?: string; +}; + +function Runtime({ className }: Props) { + const { data, isLoading, error } = useListRuntimeObjects(); + const { + data: crds, + isLoading: crdsLoading, + error: crdsError, + } = useListRuntimeCrds(); + return ( + + + + + ); +} + +export default styled(Runtime).attrs({ className: Runtime.name })``; From 2115b9afd81ab18483111055983d6629ffac2304 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Mon, 18 Dec 2023 18:20:48 +0100 Subject: [PATCH 20/43] review pr Signed-off-by: Eneko Fernandez --- api/core/core.proto | 6 +++++- ui/pages/v2/FluxRuntime.tsx | 3 --- ui/pages/v2/Runtime.tsx | 5 +---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/api/core/core.proto b/api/core/core.proto index a6552978b4..b550d6bf23 100644 --- a/api/core/core.proto +++ b/api/core/core.proto @@ -65,7 +65,7 @@ service Core { } /* - * ListRuntimeObjects lists Weave GitOps runtime components from a clusters. Weave GitOps runtime is composed of Flux runtime + * ListRuntimeObjects lists Weave GitOps runtime components from a cluster. Weave GitOps runtime is composed of Flux runtime * but also the other components in the ecosystem like TF-controller or Policy Agent. */ rpc ListRuntimeObjects(ListRuntimeObjectsRequest) @@ -75,6 +75,10 @@ service Core { }; } + /* + * ListRuntimeCrds lists Weave GitOps runtime components CRDs from a cluster. Weave GitOps runtime is composed of Flux runtime + * but also the other components in the ecosystem like TF-controller or Policy Agent. + */ rpc ListRuntimeCrds(ListRuntimeCrdsRequest) returns (ListRuntimeCrdsResponse) { option (google.api.http) = { diff --git a/ui/pages/v2/FluxRuntime.tsx b/ui/pages/v2/FluxRuntime.tsx index 778a186556..6570b41f80 100644 --- a/ui/pages/v2/FluxRuntime.tsx +++ b/ui/pages/v2/FluxRuntime.tsx @@ -1,10 +1,7 @@ import * as React from "react"; -import {useContext} from "react"; import styled from "styled-components"; import FluxRuntimeComponent from "../../components/FluxRuntime"; import Page from "../../components/Page"; -import {CoreClientContext} from "../../contexts/CoreClientContext"; -import { useFeatureFlags } from "../../hooks/featureflags"; import { useListFluxCrds, useListFluxRuntimeObjects } from "../../hooks/flux"; type Props = { diff --git a/ui/pages/v2/Runtime.tsx b/ui/pages/v2/Runtime.tsx index f628afdb74..bb6be99060 100644 --- a/ui/pages/v2/Runtime.tsx +++ b/ui/pages/v2/Runtime.tsx @@ -1,11 +1,8 @@ import * as React from "react"; -import {useContext} from "react"; import styled from "styled-components"; import FluxRuntimeComponent from "../../components/FluxRuntime"; import Page from "../../components/Page"; -import {CoreClientContext} from "../../contexts/CoreClientContext"; -import { useFeatureFlags } from "../../hooks/featureflags"; -import {useListFluxCrds, useListFluxRuntimeObjects, useListRuntimeCrds, useListRuntimeObjects} from "../../hooks/flux"; +import {useListRuntimeCrds, useListRuntimeObjects} from "../../hooks/flux"; type Props = { className?: string; From 38cbb0e770987f26cb0547971700ac8279187284 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Mon, 18 Dec 2023 18:39:49 +0100 Subject: [PATCH 21/43] fix style Signed-off-by: Eneko Fernandez --- api/core/core.proto | 10 ++++++---- api/core/core.swagger.json | 3 ++- pkg/api/core/core_grpc.pb.go | 8 ++++++-- ui/App.tsx | 3 --- ui/components/FluxRuntime.tsx | 24 +++++++++++++----------- ui/hooks/flux.ts | 13 +++++++------ ui/pages/v2/FluxRuntime.tsx | 3 +-- ui/pages/v2/Runtime.tsx | 3 +-- 8 files changed, 36 insertions(+), 31 deletions(-) diff --git a/api/core/core.proto b/api/core/core.proto index b550d6bf23..75f9e58740 100644 --- a/api/core/core.proto +++ b/api/core/core.proto @@ -65,8 +65,9 @@ service Core { } /* - * ListRuntimeObjects lists Weave GitOps runtime components from a cluster. Weave GitOps runtime is composed of Flux runtime - * but also the other components in the ecosystem like TF-controller or Policy Agent. + * ListRuntimeObjects lists Weave GitOps runtime components. + * Weave GitOps runtime is composed of Flux runtime but also other components + * in the ecosystem like TF-controller or Policy Agent. */ rpc ListRuntimeObjects(ListRuntimeObjectsRequest) returns (ListRuntimeObjectsResponse) { @@ -76,8 +77,9 @@ service Core { } /* - * ListRuntimeCrds lists Weave GitOps runtime components CRDs from a cluster. Weave GitOps runtime is composed of Flux runtime - * but also the other components in the ecosystem like TF-controller or Policy Agent. + * ListRuntimeCrds lists Weave GitOps runtime components CRDs. + * Weave GitOps runtime is composed of Flux runtime but also other components + * in the ecosystem like TF-controller or Policy Agent. */ rpc ListRuntimeCrds(ListRuntimeCrdsRequest) returns (ListRuntimeCrdsResponse) { diff --git a/api/core/core.swagger.json b/api/core/core.swagger.json index 70febc2ac4..c701ae8789 100644 --- a/api/core/core.swagger.json +++ b/api/core/core.swagger.json @@ -604,6 +604,7 @@ }, "/v1/runtime_crds": { "get": { + "summary": "ListRuntimeCrds lists Weave GitOps runtime components CRDs from a cluster. Weave GitOps runtime is composed of Flux runtime\nbut also the other components in the ecosystem like TF-controller or Policy Agent.", "operationId": "Core_ListRuntimeCrds", "responses": { "200": { @@ -634,7 +635,7 @@ }, "/v1/runtime_objects": { "get": { - "summary": "ListRuntimeObjects lists Weave GitOps runtime components from a clusters. Weave GitOps runtime is composed of Flux runtime\nbut also the other components in the ecosystem like TF-controller or Policy Agent.", + "summary": "ListRuntimeObjects lists Weave GitOps runtime components from a cluster. Weave GitOps runtime is composed of Flux runtime\nbut also the other components in the ecosystem like TF-controller or Policy Agent.", "operationId": "Core_ListRuntimeObjects", "responses": { "200": { diff --git a/pkg/api/core/core_grpc.pb.go b/pkg/api/core/core_grpc.pb.go index 882b563705..76e5b41b7d 100644 --- a/pkg/api/core/core_grpc.pb.go +++ b/pkg/api/core/core_grpc.pb.go @@ -25,9 +25,11 @@ type CoreClient interface { // ListFluxRuntimeObjects lists the flux runtime deployments from a cluster. ListFluxRuntimeObjects(ctx context.Context, in *ListFluxRuntimeObjectsRequest, opts ...grpc.CallOption) (*ListFluxRuntimeObjectsResponse, error) ListFluxCrds(ctx context.Context, in *ListFluxCrdsRequest, opts ...grpc.CallOption) (*ListFluxCrdsResponse, error) - // ListRuntimeObjects lists Weave GitOps runtime components from a clusters. Weave GitOps runtime is composed of Flux runtime + // ListRuntimeObjects lists Weave GitOps runtime components from a cluster. Weave GitOps runtime is composed of Flux runtime // but also the other components in the ecosystem like TF-controller or Policy Agent. ListRuntimeObjects(ctx context.Context, in *ListRuntimeObjectsRequest, opts ...grpc.CallOption) (*ListRuntimeObjectsResponse, error) + // ListRuntimeCrds lists Weave GitOps runtime components CRDs from a cluster. Weave GitOps runtime is composed of Flux runtime + // but also the other components in the ecosystem like TF-controller or Policy Agent. ListRuntimeCrds(ctx context.Context, in *ListRuntimeCrdsRequest, opts ...grpc.CallOption) (*ListRuntimeCrdsResponse, error) // GetReconciledObjects returns a list of objects that were created // as a result of reconciling a Flux automation. @@ -287,9 +289,11 @@ type CoreServer interface { // ListFluxRuntimeObjects lists the flux runtime deployments from a cluster. ListFluxRuntimeObjects(context.Context, *ListFluxRuntimeObjectsRequest) (*ListFluxRuntimeObjectsResponse, error) ListFluxCrds(context.Context, *ListFluxCrdsRequest) (*ListFluxCrdsResponse, error) - // ListRuntimeObjects lists Weave GitOps runtime components from a clusters. Weave GitOps runtime is composed of Flux runtime + // ListRuntimeObjects lists Weave GitOps runtime components from a cluster. Weave GitOps runtime is composed of Flux runtime // but also the other components in the ecosystem like TF-controller or Policy Agent. ListRuntimeObjects(context.Context, *ListRuntimeObjectsRequest) (*ListRuntimeObjectsResponse, error) + // ListRuntimeCrds lists Weave GitOps runtime components CRDs from a cluster. Weave GitOps runtime is composed of Flux runtime + // but also the other components in the ecosystem like TF-controller or Policy Agent. ListRuntimeCrds(context.Context, *ListRuntimeCrdsRequest) (*ListRuntimeCrdsResponse, error) // GetReconciledObjects returns a list of objects that were created // as a result of reconciling a Flux automation. diff --git a/ui/App.tsx b/ui/App.tsx index b0aea99e59..c700ad368d 100644 --- a/ui/App.tsx +++ b/ui/App.tsx @@ -202,15 +202,12 @@ const App = () => { component={withSearchParams(PolicyViolationPage)} /> - - - diff --git a/ui/components/FluxRuntime.tsx b/ui/components/FluxRuntime.tsx index ad18bc97b8..131b2b2206 100644 --- a/ui/components/FluxRuntime.tsx +++ b/ui/components/FluxRuntime.tsx @@ -52,17 +52,19 @@ function FluxRuntime({ className, deployments, crds }: Props) { }, ]; const fluxVersions: { [key: string]: FluxVersion } = {}; - deployments.filter( (d) => d.labels[partOfLabel] == fluxLabel ).forEach((d) => { - const fv = d.labels[fluxVersionLabel]; - const k = `${fv}${d.clusterName}${d.namespace}`; - if (!fluxVersions[k]) { - fluxVersions[k] = { - version: fv, - clusterName: d.clusterName, - namespace: d.namespace, - }; - } - }); + deployments + .filter((d) => d.labels[partOfLabel] == fluxLabel) + .forEach((d) => { + const fv = d.labels[fluxVersionLabel]; + const k = `${fv}${d.clusterName}${d.namespace}`; + if (!fluxVersions[k]) { + fluxVersions[k] = { + version: fv, + clusterName: d.clusterName, + namespace: d.namespace, + }; + } + }); const supportMultipleFlux = true; diff --git a/ui/hooks/flux.ts b/ui/hooks/flux.ts index 7f64c19726..3788f8def2 100644 --- a/ui/hooks/flux.ts +++ b/ui/hooks/flux.ts @@ -3,7 +3,8 @@ import { useMutation, useQuery, useQueryClient } from "react-query"; import { CoreClientContext } from "../contexts/CoreClientContext"; import { ListFluxCrdsResponse, - ListFluxRuntimeObjectsResponse, ListRuntimeObjectsResponse, + ListFluxRuntimeObjectsResponse, + ListRuntimeObjectsResponse, ToggleSuspendResourceRequest, ToggleSuspendResourceResponse, } from "../lib/api/core/core.pb"; @@ -52,22 +53,22 @@ export function useListRuntimeObjects( refetchInterval: 5000, } ) { - const { api } = useContext(CoreClientContext); + const {api} = useContext(CoreClientContext); return useQuery( "runtime_objects", - () => api.ListRuntimeObjects({ namespace, clusterName }), + () => api.ListRuntimeObjects({namespace, clusterName}), opts ); } export function useListRuntimeCrds(clusterName = DefaultCluster) { - const { api } = useContext(CoreClientContext); + const {api} = useContext(CoreClientContext); return useQuery( "runtime_crds", - () => api.ListRuntimeCrds({ clusterName }), - { retry: false, refetchInterval: 5000 } + () => api.ListRuntimeCrds({clusterName}), + {retry: false, refetchInterval: 5000} ); } diff --git a/ui/pages/v2/FluxRuntime.tsx b/ui/pages/v2/FluxRuntime.tsx index 6570b41f80..db100735ce 100644 --- a/ui/pages/v2/FluxRuntime.tsx +++ b/ui/pages/v2/FluxRuntime.tsx @@ -16,8 +16,7 @@ function FluxRuntime({ className }: Props) { error: crdsError, } = useListFluxCrds(); return ( - - Date: Mon, 18 Dec 2023 18:43:28 +0100 Subject: [PATCH 22/43] prettified Signed-off-by: Eneko Fernandez --- ui/components/FluxRuntime.tsx | 24 +++++++++--------- ui/hooks/flux.ts | 46 +++++++++++++++++------------------ ui/pages/v2/FluxRuntime.tsx | 2 +- ui/pages/v2/Runtime.tsx | 4 +-- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/ui/components/FluxRuntime.tsx b/ui/components/FluxRuntime.tsx index 131b2b2206..5fe85cd013 100644 --- a/ui/components/FluxRuntime.tsx +++ b/ui/components/FluxRuntime.tsx @@ -53,18 +53,18 @@ function FluxRuntime({ className, deployments, crds }: Props) { ]; const fluxVersions: { [key: string]: FluxVersion } = {}; deployments - .filter((d) => d.labels[partOfLabel] == fluxLabel) - .forEach((d) => { - const fv = d.labels[fluxVersionLabel]; - const k = `${fv}${d.clusterName}${d.namespace}`; - if (!fluxVersions[k]) { - fluxVersions[k] = { - version: fv, - clusterName: d.clusterName, - namespace: d.namespace, - }; - } - }); + .filter((d) => d.labels[partOfLabel] == fluxLabel) + .forEach((d) => { + const fv = d.labels[fluxVersionLabel]; + const k = `${fv}${d.clusterName}${d.namespace}`; + if (!fluxVersions[k]) { + fluxVersions[k] = { + version: fv, + clusterName: d.clusterName, + namespace: d.namespace, + }; + } + }); const supportMultipleFlux = true; diff --git a/ui/hooks/flux.ts b/ui/hooks/flux.ts index 3788f8def2..96604dde23 100644 --- a/ui/hooks/flux.ts +++ b/ui/hooks/flux.ts @@ -2,11 +2,11 @@ import { useContext } from "react"; import { useMutation, useQuery, useQueryClient } from "react-query"; import { CoreClientContext } from "../contexts/CoreClientContext"; import { - ListFluxCrdsResponse, - ListFluxRuntimeObjectsResponse, - ListRuntimeObjectsResponse, - ToggleSuspendResourceRequest, - ToggleSuspendResourceResponse, + ListFluxCrdsResponse, + ListFluxRuntimeObjectsResponse, + ListRuntimeObjectsResponse, + ToggleSuspendResourceRequest, + ToggleSuspendResourceResponse, } from "../lib/api/core/core.pb"; import { GroupVersionKind, Kind } from "../lib/api/core/types.pb"; import { getChildren } from "../lib/graph"; @@ -46,30 +46,30 @@ export function useListFluxCrds(clusterName = DefaultCluster) { } export function useListRuntimeObjects( - clusterName = DefaultCluster, - namespace = NoNamespace, - opts: ReactQueryOptions = { - retry: false, - refetchInterval: 5000, - } + clusterName = DefaultCluster, + namespace = NoNamespace, + opts: ReactQueryOptions = { + retry: false, + refetchInterval: 5000, + } ) { - const {api} = useContext(CoreClientContext); + const { api } = useContext(CoreClientContext); - return useQuery( - "runtime_objects", - () => api.ListRuntimeObjects({namespace, clusterName}), - opts - ); + return useQuery( + "runtime_objects", + () => api.ListRuntimeObjects({ namespace, clusterName }), + opts + ); } export function useListRuntimeCrds(clusterName = DefaultCluster) { - const {api} = useContext(CoreClientContext); + const { api } = useContext(CoreClientContext); - return useQuery( - "runtime_crds", - () => api.ListRuntimeCrds({clusterName}), - {retry: false, refetchInterval: 5000} - ); + return useQuery( + "runtime_crds", + () => api.ListRuntimeCrds({ clusterName }), + { retry: false, refetchInterval: 5000 } + ); } export function flattenChildren(children: FluxObject[]) { diff --git a/ui/pages/v2/FluxRuntime.tsx b/ui/pages/v2/FluxRuntime.tsx index db100735ce..039f52cc97 100644 --- a/ui/pages/v2/FluxRuntime.tsx +++ b/ui/pages/v2/FluxRuntime.tsx @@ -16,7 +16,7 @@ function FluxRuntime({ className }: Props) { error: crdsError, } = useListFluxCrds(); return ( - Date: Mon, 18 Dec 2023 18:48:36 +0100 Subject: [PATCH 23/43] ran proto Signed-off-by: Eneko Fernandez --- api/core/core.swagger.json | 6 +++--- api/core/types.swagger.json | 2 +- pkg/api/core/core_grpc.pb.go | 20 ++++++++++++-------- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/api/core/core.swagger.json b/api/core/core.swagger.json index c701ae8789..c36652853d 100644 --- a/api/core/core.swagger.json +++ b/api/core/core.swagger.json @@ -604,7 +604,7 @@ }, "/v1/runtime_crds": { "get": { - "summary": "ListRuntimeCrds lists Weave GitOps runtime components CRDs from a cluster. Weave GitOps runtime is composed of Flux runtime\nbut also the other components in the ecosystem like TF-controller or Policy Agent.", + "summary": "ListRuntimeCrds lists Weave GitOps runtime components CRDs.\nWeave GitOps runtime is composed of Flux runtime but also other components\nin the ecosystem like TF-controller or Policy Agent.", "operationId": "Core_ListRuntimeCrds", "responses": { "200": { @@ -635,7 +635,7 @@ }, "/v1/runtime_objects": { "get": { - "summary": "ListRuntimeObjects lists Weave GitOps runtime components from a cluster. Weave GitOps runtime is composed of Flux runtime\nbut also the other components in the ecosystem like TF-controller or Policy Agent.", + "summary": "ListRuntimeObjects lists Weave GitOps runtime components.\nWeave GitOps runtime is composed of Flux runtime but also other components\nin the ecosystem like TF-controller or Policy Agent.", "operationId": "Core_ListRuntimeObjects", "responses": { "200": { @@ -814,7 +814,7 @@ } }, "additionalProperties": {}, - "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\nExample 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\nExample 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" + "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" }, "rpcStatus": { "type": "object", diff --git a/api/core/types.swagger.json b/api/core/types.swagger.json index 07751266f1..3bea52f4d3 100644 --- a/api/core/types.swagger.json +++ b/api/core/types.swagger.json @@ -21,7 +21,7 @@ } }, "additionalProperties": {}, - "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\nExample 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\nExample 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" + "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(\u0026foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := \u0026pb.Foo{...}\n any, err := anypb.New(foo)\n if err != nil {\n ...\n }\n ...\n foo := \u0026pb.Foo{}\n if err := any.UnmarshalTo(foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": \u003cstring\u003e,\n \"lastName\": \u003cstring\u003e\n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" }, "rpcStatus": { "type": "object", diff --git a/pkg/api/core/core_grpc.pb.go b/pkg/api/core/core_grpc.pb.go index 76e5b41b7d..7a7bd8deb6 100644 --- a/pkg/api/core/core_grpc.pb.go +++ b/pkg/api/core/core_grpc.pb.go @@ -25,11 +25,13 @@ type CoreClient interface { // ListFluxRuntimeObjects lists the flux runtime deployments from a cluster. ListFluxRuntimeObjects(ctx context.Context, in *ListFluxRuntimeObjectsRequest, opts ...grpc.CallOption) (*ListFluxRuntimeObjectsResponse, error) ListFluxCrds(ctx context.Context, in *ListFluxCrdsRequest, opts ...grpc.CallOption) (*ListFluxCrdsResponse, error) - // ListRuntimeObjects lists Weave GitOps runtime components from a cluster. Weave GitOps runtime is composed of Flux runtime - // but also the other components in the ecosystem like TF-controller or Policy Agent. + // ListRuntimeObjects lists Weave GitOps runtime components. + // Weave GitOps runtime is composed of Flux runtime but also other components + // in the ecosystem like TF-controller or Policy Agent. ListRuntimeObjects(ctx context.Context, in *ListRuntimeObjectsRequest, opts ...grpc.CallOption) (*ListRuntimeObjectsResponse, error) - // ListRuntimeCrds lists Weave GitOps runtime components CRDs from a cluster. Weave GitOps runtime is composed of Flux runtime - // but also the other components in the ecosystem like TF-controller or Policy Agent. + // ListRuntimeCrds lists Weave GitOps runtime components CRDs. + // Weave GitOps runtime is composed of Flux runtime but also other components + // in the ecosystem like TF-controller or Policy Agent. ListRuntimeCrds(ctx context.Context, in *ListRuntimeCrdsRequest, opts ...grpc.CallOption) (*ListRuntimeCrdsResponse, error) // GetReconciledObjects returns a list of objects that were created // as a result of reconciling a Flux automation. @@ -289,11 +291,13 @@ type CoreServer interface { // ListFluxRuntimeObjects lists the flux runtime deployments from a cluster. ListFluxRuntimeObjects(context.Context, *ListFluxRuntimeObjectsRequest) (*ListFluxRuntimeObjectsResponse, error) ListFluxCrds(context.Context, *ListFluxCrdsRequest) (*ListFluxCrdsResponse, error) - // ListRuntimeObjects lists Weave GitOps runtime components from a cluster. Weave GitOps runtime is composed of Flux runtime - // but also the other components in the ecosystem like TF-controller or Policy Agent. + // ListRuntimeObjects lists Weave GitOps runtime components. + // Weave GitOps runtime is composed of Flux runtime but also other components + // in the ecosystem like TF-controller or Policy Agent. ListRuntimeObjects(context.Context, *ListRuntimeObjectsRequest) (*ListRuntimeObjectsResponse, error) - // ListRuntimeCrds lists Weave GitOps runtime components CRDs from a cluster. Weave GitOps runtime is composed of Flux runtime - // but also the other components in the ecosystem like TF-controller or Policy Agent. + // ListRuntimeCrds lists Weave GitOps runtime components CRDs. + // Weave GitOps runtime is composed of Flux runtime but also other components + // in the ecosystem like TF-controller or Policy Agent. ListRuntimeCrds(context.Context, *ListRuntimeCrdsRequest) (*ListRuntimeCrdsResponse, error) // GetReconciledObjects returns a list of objects that were created // as a result of reconciling a Flux automation. From e90c3421cf99934e0216bc1d074c48e531bb5d23 Mon Sep 17 00:00:00 2001 From: Olga Pudrovska Date: Tue, 19 Dec 2023 16:05:24 +0100 Subject: [PATCH 24/43] Smth. like that should work. --- ui/App.tsx | 107 ++++++++++++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 51 deletions(-) diff --git a/ui/App.tsx b/ui/App.tsx index c700ad368d..5ce2038170 100644 --- a/ui/App.tsx +++ b/ui/App.tsx @@ -28,6 +28,7 @@ import AppContextProvider, { } from "./contexts/AppContext"; import AuthContextProvider, { AuthCheck } from "./contexts/AuthContext"; import CoreClientContextProvider from "./contexts/CoreClientContext"; +import { useFeatureFlags } from "./hooks/featureflags"; import useNavigation from "./hooks/navigation"; import { useInDarkMode } from "./hooks/theme"; import { Core } from "./lib/api/core/core.pb"; @@ -40,6 +41,7 @@ import Error from "./pages/Error"; import SignIn from "./pages/SignIn"; import Automations from "./pages/v2/Automations"; import BucketDetail from "./pages/v2/BucketDetail"; +import FluxRuntime from "./pages/v2/FluxRuntime"; import GitRepositoryDetail from "./pages/v2/GitRepositoryDetail"; import HelmChartDetail from "./pages/v2/HelmChartDetail"; import HelmReleasePage from "./pages/v2/HelmReleasePage"; @@ -66,60 +68,63 @@ function withSearchParams(Cmp) { } // gets the right runtime navigation item based on the feature WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME -// function getRuntimeConfiguration() { -// const { isFlagEnabled } = useFeatureFlags(); -// -// if (isFlagEnabled("WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME")) { -// return { -// label: "Runtime", -// route: V2Routes.Runtime, -// component: {Runtime}, -// }; -// } -// return { -// label: "Flux Runtime", -// route: V2Routes.FluxRuntime, -// component: {FluxRuntime}, -// }; -// } +function getRuntimeConfiguration(isNewRuntimeEnabled: boolean): NavItem { + if (isNewRuntimeEnabled) { + return { + label: "Runtime", + link: { value: V2Routes.Runtime }, + icon: IconType.FluxIcon, + } + } -// const runtimeConfiguration = getRuntimeConfiguration(); - -const navItems: NavItem[] = [ - { - label: "Applications", - link: { value: V2Routes.Automations }, - icon: IconType.ApplicationsIcon, - }, - { - label: "Sources", - link: { value: V2Routes.Sources }, - icon: IconType.SourcesIcon, - }, - { - label: "Image Automation", - link: { value: V2Routes.ImageAutomation }, - icon: IconType.ImageAutomationIcon, - }, - { - label: "Policies", - link: { value: V2Routes.Policies }, - icon: IconType.PoliciesIcon, - }, - { - label: "Runtime", - link: { value: V2Routes.Runtime }, + return { + label: "Flux Runtime", + link: { value: V2Routes.FluxRuntime }, icon: IconType.FluxIcon, - }, - { - label: "Notifications", - link: { value: V2Routes.Notifications }, - icon: IconType.NotificationsIcon, - }, -]; + } +} const App = () => { const dark = useInDarkMode(); + + const { isFlagEnabled } = useFeatureFlags(); + + const isNewRuntimeEnabled = isFlagEnabled("WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME"); + + // TODO: remove debug message + console.log("WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME:") + console.log(isNewRuntimeEnabled) + + const runtimeNavItem: NavItem = getRuntimeConfiguration(isNewRuntimeEnabled); + + const navItems: NavItem[] = [ + { + label: "Applications", + link: { value: V2Routes.Automations }, + icon: IconType.ApplicationsIcon, + }, + { + label: "Sources", + link: { value: V2Routes.Sources }, + icon: IconType.SourcesIcon, + }, + { + label: "Image Automation", + link: { value: V2Routes.ImageAutomation }, + icon: IconType.ImageAutomationIcon, + }, + { + label: "Policies", + link: { value: V2Routes.Policies }, + icon: IconType.PoliciesIcon, + }, + runtimeNavItem, + { + label: "Notifications", + link: { value: V2Routes.Notifications }, + icon: IconType.NotificationsIcon, + }]; + const [collapsed, setCollapsed] = React.useState(false); const { currentPage } = useNavigation(); const value = getParentNavRouteValue(currentPage); @@ -163,8 +168,8 @@ const App = () => { path={V2Routes.ImagePolicyDetails} component={withSearchParams(ImagePolicyDetails)} /> - // TODO should be configurable based on feature flag - + {/* TODO should be configurable based on feature flag */} + {isNewRuntimeEnabled ? : } Date: Tue, 19 Dec 2023 17:58:58 +0100 Subject: [PATCH 25/43] just my grain of salt of the pr Signed-off-by: Eneko Fernandez --- ui/App.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ui/App.tsx b/ui/App.tsx index 5ce2038170..ddfac39e72 100644 --- a/ui/App.tsx +++ b/ui/App.tsx @@ -67,21 +67,20 @@ function withSearchParams(Cmp) { }; } -// gets the right runtime navigation item based on the feature WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME -function getRuntimeConfiguration(isNewRuntimeEnabled: boolean): NavItem { +function getRuntimeNavItem(isNewRuntimeEnabled: boolean): NavItem { if (isNewRuntimeEnabled) { return { label: "Runtime", link: { value: V2Routes.Runtime }, icon: IconType.FluxIcon, - } + }; } return { label: "Flux Runtime", link: { value: V2Routes.FluxRuntime }, icon: IconType.FluxIcon, - } + }; } const App = () => { @@ -95,8 +94,6 @@ const App = () => { console.log("WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME:") console.log(isNewRuntimeEnabled) - const runtimeNavItem: NavItem = getRuntimeConfiguration(isNewRuntimeEnabled); - const navItems: NavItem[] = [ { label: "Applications", @@ -118,12 +115,13 @@ const App = () => { link: { value: V2Routes.Policies }, icon: IconType.PoliciesIcon, }, - runtimeNavItem, + getRuntimeNavItem(isNewRuntimeEnabled), { label: "Notifications", link: { value: V2Routes.Notifications }, icon: IconType.NotificationsIcon, - }]; + }, + ]; const [collapsed, setCollapsed] = React.useState(false); const { currentPage } = useNavigation(); From 2412c37f52f5de4f1f562edcbe67b3da519ded51 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Tue, 19 Dec 2023 18:04:05 +0100 Subject: [PATCH 26/43] review PR Signed-off-by: Eneko Fernandez --- ui/App.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ui/App.tsx b/ui/App.tsx index ddfac39e72..fd1d59ba15 100644 --- a/ui/App.tsx +++ b/ui/App.tsx @@ -90,10 +90,6 @@ const App = () => { const isNewRuntimeEnabled = isFlagEnabled("WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME"); - // TODO: remove debug message - console.log("WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME:") - console.log(isNewRuntimeEnabled) - const navItems: NavItem[] = [ { label: "Applications", @@ -166,7 +162,6 @@ const App = () => { path={V2Routes.ImagePolicyDetails} component={withSearchParams(ImagePolicyDetails)} /> - {/* TODO should be configurable based on feature flag */} {isNewRuntimeEnabled ? : } Date: Tue, 19 Dec 2023 18:08:47 +0100 Subject: [PATCH 27/43] review PR Signed-off-by: Eneko Fernandez --- ui/App.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ui/App.tsx b/ui/App.tsx index fd1d59ba15..eeebcde691 100644 --- a/ui/App.tsx +++ b/ui/App.tsx @@ -88,7 +88,9 @@ const App = () => { const { isFlagEnabled } = useFeatureFlags(); - const isNewRuntimeEnabled = isFlagEnabled("WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME"); + const isNewRuntimeEnabled = isFlagEnabled( + "WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME" + ); const navItems: NavItem[] = [ { @@ -162,7 +164,11 @@ const App = () => { path={V2Routes.ImagePolicyDetails} component={withSearchParams(ImagePolicyDetails)} /> - {isNewRuntimeEnabled ? : } + {isNewRuntimeEnabled ? ( + + ) : ( + + )} Date: Wed, 20 Dec 2023 15:02:30 +0100 Subject: [PATCH 28/43] added documentation Signed-off-by: Eneko Fernandez --- charts/gitops-server/values.yaml | 4 +-- .../open-source/getting-started/ui-OSS.mdx | 29 ++++++++++++++++-- .../dashboard-flux-runtime-terraform-crds.png | Bin 0 -> 139442 bytes .../img/dashboard-flux-runtime-versions.png | Bin 0 -> 145484 bytes 4 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 website/static/img/dashboard-flux-runtime-terraform-crds.png create mode 100644 website/static/img/dashboard-flux-runtime-versions.png diff --git a/charts/gitops-server/values.yaml b/charts/gitops-server/values.yaml index df2f10a144..d71a5d6e6a 100644 --- a/charts/gitops-server/values.yaml +++ b/charts/gitops-server/values.yaml @@ -24,8 +24,8 @@ envVars: value: "true" - name: WEAVE_GITOPS_FEATURE_CLUSTER value: "false" - # configures the behaviour for weave gitops runtime ui. if enabled, flux runtime UI - # will show other weave-gitops components like policy-agent + # -- Enable feature this feature flag if you want to expand Flux Runtime UI with other Weave GitOps components like Policy Agent or TF-Controller. + # Ensure that Weave GitOps Deployment and CRDs are labelled with Label 'app.kubernetes.io/part-of=weave-gitops'. - name: WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME value: "false" diff --git a/website/docs/open-source/getting-started/ui-OSS.mdx b/website/docs/open-source/getting-started/ui-OSS.mdx index c2b256ece1..f71202c417 100644 --- a/website/docs/open-source/getting-started/ui-OSS.mdx +++ b/website/docs/open-source/getting-started/ui-OSS.mdx @@ -149,9 +149,31 @@ If you make a mistake configuring one of the resources, you can use WeGO to easi ## The Flux Runtime View -Let's go back to the left-hand menu of the UI and click on `Flux Runtime`. This view provides information on the GitOps engine, which continuously reconciles your desired and live state, and helps users to know which apiVersion to use in manifests. It comes with two tabs: one for controllers, and other for custom resource definitions (CRDs). +Let's go back to the left-hand menu of the UI and click on `Flux Runtime`. This view provides information on the GitOps engine, +which continuously reconciles your desired and live state, and helps users to know which apiVersion to use in manifests. -#### Controllers +:::tip Beyond Flux Runtime + +Enable the feature flag [`WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME`](../../references/helm-reference.md) if you want to expand Flux Runtime UI with the ability to +view other Weave GitOps Runtime Components like Policy Agent or TF-Controller + +![Runtime view showing Terraform CRDs](/img/dashboard-flux-runtime-terraform-crds.png) + +::: + +It comes with three tabs: + +1. [Flux Versions](#flux-versions) that summaries the versions of flux running in your cluster. +2. [Controllers](#controllers): that shows your installed [GitOps Toolkit Controllers](https://fluxcd.io/flux/components/) and their version. +3. [CRDs](#crds): that shows your installed [GitOps Toolkit Custom Resources Definitions Toolkit Controllers](https://fluxcd.io/flux/components/) and their version. + +### Flux Versions + +The Flux Versions tab shows your installed [Flux Versions](https://github.com/fluxcd/flux2/releases). + +![Flux Runtime view showing the various GitOps Toolkit controllers](/img/dashboard-flux-runtime-versions.png) + +### Controllers The Controllers tab shows your installed [GitOps Toolkit Controllers](https://fluxcd.io/flux/components/) and their version. @@ -165,12 +187,13 @@ By default, `flux bootstrap` will install the following controllers: From this view you can see whether the controllers are healthy and which version of a given component is currently deployed. -#### CRDs +### CRDs The CRD tab lists the custom resources that the GitOps Toolkit Controllers use. This allows you to see which resources you will be able to create. ![Flux Runtime view showing the various GitOps Toolkit controllers](/img/dashboard-flux-runtime-crd.png) + ## Moving On Now that we are familiar with the dashboard, let's [deploy a new application](./deploy-OSS.mdx) :sparkles:. diff --git a/website/static/img/dashboard-flux-runtime-terraform-crds.png b/website/static/img/dashboard-flux-runtime-terraform-crds.png new file mode 100644 index 0000000000000000000000000000000000000000..f0690c25f953c7a2269cc32c5a343a4416b3e6aa GIT binary patch literal 139442 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� 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 literal 0 HcmV?d00001 diff --git a/website/static/img/dashboard-flux-runtime-versions.png b/website/static/img/dashboard-flux-runtime-versions.png new file mode 100644 index 0000000000000000000000000000000000000000..aac3ac7ff675f99a9d3e5a127a7e20e39a413b2a GIT binary patch literal 145484 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 JPDHLkV1l;$zJmY& literal 0 HcmV?d00001 From c041d6909d77fa31247dd64321f3f8f5b71dba11 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Wed, 20 Dec 2023 17:54:48 +0100 Subject: [PATCH 29/43] review pr Signed-off-by: Eneko Fernandez --- ui/lib/nav.ts | 2 +- ui/pages/v2/FluxRuntime.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/lib/nav.ts b/ui/lib/nav.ts index ab783b52c9..53699f7d70 100644 --- a/ui/lib/nav.ts +++ b/ui/lib/nav.ts @@ -113,7 +113,7 @@ export const getParentNavRouteValue = ( const pageTitles = { [V2Routes.Automations]: "Applications", [V2Routes.Sources]: "Sources", - [V2Routes.FluxRuntime]: "Runtime", + [V2Routes.FluxRuntime]: "Flux Runtime", [V2Routes.Notifications]: "Notifications", [V2Routes.ImageAutomation]: "Image Automations", [V2Routes.ImagePolicies]: "Image Policies", diff --git a/ui/pages/v2/FluxRuntime.tsx b/ui/pages/v2/FluxRuntime.tsx index 039f52cc97..ac57059df7 100644 --- a/ui/pages/v2/FluxRuntime.tsx +++ b/ui/pages/v2/FluxRuntime.tsx @@ -20,7 +20,7 @@ function FluxRuntime({ className }: Props) { loading={isLoading || crdsLoading} error={error || crdsError} className={className} - path={[{ label: "Runtime" }]} + path={[{ label: "Flux Runtime" }]} > From 431c614800b649b0a5fc13fccb3daaa10ea0df2e Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Wed, 20 Dec 2023 17:58:22 +0100 Subject: [PATCH 30/43] review pr Signed-off-by: Eneko Fernandez --- tools/helm-values-dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/helm-values-dev.yaml b/tools/helm-values-dev.yaml index 26ad61eeda..1acc2923b3 100644 --- a/tools/helm-values-dev.yaml +++ b/tools/helm-values-dev.yaml @@ -30,7 +30,7 @@ envVars: - name: WEAVE_GITOPS_FEATURE_DEV_MODE value: "true" - name: WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME - value: "true" + value: "false" # Run the UI and API under /wego # additionalArgs: From 0d07ba261ebe41bcf0cd34b09374f63d3bfe8295 Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 22 Dec 2023 08:29:58 +0100 Subject: [PATCH 31/43] added documentation on how to decorate manifests Signed-off-by: Eneko Fernandez Signed-off-by: Eneko Fernandez --- charts/gitops-server/values.yaml | 2 +- .../getting-started/install-OSS.mdx | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/charts/gitops-server/values.yaml b/charts/gitops-server/values.yaml index d71a5d6e6a..2941090b9f 100644 --- a/charts/gitops-server/values.yaml +++ b/charts/gitops-server/values.yaml @@ -25,7 +25,7 @@ envVars: - name: WEAVE_GITOPS_FEATURE_CLUSTER value: "false" # -- Enable feature this feature flag if you want to expand Flux Runtime UI with other Weave GitOps components like Policy Agent or TF-Controller. - # Ensure that Weave GitOps Deployment and CRDs are labelled with Label 'app.kubernetes.io/part-of=weave-gitops'. + # Ensure that Weave GitOps Deployment and CRDs are labelled with Label 'app.kubernetes.io/part-of=weave-gitops'. See https://docs.gitops.weave.works/docs/open-source/getting-started/install-OSS/#optional-deploy-other-flux-and-weave-gitos-components for more info. - name: WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME value: "false" diff --git a/website/docs/open-source/getting-started/install-OSS.mdx b/website/docs/open-source/getting-started/install-OSS.mdx index eace089a2c..50c6cb3bef 100644 --- a/website/docs/open-source/getting-started/install-OSS.mdx +++ b/website/docs/open-source/getting-started/install-OSS.mdx @@ -223,6 +223,51 @@ You can use the Weave GitOps Helm Chart to customize your installation. Find the full Chart reference [here](../../references/helm-reference.md). ::: +### Optional Deploy Other Flux and Weave GitOs Components + +You could supercharge your Weave GitOps installation by deploying: + +- [Policy Agent:](../../../policy/intro/) for policy enforcement. [Flux Manifests Here](https://raw.githubusercontent.com/weaveworks/tf-controller/main/docs/release.yaml) +- [Tf-Controller:](../../../terraform/terraform-intro/) for infrastructure management. [Flux Manifests Here](https://raw.githubusercontent.com/weaveworks/policy-agent/dev/docs/examples/policy-agent-helmrelease.yaml) + +:::tip View them in the Runtime UI + +1. Enable the feature flag [`WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME`](../../references/helm-reference.md). +2. Leverage [Flux Post-Rendering](https://fluxcd.io/flux/components/helm/helmreleases/#post-renderers) to label Deployments and CRDs with `app.kubernetes.io/part-of: weave-gitops` + +A simplified example for Tf-Controller is: + +```yaml +apiVersion: helm.toolkit.fluxcd.io/v2beta2 +kind: HelmRelease +metadata: + name: tf-controller + namespace: flux-system +spec: + # ... striped for simlicity + interval: 1h0s + postRenderers: + - kustomize: + patchesStrategicMerge: + - apiVersion: apps/v1 + kind: Deployment + metadata: + labels: + app.kubernetes.io/part-of: weave-gitops + name: tf-controller + - apiVersion: apiextensions.k8s.io/v1 + kind: CustomResourceDefinition + metadata: + labels: + app.kubernetes.io/part-of: weave-gitops + name: terraforms.infra.contrib.fluxcd.io +``` + +Deploy it and see controllers and CRDs in the UI: + +![Runtime view showing Terraform CRDs](/img/dashboard-flux-runtime-terraform-crds.png) +::: + ## Next steps Now let's [explore the Weave GitOps Open Source UI](./ui-OSS.mdx). Then, we'll deploy an application. From 2b87e0eeac54842dbe96194923ba874562a3d7df Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 22 Dec 2023 08:48:20 +0100 Subject: [PATCH 32/43] some more docs refactoring Signed-off-by: Eneko Fernandez --- charts/gitops-server/values.yaml | 2 +- .../getting-started/install-OSS.mdx | 21 ++++++++++++------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/charts/gitops-server/values.yaml b/charts/gitops-server/values.yaml index 2941090b9f..8fb27f05c8 100644 --- a/charts/gitops-server/values.yaml +++ b/charts/gitops-server/values.yaml @@ -25,7 +25,7 @@ envVars: - name: WEAVE_GITOPS_FEATURE_CLUSTER value: "false" # -- Enable feature this feature flag if you want to expand Flux Runtime UI with other Weave GitOps components like Policy Agent or TF-Controller. - # Ensure that Weave GitOps Deployment and CRDs are labelled with Label 'app.kubernetes.io/part-of=weave-gitops'. See https://docs.gitops.weave.works/docs/open-source/getting-started/install-OSS/#optional-deploy-other-flux-and-weave-gitos-components for more info. + # Ensure that Weave GitOps Deployment and CRDs are labelled with Label 'app.kubernetes.io/part-of=weave-gitops'. See https://docs.gitops.weave.works/docs/open-source/getting-started/install-OSS for more info. - name: WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME value: "false" diff --git a/website/docs/open-source/getting-started/install-OSS.mdx b/website/docs/open-source/getting-started/install-OSS.mdx index 50c6cb3bef..32a3575a49 100644 --- a/website/docs/open-source/getting-started/install-OSS.mdx +++ b/website/docs/open-source/getting-started/install-OSS.mdx @@ -142,7 +142,7 @@ brew install weaveworks/tap/gitops -### Deploy Weave GitOps +## Install Weave GitOps In this section we will: @@ -223,19 +223,20 @@ You can use the Weave GitOps Helm Chart to customize your installation. Find the full Chart reference [here](../../references/helm-reference.md). ::: -### Optional Deploy Other Flux and Weave GitOs Components +## Supercharge your Weave GitOps -You could supercharge your Weave GitOps installation by deploying: +You could optionally unlock more from Weave GitOps by installing other components: -- [Policy Agent:](../../../policy/intro/) for policy enforcement. [Flux Manifests Here](https://raw.githubusercontent.com/weaveworks/tf-controller/main/docs/release.yaml) -- [Tf-Controller:](../../../terraform/terraform-intro/) for infrastructure management. [Flux Manifests Here](https://raw.githubusercontent.com/weaveworks/policy-agent/dev/docs/examples/policy-agent-helmrelease.yaml) +- [Policy Agent:](../../../policy/intro/) for policy enforcement. Use the Flux Manifests [Here](https://raw.githubusercontent.com/weaveworks/tf-controller/main/docs/release.yaml). +- [Tf-Controller:](../../../terraform/terraform-intro/) for infrastructure management. Use the Flux Manifests [Here](https://raw.githubusercontent.com/weaveworks/policy-agent/dev/docs/examples/policy-agent-helmrelease.yaml) -:::tip View them in the Runtime UI +### View them in the UI -1. Enable the feature flag [`WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME`](../../references/helm-reference.md). +1. Enable the feature flag [WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME](../../references/helm-reference.md). 2. Leverage [Flux Post-Rendering](https://fluxcd.io/flux/components/helm/helmreleases/#post-renderers) to label Deployments and CRDs with `app.kubernetes.io/part-of: weave-gitops` -A simplified example for Tf-Controller is: + +

Expand to see a simplified example for TF-controller ```yaml apiVersion: helm.toolkit.fluxcd.io/v2beta2 @@ -268,6 +269,10 @@ Deploy it and see controllers and CRDs in the UI: ![Runtime view showing Terraform CRDs](/img/dashboard-flux-runtime-terraform-crds.png) ::: + +
+ + ## Next steps Now let's [explore the Weave GitOps Open Source UI](./ui-OSS.mdx). Then, we'll deploy an application. From 05f8a4b60bd7b052eca30a67c11c06fe4aef71bc Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 22 Dec 2023 09:24:50 +0100 Subject: [PATCH 33/43] pr review Signed-off-by: Eneko Fernandez --- tools/helm-values-dev.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/helm-values-dev.yaml b/tools/helm-values-dev.yaml index 1acc2923b3..26ad61eeda 100644 --- a/tools/helm-values-dev.yaml +++ b/tools/helm-values-dev.yaml @@ -30,7 +30,7 @@ envVars: - name: WEAVE_GITOPS_FEATURE_DEV_MODE value: "true" - name: WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME - value: "false" + value: "true" # Run the UI and API under /wego # additionalArgs: From 276f873b7538ebb7429c9cf95af667a5f2e8a80b Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 22 Dec 2023 09:26:52 +0100 Subject: [PATCH 34/43] removed as not expected refactoring but replacement Signed-off-by: Eneko Fernandez --- ui/components/FluxRuntime.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/components/FluxRuntime.tsx b/ui/components/FluxRuntime.tsx index 5fe85cd013..4c7e40d670 100644 --- a/ui/components/FluxRuntime.tsx +++ b/ui/components/FluxRuntime.tsx @@ -30,7 +30,6 @@ const fluxVersionLabel = "app.kubernetes.io/version"; const partOfLabel = "app.kubernetes.io/part-of"; const fluxLabel = "flux"; -// FIXME: make it generic enough so it fits for both FluxRuntime or WeaveGitopsRuntime function FluxRuntime({ className, deployments, crds }: Props) { const { path } = useRouteMatch(); const tabs: Array = [ From 2168725f257226dfb33f7a4343396be95752fa1f Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 22 Dec 2023 09:33:42 +0100 Subject: [PATCH 35/43] removed multiple flux detection and test version logic for consistency between oss and ee Signed-off-by: Eneko Fernandez --- ui/components/FluxRuntime.tsx | 37 ++++++++--------------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/ui/components/FluxRuntime.tsx b/ui/components/FluxRuntime.tsx index 4c7e40d670..d3299bd250 100644 --- a/ui/components/FluxRuntime.tsx +++ b/ui/components/FluxRuntime.tsx @@ -8,18 +8,6 @@ import CrdsTable from "./CrdsTable"; import FluxVersionsTable, { FluxVersion } from "./FluxVersionsTable"; import { routeTab } from "./KustomizationDetail"; import SubRouterTabs, { RouterTab } from "./SubRouterTabs"; -import Text from "./Text"; - -const FluxVersionText = styled(Text)` - font-weight: 700; - margin-bottom: ${(props) => props.theme.spacing.medium}; - - span { - color: ${(props) => props.theme.colors.neutral40}; - font-weight: 400; - margin-left: ${(props) => props.theme.spacing.xs}; - } -`; type Props = { className?: string; @@ -65,27 +53,18 @@ function FluxRuntime({ className, deployments, crds }: Props) { } }); - const supportMultipleFlux = true; + tabs.unshift({ + name: "Flux Versions", + path: `${path}/flux`, + component: () => { + return ; + }, + visible: true, + }); - if (supportMultipleFlux) { - tabs.unshift({ - name: "Flux Versions", - path: `${path}/flux`, - component: () => { - return ; - }, - visible: true, - }); - } return ( <> - {!supportMultipleFlux && deployments[0]?.labels[fluxVersionLabel] && ( - - This cluster is running Flux version: - {deployments[0].labels[fluxVersionLabel]} - - )} {tabs.map( (subRoute, index) => From 49f0d58cf53a54f6c2beea19b7a64a739349ebf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Fern=C3=A1ndez?= <12957664+enekofb@users.noreply.github.com> Date: Fri, 22 Dec 2023 09:51:00 +0100 Subject: [PATCH 36/43] Apply suggestions from code review Co-authored-by: Olga Pudrovska --- charts/gitops-server/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/gitops-server/values.yaml b/charts/gitops-server/values.yaml index 8fb27f05c8..90075a4338 100644 --- a/charts/gitops-server/values.yaml +++ b/charts/gitops-server/values.yaml @@ -24,7 +24,7 @@ envVars: value: "true" - name: WEAVE_GITOPS_FEATURE_CLUSTER value: "false" - # -- Enable feature this feature flag if you want to expand Flux Runtime UI with other Weave GitOps components like Policy Agent or TF-Controller. + # -- Enable this feature flag if you want to expand Flux Runtime UI with other Weave GitOps components like Policy Agent or TF-Controller. # Ensure that Weave GitOps Deployment and CRDs are labelled with Label 'app.kubernetes.io/part-of=weave-gitops'. See https://docs.gitops.weave.works/docs/open-source/getting-started/install-OSS for more info. - name: WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME value: "false" From 306f51301c39c5172417517836d5429c0c63578a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Fern=C3=A1ndez?= <12957664+enekofb@users.noreply.github.com> Date: Fri, 22 Dec 2023 11:09:40 +0100 Subject: [PATCH 37/43] Update website/docs/open-source/getting-started/install-OSS.mdx Co-authored-by: Yiannis Triantafyllopoulos <8741709+yiannistri@users.noreply.github.com> --- website/docs/open-source/getting-started/install-OSS.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/open-source/getting-started/install-OSS.mdx b/website/docs/open-source/getting-started/install-OSS.mdx index 32a3575a49..062eefceae 100644 --- a/website/docs/open-source/getting-started/install-OSS.mdx +++ b/website/docs/open-source/getting-started/install-OSS.mdx @@ -245,7 +245,7 @@ metadata: name: tf-controller namespace: flux-system spec: - # ... striped for simlicity + # ... omitted for simplicity interval: 1h0s postRenderers: - kustomize: From 93d08fed0fe5403eef9f92666993e05c2a1c4ddf Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 22 Dec 2023 16:27:53 +0100 Subject: [PATCH 38/43] exporting runtime components Signed-off-by: Eneko Fernandez --- ui/index.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui/index.ts b/ui/index.ts index 236838c2a7..22d17a1294 100644 --- a/ui/index.ts +++ b/ui/index.ts @@ -97,6 +97,8 @@ import { useFeatureFlags } from "./hooks/featureflags"; import { useListFluxCrds, useListFluxRuntimeObjects, + useListRuntimeObjects, + useListRuntimeCrds, useToggleSuspend, } from "./hooks/flux"; import { useCheckCRDInstalled } from "./hooks/imageautomation"; @@ -139,6 +141,7 @@ import { withBasePath, } from "./lib/utils"; import SignIn from "./pages/SignIn"; +import Runtime from "./pages/v2/Runtime"; export { Alert, @@ -170,6 +173,7 @@ export { FluxObject, FluxObjectsTable, FluxRuntime, + Runtime, Footer, GitRepository, GitRepositoryDetail, @@ -279,6 +283,8 @@ export { useListEvents, useListFluxCrds, useListFluxRuntimeObjects, + useListRuntimeCrds, + useListRuntimeObjects, useListObjects, useListProviders, useListSources, From 62161d2653ddd5a42b3e2e53f8d9f195baf45908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Fern=C3=A1ndez?= <12957664+enekofb@users.noreply.github.com> Date: Fri, 22 Dec 2023 17:24:04 +0100 Subject: [PATCH 39/43] Update website/docs/open-source/getting-started/ui-OSS.mdx Co-authored-by: Olga Pudrovska --- website/docs/open-source/getting-started/ui-OSS.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/open-source/getting-started/ui-OSS.mdx b/website/docs/open-source/getting-started/ui-OSS.mdx index f71202c417..d19d597c21 100644 --- a/website/docs/open-source/getting-started/ui-OSS.mdx +++ b/website/docs/open-source/getting-started/ui-OSS.mdx @@ -164,7 +164,7 @@ view other Weave GitOps Runtime Components like Policy Agent or TF-Controller It comes with three tabs: 1. [Flux Versions](#flux-versions) that summaries the versions of flux running in your cluster. -2. [Controllers](#controllers): that shows your installed [GitOps Toolkit Controllers](https://fluxcd.io/flux/components/) and their version. +2. [Controllers](#controllers): that shows your installed [GitOps Toolkit Controllers](https://fluxcd.io/flux/components/) and their versions. 3. [CRDs](#crds): that shows your installed [GitOps Toolkit Custom Resources Definitions Toolkit Controllers](https://fluxcd.io/flux/components/) and their version. ### Flux Versions From 314a13d5835817e0848986fbc5513bc6db2653c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Fern=C3=A1ndez?= <12957664+enekofb@users.noreply.github.com> Date: Fri, 22 Dec 2023 17:25:50 +0100 Subject: [PATCH 40/43] Update website/docs/open-source/getting-started/ui-OSS.mdx Co-authored-by: Olga Pudrovska --- website/docs/open-source/getting-started/ui-OSS.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/open-source/getting-started/ui-OSS.mdx b/website/docs/open-source/getting-started/ui-OSS.mdx index d19d597c21..e75bc950a4 100644 --- a/website/docs/open-source/getting-started/ui-OSS.mdx +++ b/website/docs/open-source/getting-started/ui-OSS.mdx @@ -163,7 +163,7 @@ view other Weave GitOps Runtime Components like Policy Agent or TF-Controller It comes with three tabs: -1. [Flux Versions](#flux-versions) that summaries the versions of flux running in your cluster. +1. [Flux Versions](#flux-versions) that summarizes the versions of Flux running in your cluster. 2. [Controllers](#controllers): that shows your installed [GitOps Toolkit Controllers](https://fluxcd.io/flux/components/) and their versions. 3. [CRDs](#crds): that shows your installed [GitOps Toolkit Custom Resources Definitions Toolkit Controllers](https://fluxcd.io/flux/components/) and their version. From fa4a07be9ab72cf98815890aa58450d23c8523dd Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 22 Dec 2023 17:26:35 +0100 Subject: [PATCH 41/43] pr review comments Signed-off-by: Eneko Fernandez --- website/docs/open-source/getting-started/ui-OSS.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/open-source/getting-started/ui-OSS.mdx b/website/docs/open-source/getting-started/ui-OSS.mdx index e75bc950a4..d5ad5ccc32 100644 --- a/website/docs/open-source/getting-started/ui-OSS.mdx +++ b/website/docs/open-source/getting-started/ui-OSS.mdx @@ -165,7 +165,7 @@ It comes with three tabs: 1. [Flux Versions](#flux-versions) that summarizes the versions of Flux running in your cluster. 2. [Controllers](#controllers): that shows your installed [GitOps Toolkit Controllers](https://fluxcd.io/flux/components/) and their versions. -3. [CRDs](#crds): that shows your installed [GitOps Toolkit Custom Resources Definitions Toolkit Controllers](https://fluxcd.io/flux/components/) and their version. +3. [CRDs](#crds): that shows your installed [GitOps Toolkit Custom Resources Definitions](https://fluxcd.io/flux/components/) and their versions. ### Flux Versions From 302b9f3483e380a232f8ce982155301a6820019d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eneko=20Fern=C3=A1ndez?= <12957664+enekofb@users.noreply.github.com> Date: Fri, 22 Dec 2023 17:27:16 +0100 Subject: [PATCH 42/43] Update website/docs/open-source/getting-started/ui-OSS.mdx Co-authored-by: Olga Pudrovska --- website/docs/open-source/getting-started/ui-OSS.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/open-source/getting-started/ui-OSS.mdx b/website/docs/open-source/getting-started/ui-OSS.mdx index d5ad5ccc32..25bbc1a2b0 100644 --- a/website/docs/open-source/getting-started/ui-OSS.mdx +++ b/website/docs/open-source/getting-started/ui-OSS.mdx @@ -175,7 +175,7 @@ The Flux Versions tab shows your installed [Flux Versions](https://github.com/fl ### Controllers -The Controllers tab shows your installed [GitOps Toolkit Controllers](https://fluxcd.io/flux/components/) and their version. +The Controllers tab shows your installed [GitOps Toolkit Controllers](https://fluxcd.io/flux/components/) and their versions. ![Flux Runtime view showing the various GitOps Toolkit controllers](/img/dashboard-flux-runtime.png) From e1f470a995e4e8b17931a8569ea2e341eb46a3fb Mon Sep 17 00:00:00 2001 From: Eneko Fernandez Date: Fri, 22 Dec 2023 17:29:09 +0100 Subject: [PATCH 43/43] better wording Signed-off-by: Eneko Fernandez --- charts/gitops-server/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/gitops-server/values.yaml b/charts/gitops-server/values.yaml index 90075a4338..41570debdc 100644 --- a/charts/gitops-server/values.yaml +++ b/charts/gitops-server/values.yaml @@ -25,7 +25,7 @@ envVars: - name: WEAVE_GITOPS_FEATURE_CLUSTER value: "false" # -- Enable this feature flag if you want to expand Flux Runtime UI with other Weave GitOps components like Policy Agent or TF-Controller. - # Ensure that Weave GitOps Deployment and CRDs are labelled with Label 'app.kubernetes.io/part-of=weave-gitops'. See https://docs.gitops.weave.works/docs/open-source/getting-started/install-OSS for more info. + # Ensure that Weave GitOps Deployment and CRDs have the label 'app.kubernetes.io/part-of=weave-gitops'. See https://docs.gitops.weave.works/docs/open-source/getting-started/install-OSS for more info. - name: WEAVE_GITOPS_FEATURE_GITOPS_RUNTIME value: "false"