From 1eebd850c606322fff7ada6221234b265160bbba Mon Sep 17 00:00:00 2001 From: Spencer Whaley Date: Fri, 7 Jun 2024 11:07:47 -0700 Subject: [PATCH] Cherry-pick commit to make kube worker aware of Secret kube resource type Signed-off-by: Max McAdam --- kube_operator/api_objects.go | 91 +++++++++++++++++++++++++++++++++++- kube_operator/client.go | 15 ++++-- 2 files changed, 99 insertions(+), 7 deletions(-) diff --git a/kube_operator/api_objects.go b/kube_operator/api_objects.go index 567b8294c..d0ac518c8 100644 --- a/kube_operator/api_objects.go +++ b/kube_operator/api_objects.go @@ -4,6 +4,9 @@ import ( "context" "encoding/base64" "fmt" + "strings" + "time" + "github.com/golang/glog" "github.com/open-horizon/anax/config" "github.com/open-horizon/anax/cutil" @@ -20,8 +23,6 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" dynamic "k8s.io/client-go/dynamic" - "strings" - "time" ) type APIObjectInterface interface { @@ -114,6 +115,25 @@ func sortAPIObjects(allObjects []APIObjects, customResources map[string][]*unstr } else { return objMap, namespace, fmt.Errorf(kwlog(fmt.Sprintf("Error: rolebinding object has unrecognized type %T: %v", obj.Object, obj.Object))) } + case K8S_SECRET_TYPE: + if typedSecret, ok := obj.Object.(*corev1.Secret); ok { + if typedSecret.ObjectMeta.Namespace != "" { + if namespace == "" { + namespace = typedSecret.ObjectMeta.Namespace + } else if namespace != typedSecret.ObjectMeta.Namespace { + return objMap, namespace, fmt.Errorf(kwlog(fmt.Sprintf("Error: multiple namespaces specified in operator: %s and %s", namespace, typedSecret.ObjectMeta.Namespace))) + } + } + newSecret := SecretCoreV1{SecretObject: typedSecret} + if newSecret.Name() != "" { + glog.V(4).Infof(kwlog(fmt.Sprintf("Found kubernetes secret object %s.", newSecret.Name()))) + objMap[K8S_SECRET_TYPE] = append(objMap[K8S_SECRET_TYPE], newSecret) + } else { + return objMap, namespace, fmt.Errorf(kwlog(fmt.Sprintf("Error: secret object must have a name in its metadata section."))) + } + } else { + return objMap, namespace, fmt.Errorf(kwlog(fmt.Sprintf("Error: secret object has unrecognized type %T: %v", obj.Object, obj.Object))) + } case K8S_DEPLOYMENT_TYPE: if typedDeployment, ok := obj.Object.(*appsv1.Deployment); ok { if typedDeployment.ObjectMeta.Namespace != "" { @@ -623,6 +643,73 @@ func (sa ServiceAccountCoreV1) Namespace() string { return sa.ServiceAccountObject.ObjectMeta.Namespace } +// ----------------Secret---------------- +type SecretCoreV1 struct { + SecretObject *corev1.Secret +} + +func (s SecretCoreV1) Install(c KubeClient, namespace string) error { + glog.V(3).Infof(kwlog(fmt.Sprintf("attempting to create secret %v", s.Name()))) // Don't display the whole object to avoid logging secret data + if namespace != s.Namespace() { + glog.Warningf(kwlog(fmt.Sprintf("Embedded namespace '%v' is ignored. Service will be deployed to '%v'.", s.Namespace(), namespace))) + s.SecretObject.ObjectMeta.Namespace = namespace + } + + _, err := c.Client.CoreV1().Secrets(namespace).Create(context.Background(), s.SecretObject, metav1.CreateOptions{}) + if err != nil && errors.IsAlreadyExists(err) { + glog.Warningf(kwlog(fmt.Sprintf("Secret %s already exists, deleting and re-creating", s.Name()))) + s.Uninstall(c, s.Name()) + _, err = c.Client.CoreV1().Secrets(namespace).Create(context.Background(), s.SecretObject, metav1.CreateOptions{}) + } + if err != nil { + return fmt.Errorf(kwlog(fmt.Sprintf("Error creating the secret: %v", err))) + } + return nil +} + +func (s SecretCoreV1) Uninstall(c KubeClient, secretName string) { + glog.V(3).Infof(kwlog(fmt.Sprintf("deleting secret %v", secretName))) + err := c.Client.CoreV1().Secrets(s.Namespace()).Delete(context.Background(), secretName, metav1.DeleteOptions{}) + if err != nil { + glog.Errorf(kwlog(fmt.Sprintf("unable to delete secret %s. Error: %v", secretName, err))) + } +} + +func (s SecretCoreV1) Status(c KubeClient, namespace string) (interface{}, error) { + secretFromKube, err := c.Client.CoreV1().Secrets(namespace).Get(context.Background(), s.Name(), metav1.GetOptions{}) + if err != nil { + return nil, fmt.Errorf(kwlog(fmt.Sprintf("Error getting secret status: %v", err))) + } + return secretFromKube, nil +} + +func (s SecretCoreV1) Update(c KubeClient, namespace string) error { + if secretFromKube, err := c.Client.CoreV1().Secrets(namespace).Get(context.Background(), s.SecretObject.Name, metav1.GetOptions{}); err != nil { + return fmt.Errorf(kwlog(fmt.Sprintf("Error getting secret from kube: %v", err))) + } else if secretFromKube == nil { + // invalid, return err + return fmt.Errorf(kwlog(fmt.Sprintf("Error updating secret %v in namespace %v: secret doesn't exist", s.SecretObject.Name, namespace))) + } else { + //Update the secret retrieved from kube with the new data + secretFromKube.Data = s.SecretObject.Data + updatedSecret, err := c.Client.CoreV1().Secrets(namespace).Update(context.Background(), secretFromKube, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf(kwlog(fmt.Sprintf("Error updating secret %v in namespace %v: %v", s.SecretObject.Name, namespace, err))) + } + glog.V(3).Infof(kwlog(fmt.Sprintf("Secret %v in namespace %v updated successfully", updatedSecret.Name, namespace))) + } + + return nil +} + +func (s SecretCoreV1) Name() string { + return s.SecretObject.ObjectMeta.Name +} + +func (s SecretCoreV1) Namespace() string { + return s.SecretObject.ObjectMeta.Namespace +} + //----------------Deployment---------------- // The deployment object includes the environment variable config map and vault secret as k8s secret diff --git a/kube_operator/client.go b/kube_operator/client.go index b986b5836..361239fd9 100644 --- a/kube_operator/client.go +++ b/kube_operator/client.go @@ -6,6 +6,11 @@ import ( "context" "encoding/base64" "fmt" + "io" + "io/ioutil" + "reflect" + "strings" + "github.com/golang/glog" "github.com/open-horizon/anax/config" "github.com/open-horizon/anax/cutil" @@ -15,8 +20,6 @@ import ( olmv1client "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/typed/operators/v1" olmv1alpha1client "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/typed/operators/v1alpha1" yaml "gopkg.in/yaml.v2" - "io" - "io/ioutil" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -31,8 +34,6 @@ import ( dynamic "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" - "reflect" - "strings" ) const ( @@ -54,12 +55,16 @@ const ( K8S_SERVICEACCOUNT_TYPE = "ServiceAccount" K8S_CRD_TYPE = "CustomResourceDefinition" K8S_NAMESPACE_TYPE = "Namespace" + K8S_SECRET_TYPE = "Secret" K8S_UNSTRUCTURED_TYPE = "Unstructured" K8S_OLM_OPERATOR_GROUP_TYPE = "OperatorGroup" ) +// Order is important here since it will be used to determine install order. +// For example, secrets should be before deployments, +// because it may be an image pull secret used by the deployment func getBaseK8sKinds() []string { - return []string{K8S_NAMESPACE_TYPE, K8S_CLUSTER_ROLE_TYPE, K8S_CLUSTER_ROLEBINDING_TYPE, K8S_ROLE_TYPE, K8S_ROLEBINDING_TYPE, K8S_DEPLOYMENT_TYPE, K8S_SERVICEACCOUNT_TYPE, K8S_CRD_TYPE} + return []string{K8S_NAMESPACE_TYPE, K8S_CLUSTER_ROLE_TYPE, K8S_CLUSTER_ROLEBINDING_TYPE, K8S_ROLE_TYPE, K8S_ROLEBINDING_TYPE, K8S_SERVICEACCOUNT_TYPE, K8S_SECRET_TYPE, K8S_CRD_TYPE, K8S_DEPLOYMENT_TYPE} } func getDangerKinds() []string {