diff --git a/charts/kyverno-aws-adapter/templates/deployment.yaml b/charts/kyverno-aws-adapter/templates/deployment.yaml index 1c93d4b..b6af10c 100644 --- a/charts/kyverno-aws-adapter/templates/deployment.yaml +++ b/charts/kyverno-aws-adapter/templates/deployment.yaml @@ -26,6 +26,15 @@ spec: image: {{ include "kyverno-aws-adapter.image" . }} imagePullPolicy: {{ .Values.image.pullPolicy }} name: manager + env: + - name: ADAPTER_NAME + value: {{ include "kyverno-aws-adapter.fullname" . }} + - name: ADAPTER_NAMESPACE + value: {{ .Release.Namespace }} + - name: CLUSTER_NAME + value: {{ required "EKS cluster name is required" .Values.eksCluster.name }} + - name: CLUSTER_REGION + value: {{ required "EKS cluster region is required" .Values.eksCluster.region }} {{- if .Values.pollInterval }} args: - --sync-period={{ .Values.pollInterval }} diff --git a/charts/kyverno-aws-adapter/templates/resource.yaml b/charts/kyverno-aws-adapter/templates/resource.yaml deleted file mode 100644 index e05bb3c..0000000 --- a/charts/kyverno-aws-adapter/templates/resource.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: security.nirmata.io/v1alpha1 -kind: AWSAdapterConfig -metadata: - name: {{ include "kyverno-aws-adapter.fullname" . }} - labels: - {{- include "kyverno-aws-adapter.labels" . | nindent 4 }} -spec: - name: {{ required "EKS cluster name is required" .Values.eksCluster.name }} - region: {{ required "EKS cluster region is required" .Values.eksCluster.region }} diff --git a/controllers/awsadapterconfig_controller.go b/controllers/awsadapterconfig_controller.go index 5fd38e5..932ef0c 100644 --- a/controllers/awsadapterconfig_controller.go +++ b/controllers/awsadapterconfig_controller.go @@ -24,8 +24,10 @@ import ( "github.com/go-logr/logr" "github.com/google/go-cmp/cmp" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + apimachineryTypes "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -86,13 +88,13 @@ func (r *AWSAdapterConfigReconciler) Reconcile(ctx context.Context, req ctrl.Req } l.Info("Reconciling", "req", req) - l.Info("Loading AWS SDK config") + l.Info("Loading AWS Adapter config") cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion(*objOld.Spec.Region)) if err != nil { - l.Error(err, "error occurred while loading aws sdk config") - return r.updateLastPollStatusFailure(ctx, objOld, "error occurred while loading aws sdk config", err, &l, time.Now()) + l.Error(err, "error occurred while loading aws adapter config") + return r.updateLastPollStatusFailure(ctx, objOld, "error occurred while loading aws adapter config", err, &l, time.Now()) } - l.Info("AWS SDK config loaded successfully") + l.Info("AWS Adapter config loaded successfully") stsClient := sts.NewFromConfig(cfg) ec2Client := ec2.NewFromConfig(cfg) @@ -464,6 +466,31 @@ func (r *AWSAdapterConfigReconciler) updateLastPollStatusFailure(ctx context.Con return ctrl.Result{RequeueAfter: r.RequeueInterval}, nil } +func (r *AWSAdapterConfigReconciler) IsAWSAdapterConfigPresent(adapterName, adapterNamespace string) (bool, error) { + obj := &securityv1alpha1.AWSAdapterConfig{} + err := r.Get(context.TODO(), apimachineryTypes.NamespacedName{Namespace: adapterNamespace, Name: adapterName}, obj) + if err == nil { + return true, nil + } + if errors.IsNotFound(err) { + return false, nil + } + return false, err +} + +func (r *AWSAdapterConfigReconciler) CreateAWSAdapterConfig(clusterName, clusterRegion, adapterName, adapterNamespace string) error { + return r.Create(context.TODO(), &securityv1alpha1.AWSAdapterConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: adapterName, + Namespace: adapterNamespace, + }, + Spec: securityv1alpha1.AWSAdapterConfigSpec{ + Name: &clusterName, + Region: &clusterRegion, + }, + }) +} + func getAmi(ctx context.Context, ec2Client *ec2.Client, imageId *string) (*types.Image, error) { amis, err := ec2Client.DescribeImages(ctx, &ec2.DescribeImagesInput{ DryRun: aws.Bool(false), diff --git a/docs/getting_started.md b/docs/getting_started.md index afff270..4b0c2ec 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -163,3 +163,27 @@ kubectl get awsacfg -n nirmata-aws-adapter NAME ... CLUSTER NAME ... LAST POLLED STATUS kyverno-aws-adapter ... cluster-name ... success ``` + +### Uninstalling the AWS Adapter Helm chart + +To uninstall the AWS Adapter Helm chart, use the following command. + +```bash +helm uninstall kyverno-aws-adapter --namespace nirmata-aws-adapter +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +The `awsadapterconfigs.security.nirmata.io` CRD created by this chart is not removed by default and should be manually cleaned up. So, after uninstalling helm chart the following command can be used to remove the CRD. + +```bash +kubectl delete crd awsadapterconfigs.security.nirmata.io +``` + +### Deleting the AWSAdapterConfig + +The `AWSAdapterConfig` CR is not deleted by `helm uninstall` or by deleting the pod, and must be manually cleaned up. + +```bash +kubectl delete awsacfg kyverno-aws-adapter -n nirmata-aws-adapter +``` diff --git a/main.go b/main.go index b67972c..ebcbe9e 100644 --- a/main.go +++ b/main.go @@ -29,6 +29,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -92,11 +93,12 @@ func main() { os.Exit(1) } - if err = (&controllers.AWSAdapterConfigReconciler{ - Client: mgr.GetClient(), + r := &controllers.AWSAdapterConfigReconciler{ + Client: getClient(), Scheme: mgr.GetScheme(), RequeueInterval: time.Duration(syncPeriod) * time.Minute, - }).SetupWithManager(mgr); err != nil { + } + if err = r.SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "AWSAdapterConfig") os.Exit(1) } @@ -111,9 +113,82 @@ func main() { os.Exit(1) } + createAWSAdapterConfigIfNotPresent(r) + setupLog.Info("starting manager") if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { setupLog.Error(err, "problem running manager") os.Exit(1) } } + +func getClient() client.Client { + cl, err := client.New(ctrl.GetConfigOrDie(), client.Options{Scheme: scheme}) + if err != nil { + setupLog.Error(err, "unable to create client") + os.Exit(1) + } + return cl +} + +type requiredParams struct { + clusterName string + clusterRegion string + adapterName string + adapterNamespace string +} + +func (rp *requiredParams) areAllPresent() bool { + return rp.clusterName != "" && rp.clusterRegion != "" && rp.adapterName != "" && rp.adapterNamespace != "" +} + +func createAWSAdapterConfigIfNotPresent(r *controllers.AWSAdapterConfigReconciler) { + rp := requiredParams{ + clusterName: getClusterName(), + clusterRegion: getClusterRegion(), + adapterName: getAdapterName(), + adapterNamespace: getAdapterNamespace(), + } + + if rp.areAllPresent() { + setupLog.Info("One or more of the required parameters could not be found: clusterName='%s' clusterRegion='%s' adapterName='%s' adapterNamespace='%s'", rp.clusterName, rp.clusterRegion, rp.adapterName, rp.adapterNamespace) + return + } + + if isAWSAdapterConfigPresent, err := r.IsAWSAdapterConfigPresent(rp.adapterName, rp.adapterNamespace); err != nil { + setupLog.Error(err, "problem checking if AWS Adapter config exists") + os.Exit(1) + } else if isAWSAdapterConfigPresent { + setupLog.Info("AWS Adapter config already exists. Skipping resource creation.") + } else { + setupLog.Info("creating AWS Adapter config") + if err := r.CreateAWSAdapterConfig(rp.clusterName, rp.clusterRegion, rp.adapterName, rp.adapterNamespace); err != nil { + setupLog.Error(err, "unable to create AWS Adapter config") + os.Exit(1) + } + setupLog.Info("AWS Adapter config created successfully") + } +} + +const ( + ADAPTER_NAME_ENV_VAR = "ADAPTER_NAME" + ADAPTER_NAMESPACE_ENV_VAR = "ADAPTER_NAMESPACE" + CLUSTER_NAME_ENV_VAR = "CLUSTER_NAME" + CLUSTER_REGION_ENV_VAR = "CLUSTER_REGION" +) + +func getAdapterName() string { + return os.Getenv(ADAPTER_NAME_ENV_VAR) +} + +func getAdapterNamespace() string { + return os.Getenv(ADAPTER_NAMESPACE_ENV_VAR) +} + +func getClusterName() string { + return os.Getenv(CLUSTER_NAME_ENV_VAR) +} + +func getClusterRegion() string { + return os.Getenv(CLUSTER_REGION_ENV_VAR) +}