diff --git a/internal/controller/basic_authenticator/basicauthenticator_controller.go b/internal/controller/basic_authenticator/basicauthenticator_controller.go index 7b75f12..2b818cb 100644 --- a/internal/controller/basic_authenticator/basicauthenticator_controller.go +++ b/internal/controller/basic_authenticator/basicauthenticator_controller.go @@ -18,14 +18,19 @@ package basic_authenticator import ( "context" + "github.com/go-logr/logr" authenticatorv1alpha1 "github.com/snapp-incubator/simple-authenticator/api/v1alpha1" "github.com/snapp-incubator/simple-authenticator/internal/config" appv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" ) // BasicAuthenticatorReconciler reconciles a BasicAuthenticator object @@ -33,6 +38,7 @@ type BasicAuthenticatorReconciler struct { client.Client Scheme *runtime.Scheme CustomConfig *config.CustomConfig + logger logr.Logger } //+kubebuilder:rbac:groups=authenticator.snappcloud.io,resources=basicauthenticators,verbs=get;list;watch;create;update;patch;delete @@ -41,11 +47,12 @@ type BasicAuthenticatorReconciler struct { //+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete func (r *BasicAuthenticatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - logger := log.FromContext(ctx) - logger.Info("reconcile triggered") - logger.Info(req.String()) + r.logger = log.FromContext(ctx) + r.logger.Info("reconcile triggered") + r.logger.Info(req.String()) return r.Provision(ctx, req) } @@ -56,5 +63,29 @@ func (r *BasicAuthenticatorReconciler) SetupWithManager(mgr ctrl.Manager) error Owns(&appv1.Deployment{}). Owns(&corev1.ConfigMap{}). Owns(&corev1.Secret{}). + Watches( + &source.Kind{Type: &appv1.Deployment{}}, + handler.EnqueueRequestsFromMapFunc(r.findExternallyManagedDeployments), + ). Complete(r) } + +func (r *BasicAuthenticatorReconciler) findExternallyManagedDeployments(deployment client.Object) []reconcile.Request { + deploy, ok := deployment.(*appv1.Deployment) + if !ok { + return nil + } + if deploy.ObjectMeta.Annotations == nil { + return nil + } + basicAuthName, exists := deploy.ObjectMeta.Annotations[ExternallyManaged] + if !exists { + return nil + } + + return []reconcile.Request{ + { + NamespacedName: types.NamespacedName{Name: basicAuthName, Namespace: deploy.Namespace}, + }, + } +} diff --git a/internal/controller/basic_authenticator/const.go b/internal/controller/basic_authenticator/const.go index 3d0a956..7a29a15 100644 --- a/internal/controller/basic_authenticator/const.go +++ b/internal/controller/basic_authenticator/const.go @@ -5,5 +5,6 @@ const ( nginxDefaultContainerName = "nginx" SecretAnnotation = "authenticator.snappcloud.io/secret.name" ConfigmapAnnotation = "authenticator.snappcloud.io/configmap.name" - basicAuthenticatorFinalizer = "basicauthenticators.authenticator.snappcloud.io/finalizer" + basicAuthenticatorFinalizer = "basicauthenticator.snappcloud.io/finalizer" + ExternallyManaged = "basicauthenticator.snappcloud.io/externally.managed" ) diff --git a/internal/controller/basic_authenticator/nginx.go b/internal/controller/basic_authenticator/nginx.go index da00ba3..4c8abd4 100644 --- a/internal/controller/basic_authenticator/nginx.go +++ b/internal/controller/basic_authenticator/nginx.go @@ -2,11 +2,11 @@ package basic_authenticator import ( "context" - "crypto/sha256" - "encoding/hex" "fmt" + "github.com/pkg/errors" "github.com/snapp-incubator/simple-authenticator/api/v1alpha1" - "github.com/snapp-incubator/simple-authenticator/pkg/hash" + "github.com/snapp-incubator/simple-authenticator/internal/config" + "github.com/snapp-incubator/simple-authenticator/pkg/random_generator" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -36,37 +36,30 @@ const ( ) // TODO: come up with better name that "nginx" -func (r *BasicAuthenticatorReconciler) CreateNginxDeployment(basicAuthenticator *v1alpha1.BasicAuthenticator, configMapName string, credentialName string) *appsv1.Deployment { - nginxImageAddress := nginxDefaultImageAddress - if r.CustomConfig != nil && r.CustomConfig.WebserverConf.Image != "" { - nginxImageAddress = r.CustomConfig.WebserverConf.Image - } - - nginxContainerName := nginxDefaultContainerName - if r.CustomConfig != nil && r.CustomConfig.WebserverConf.ContainerName != "" { - nginxContainerName = r.CustomConfig.WebserverConf.ContainerName - } +func createNginxDeployment(basicAuthenticator *v1alpha1.BasicAuthenticator, configMapName string, credentialName string, customConfig *config.CustomConfig) *appsv1.Deployment { + nginxImageAddress := getNginxContainerImage(customConfig) + nginxContainerName := getNginxContainerName(customConfig) - deploymentName := GenerateRandomName(basicAuthenticator.Name, "deployment") + deploymentName := random_generator.GenerateRandomName(basicAuthenticator.Name, "deployment") replicas := int32(basicAuthenticator.Spec.Replicas) authenticatorPort := int32(basicAuthenticator.Spec.AuthenticatorPort) - labels := map[string]string{"app": deploymentName} + basicAuthLabels := map[string]string{"app": deploymentName} //TODO: mount secret as volume deploy := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: deploymentName, Namespace: basicAuthenticator.Namespace, - Labels: labels, + Labels: basicAuthLabels, }, Spec: appsv1.DeploymentSpec{ Replicas: &replicas, - Selector: &metav1.LabelSelector{MatchLabels: labels}, + Selector: &metav1.LabelSelector{MatchLabels: basicAuthLabels}, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Name: deploymentName, - Labels: labels, + Labels: basicAuthLabels, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ @@ -118,12 +111,12 @@ func (r *BasicAuthenticatorReconciler) CreateNginxDeployment(basicAuthenticator return deploy } -func (r *BasicAuthenticatorReconciler) CreateNginxConfigmap(basicAuthenticator *v1alpha1.BasicAuthenticator) *corev1.ConfigMap { - configmapName := GenerateRandomName(basicAuthenticator.Name, "configmap") - labels := map[string]string{ +func createNginxConfigmap(basicAuthenticator *v1alpha1.BasicAuthenticator) *corev1.ConfigMap { + configmapName := random_generator.GenerateRandomName(basicAuthenticator.Name, "configmap") + basicAuthLabels := map[string]string{ "app": basicAuthenticator.Name, } - nginxConf := FillTemplate(template, SecretMountPath, basicAuthenticator) + nginxConf := fillTemplate(template, SecretMountPath, basicAuthenticator) data := map[string]string{ "nginx.conf": nginxConf, } @@ -131,17 +124,28 @@ func (r *BasicAuthenticatorReconciler) CreateNginxConfigmap(basicAuthenticator * ObjectMeta: metav1.ObjectMeta{ Name: configmapName, Namespace: basicAuthenticator.Namespace, - Labels: labels, + Labels: basicAuthLabels, }, Data: data, } return configMap } -func (r *BasicAuthenticatorReconciler) CreateCredentials(basicAuthenticator *v1alpha1.BasicAuthenticator) *corev1.Secret { - username, password := hash.GenerateRandomString(20), hash.GenerateRandomString(20) +func createCredentials(basicAuthenticator *v1alpha1.BasicAuthenticator) (*corev1.Secret, error) { + username, err := random_generator.GenerateRandomString(20) + if err != nil { + return nil, errors.Wrap(err, "failed to generate username") + } + password, err := random_generator.GenerateRandomString(20) + if err != nil { + return nil, errors.Wrap(err, "failed to generate password") + } htpasswdString := fmt.Sprintf("%s:%s", username, password) - secretName := GenerateRandomName(basicAuthenticator.Name, hash.GenerateRandomString(10)) + salt, err := random_generator.GenerateRandomString(10) + if err != nil { + return nil, errors.Wrap(err, "failed to generate salt") + } + secretName := random_generator.GenerateRandomName(basicAuthenticator.Name, salt) secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretName, @@ -151,22 +155,16 @@ func (r *BasicAuthenticatorReconciler) CreateCredentials(basicAuthenticator *v1a ".htpasswd": htpasswdString, }, } - return secret + return secret, nil } -func (r *BasicAuthenticatorReconciler) Injector(ctx context.Context, basicAuthenticator *v1alpha1.BasicAuthenticator, configMapName string, credentialName string) ([]*appsv1.Deployment, error) { - nginxImageAddress := nginxDefaultImageAddress - if r.CustomConfig != nil && r.CustomConfig.WebserverConf.Image != "" { - nginxImageAddress = r.CustomConfig.WebserverConf.Image - } +func injector(ctx context.Context, basicAuthenticator *v1alpha1.BasicAuthenticator, configMapName string, credentialName string, customConfig *config.CustomConfig, k8Client client.Client) ([]*appsv1.Deployment, error) { + nginxImageAddress := getNginxContainerImage(customConfig) + nginxContainerName := getNginxContainerName(customConfig) - nginxContainerName := nginxDefaultContainerName - if r.CustomConfig != nil && r.CustomConfig.WebserverConf.ContainerName != "" { - nginxContainerName = r.CustomConfig.WebserverConf.ContainerName - } authenticatorPort := int32(basicAuthenticator.Spec.AuthenticatorPort) var deploymentList appsv1.DeploymentList - if err := r.Client.List( + if err := k8Client.List( ctx, &deploymentList, client.MatchingLabelsSelector{Selector: labels.SelectorFromSet(basicAuthenticator.Spec.Selector.MatchLabels)}, @@ -227,7 +225,7 @@ func (r *BasicAuthenticatorReconciler) Injector(ctx context.Context, basicAuthen return resultDeployments, nil } -func FillTemplate(template string, secretPath string, authenticator *v1alpha1.BasicAuthenticator) string { +func fillTemplate(template string, secretPath string, authenticator *v1alpha1.BasicAuthenticator) string { var result string var appservice string if authenticator.Spec.Type == "sidecar" { @@ -241,10 +239,3 @@ func FillTemplate(template string, secretPath string, authenticator *v1alpha1.Ba result = strings.Replace(result, "APP_PORT", fmt.Sprintf("%d", authenticator.Spec.AppPort), 1) return result } - -func GenerateRandomName(baseName string, salt string) string { - tuple := fmt.Sprintf("%s-%s", baseName, salt) - sum := sha256.Sum256([]byte(tuple)) - subByte := sum[:8] - return fmt.Sprintf("%s-%s", baseName, hex.EncodeToString(subByte)) -} diff --git a/internal/controller/basic_authenticator/provision.go b/internal/controller/basic_authenticator/provision.go index 96620b9..04f1770 100644 --- a/internal/controller/basic_authenticator/provision.go +++ b/internal/controller/basic_authenticator/provision.go @@ -9,9 +9,10 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" + "math" "reflect" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/client" ) // Provision provisions the required resources for the basicAuthenticator object @@ -32,38 +33,44 @@ func (r *BasicAuthenticatorReconciler) Provision(ctx context.Context, req ctrl.R return subreconciler.Evaluate(subreconciler.DoNotRequeue()) } -func (r *BasicAuthenticatorReconciler) GetLatestBasicAuthenticator(ctx context.Context, req ctrl.Request, basicAuthenticator *v1alpha1.BasicAuthenticator) (*ctrl.Result, error) { - logger := log.FromContext(ctx) +func (r *BasicAuthenticatorReconciler) getLatestBasicAuthenticator(ctx context.Context, req ctrl.Request, basicAuthenticator *v1alpha1.BasicAuthenticator) (*ctrl.Result, error) { err := r.Get(ctx, req.NamespacedName, basicAuthenticator) if err != nil { if errors.IsNotFound(err) { - logger.Info("Resource not found. Ignoring since object must be deleted") + r.logger.Info("Resource not found. Ignoring since object must be deleted") return subreconciler.DoNotRequeue() } - logger.Error(err, "Failed to get BasicAuthenticator") + r.logger.Error(err, "Failed to get BasicAuthenticator") return subreconciler.RequeueWithError(err) } return subreconciler.ContinueReconciling() } func (r *BasicAuthenticatorReconciler) ensureSecret(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { - logger := log.FromContext(ctx) basicAuthenticator := &v1alpha1.BasicAuthenticator{} - if r, err := r.GetLatestBasicAuthenticator(ctx, req, basicAuthenticator); subreconciler.ShouldHaltOrRequeue(r, err) { + if r, err := r.getLatestBasicAuthenticator(ctx, req, basicAuthenticator); subreconciler.ShouldHaltOrRequeue(r, err) { return r, err } credentialName := basicAuthenticator.Spec.CredentialsSecretRef var credentialSecret corev1.Secret if credentialName == "" { //create secret - newSecret := r.CreateCredentials(basicAuthenticator) - err := r.Get(ctx, types.NamespacedName{Name: newSecret.Name, Namespace: newSecret.Namespace}, &credentialSecret) + newSecret, err := createCredentials(basicAuthenticator) + if err != nil { + r.logger.Error(err, "failed to create credentials") + return subreconciler.RequeueWithError(err) + } + err = r.Get(ctx, types.NamespacedName{Name: newSecret.Name, Namespace: newSecret.Namespace}, &credentialSecret) if errors.IsNotFound(err) { + if err := ctrl.SetControllerReference(basicAuthenticator, newSecret, r.Scheme); err != nil { + r.logger.Error(err, "failed to set secret owner") + return subreconciler.RequeueWithError(err) + } // update basic auth err := r.Create(ctx, newSecret) if err != nil { - logger.Error(err, "failed to create new secret") + r.logger.Error(err, "failed to create new secret") return subreconciler.RequeueWithError(err) } @@ -75,7 +82,7 @@ func (r *BasicAuthenticatorReconciler) ensureSecret(ctx context.Context, req ctr assignAnnotation(basicAuthenticator, SecretAnnotation, credentialName) err = r.Update(ctx, basicAuthenticator) if err != nil { - logger.Error(err, "failed to updated basic authenticator") + r.logger.Error(err, "failed to updated basic authenticator") return subreconciler.RequeueWithError(err) } @@ -85,7 +92,7 @@ func (r *BasicAuthenticatorReconciler) ensureSecret(ctx context.Context, req ctr } else { err := r.Get(ctx, types.NamespacedName{Name: credentialName, Namespace: basicAuthenticator.Namespace}, &credentialSecret) if err != nil { - logger.Error(err, "failed to fetch secret") + r.logger.Error(err, "failed to fetch secret") return subreconciler.RequeueWithError(err) } @@ -93,7 +100,7 @@ func (r *BasicAuthenticatorReconciler) ensureSecret(ctx context.Context, req ctr assignAnnotation(basicAuthenticator, SecretAnnotation, credentialName) err = r.Update(ctx, basicAuthenticator) if err != nil { - logger.Error(err, "failed to updated basic authenticator") + r.logger.Error(err, "failed to updated basic authenticator") return subreconciler.RequeueWithError(err) } } @@ -101,24 +108,23 @@ func (r *BasicAuthenticatorReconciler) ensureSecret(ctx context.Context, req ctr } func (r *BasicAuthenticatorReconciler) ensureConfigmap(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { - logger := log.FromContext(ctx) basicAuthenticator := &v1alpha1.BasicAuthenticator{} - if r, err := r.GetLatestBasicAuthenticator(ctx, req, basicAuthenticator); subreconciler.ShouldHaltOrRequeue(r, err) { + if r, err := r.getLatestBasicAuthenticator(ctx, req, basicAuthenticator); subreconciler.ShouldHaltOrRequeue(r, err) { return r, err } - authenticatorConfig := r.CreateNginxConfigmap(basicAuthenticator) + authenticatorConfig := createNginxConfigmap(basicAuthenticator) var foundConfigmap corev1.ConfigMap err := r.Get(ctx, types.NamespacedName{Name: authenticatorConfig.Name, Namespace: basicAuthenticator.Namespace}, &foundConfigmap) if errors.IsNotFound(err) { if err := ctrl.SetControllerReference(basicAuthenticator, authenticatorConfig, r.Scheme); err != nil { - logger.Error(err, "failed to set configmap owner") + r.logger.Error(err, "failed to set configmap owner") return subreconciler.RequeueWithError(err) } err := r.Create(ctx, authenticatorConfig) if err != nil { - logger.Error(err, "failed to create new configmap") + r.logger.Error(err, "failed to create new configmap") return subreconciler.RequeueWithError(err) } //saving secretName inorder to be used in next steps @@ -126,27 +132,27 @@ func (r *BasicAuthenticatorReconciler) ensureConfigmap(ctx context.Context, req err = r.Update(ctx, basicAuthenticator) if err != nil { - logger.Error(err, "failed to updated basic authenticator") + r.logger.Error(err, "failed to updated basic authenticator") return subreconciler.RequeueWithError(err) } return subreconciler.Requeue() } else if err != nil { - logger.Error(err, "failed to fetch configmap") + r.logger.Error(err, "failed to fetch configmap") return subreconciler.RequeueWithError(err) } else { if !reflect.DeepEqual(authenticatorConfig.Data, foundConfigmap.Data) { - logger.Info("updating configmap") + r.logger.Info("updating configmap") foundConfigmap.Data = authenticatorConfig.Data err := r.Update(ctx, &foundConfigmap) if err != nil { - logger.Error(err, "failed to update configmap") + r.logger.Error(err, "failed to update configmap") return subreconciler.RequeueWithError(err) } } assignAnnotation(basicAuthenticator, ConfigmapAnnotation, authenticatorConfig.Name) err = r.Update(ctx, basicAuthenticator) if err != nil { - logger.Error(err, "failed to updated basic authenticator") + r.logger.Error(err, "failed to updated basic authenticator") return subreconciler.RequeueWithError(err) } } @@ -155,10 +161,9 @@ func (r *BasicAuthenticatorReconciler) ensureConfigmap(ctx context.Context, req } func (r *BasicAuthenticatorReconciler) ensureDeployment(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { - _ = log.FromContext(ctx) basicAuthenticator := &v1alpha1.BasicAuthenticator{} - if r, err := r.GetLatestBasicAuthenticator(ctx, req, basicAuthenticator); subreconciler.ShouldHaltOrRequeue(r, err) { + if r, err := r.getLatestBasicAuthenticator(ctx, req, basicAuthenticator); subreconciler.ShouldHaltOrRequeue(r, err) { return r, err } if basicAuthenticator.ObjectMeta.Annotations == nil { @@ -177,77 +182,140 @@ func (r *BasicAuthenticatorReconciler) ensureDeployment(ctx context.Context, req //Deciding to create sidecar injection or create deployment isSidecar := basicAuthenticator.Spec.Type == "sidecar" if isSidecar { - return r.CreateSidecarAuthenticator(ctx, req, basicAuthenticator, authenticatorConfigName, secretName) + return r.createSidecarAuthenticator(ctx, req, basicAuthenticator, authenticatorConfigName, secretName) } else { - return r.CreateDeploymentAuthenticator(ctx, req, basicAuthenticator, authenticatorConfigName, secretName) + return r.createDeploymentAuthenticator(ctx, req, basicAuthenticator, authenticatorConfigName, secretName) } } -func (r *BasicAuthenticatorReconciler) CreateDeploymentAuthenticator(ctx context.Context, req ctrl.Request, basicAuthenticator *v1alpha1.BasicAuthenticator, authenticatorConfigName, secretName string) (*ctrl.Result, error) { - logger := log.FromContext(ctx) +func (r *BasicAuthenticatorReconciler) createDeploymentAuthenticator(ctx context.Context, req ctrl.Request, basicAuthenticator *v1alpha1.BasicAuthenticator, authenticatorConfigName, secretName string) (*ctrl.Result, error) { - newDeployment := r.CreateNginxDeployment(basicAuthenticator, authenticatorConfigName, secretName) + newDeployment := createNginxDeployment(basicAuthenticator, authenticatorConfigName, secretName, r.CustomConfig) foundDeployment := &appv1.Deployment{} err := r.Get(ctx, types.NamespacedName{Name: newDeployment.Name, Namespace: basicAuthenticator.Namespace}, foundDeployment) if errors.IsNotFound(err) { if err := ctrl.SetControllerReference(basicAuthenticator, newDeployment, r.Scheme); err != nil { - logger.Error(err, "failed to set deployment owner") + r.logger.Error(err, "failed to set deployment owner") return subreconciler.RequeueWithError(err) } + if basicAuthenticator.Spec.AdaptiveScale && basicAuthenticator.Spec.AppService != "" { + replica, err := r.acquireTargetReplica(ctx, basicAuthenticator) + if err != nil { + r.logger.Error(err, "failed to acquire target replica using adaptiveScale") + return subreconciler.RequeueWithError(err) + } + newDeployment.Spec.Replicas = &replica + } + //create deployment err := r.Create(ctx, newDeployment) if err != nil { - logger.Error(err, "failed to create new deployment") + r.logger.Error(err, "failed to create new deployment") return subreconciler.RequeueWithError(err) } - logger.Info("created deployment") + err = r.Get(ctx, types.NamespacedName{Name: foundDeployment.Name, Namespace: basicAuthenticator.Namespace}, foundDeployment) + if err != nil { + r.logger.Error(err, "failed to refetch") + return subreconciler.RequeueWithError(err) + } + r.logger.Info("created deployment") return subreconciler.Requeue() } else if err != nil { if err != nil { - logger.Error(err, "failed to fetch deployment") + r.logger.Error(err, "failed to fetch deployment") return subreconciler.RequeueWithError(err) } } else { //update deployment + targetReplica := newDeployment.Spec.Replicas + if basicAuthenticator.Spec.AdaptiveScale && basicAuthenticator.Spec.AppService != "" { + replica, err := r.acquireTargetReplica(ctx, basicAuthenticator) + if err != nil { + r.logger.Error(err, "failed to acquire target replica using adaptiveScale") + } + targetReplica = &replica + } if !reflect.DeepEqual(newDeployment.Spec, foundDeployment.Spec) { - logger.Info("updating deployment") + r.logger.Info("updating deployment") + foundDeployment.Spec = newDeployment.Spec + foundDeployment.Spec.Replicas = targetReplica + err := r.Update(ctx, foundDeployment) if err != nil { - logger.Error(err, "failed to update deployment") + r.logger.Error(err, "failed to update deployment") return subreconciler.RequeueWithError(err) } + err = r.Get(ctx, types.NamespacedName{Name: foundDeployment.Name, Namespace: basicAuthenticator.Namespace}, foundDeployment) + if err != nil { + r.logger.Error(err, "failed to refetch") + return subreconciler.RequeueWithError(err) + } + } - logger.Info("updating ready replicas") + r.logger.Info("updating ready replicas") basicAuthenticator.Status.ReadyReplicas = int(foundDeployment.Status.ReadyReplicas) err := r.Status().Update(ctx, basicAuthenticator) if err != nil { - logger.Error(err, "failed to update basic authenticator status") + r.logger.Error(err, "failed to update basic authenticator status") return subreconciler.RequeueWithError(err) } } return subreconciler.ContinueReconciling() } -func (r *BasicAuthenticatorReconciler) CreateSidecarAuthenticator(ctx context.Context, req ctrl.Request, basicAuthenticator *v1alpha1.BasicAuthenticator, authenticatorConfigName, secretName string) (*ctrl.Result, error) { - logger := log.FromContext(ctx) - deploymentsToUpdate, err := r.Injector(ctx, basicAuthenticator, authenticatorConfigName, secretName) +func (r *BasicAuthenticatorReconciler) createSidecarAuthenticator(ctx context.Context, req ctrl.Request, basicAuthenticator *v1alpha1.BasicAuthenticator, authenticatorConfigName, secretName string) (*ctrl.Result, error) { + deploymentsToUpdate, err := injector(ctx, basicAuthenticator, authenticatorConfigName, secretName, r.CustomConfig, r.Client) if err != nil { - logger.Error(err, "failed to inject into deployments") + r.logger.Error(err, "failed to inject into deployments") return subreconciler.RequeueWithError(err) } for _, deploy := range deploymentsToUpdate { if err := ctrl.SetControllerReference(basicAuthenticator, deploy, r.Scheme); err != nil { - logger.Error(err, "failed to set injected deployment owner") + r.logger.Error(err, "failed to set injected deployment owner") return subreconciler.RequeueWithError(err) } err := r.Update(ctx, deploy) if err != nil { - logger.Error(err, "failed to update injected deployments") + r.logger.Error(err, "failed to update injected deployments") return subreconciler.RequeueWithError(err) } } return subreconciler.ContinueReconciling() } + +func (r *BasicAuthenticatorReconciler) acquireTargetReplica(ctx context.Context, basicAuthenticator *v1alpha1.BasicAuthenticator) (int32, error) { + var targetService corev1.Service + // service should be in same ns with basic auth + if err := r.Get(ctx, types.NamespacedName{Name: basicAuthenticator.Spec.AppService, Namespace: basicAuthenticator.ObjectMeta.Namespace}, &targetService); err != nil { + return -1, err + } + labelSelector := targetService.Spec.Selector + + deployments := &appv1.DeploymentList{} + if err := r.List(ctx, deployments, client.MatchingLabels(labelSelector)); err != nil { + return -1, err + } + + if len(deployments.Items) == 0 { + return -1, defaultError.New("no deployment is selected by appService") + } + + targetDeploy := deployments.Items[0] //we expect it to be single deployment + if targetDeploy.ObjectMeta.Annotations == nil { + targetDeploy.ObjectMeta.Annotations = make(map[string]string) + } + + targetDeploy.ObjectMeta.Annotations[ExternallyManaged] = basicAuthenticator.Name + + err := r.Update(ctx, &targetDeploy) + if err != nil { + return -1, err + } + replicas := deployments.Items[0].Spec.Replicas + targetReplica := math.Floor(float64((*replicas + 1) / 2)) + + return int32(targetReplica), nil +} diff --git a/internal/controller/basic_authenticator/utility.go b/internal/controller/basic_authenticator/utility.go index 83ee572..92ca173 100644 --- a/internal/controller/basic_authenticator/utility.go +++ b/internal/controller/basic_authenticator/utility.go @@ -1,6 +1,9 @@ package basic_authenticator -import "github.com/snapp-incubator/simple-authenticator/api/v1alpha1" +import ( + "github.com/snapp-incubator/simple-authenticator/api/v1alpha1" + "github.com/snapp-incubator/simple-authenticator/internal/config" +) func assignAnnotation(authenticator *v1alpha1.BasicAuthenticator, key, value string) { if authenticator.ObjectMeta.Annotations == nil { @@ -8,3 +11,17 @@ func assignAnnotation(authenticator *v1alpha1.BasicAuthenticator, key, value str } authenticator.ObjectMeta.Annotations[key] = value } + +func getNginxContainerImage(customConfig *config.CustomConfig) string { + + if customConfig != nil && customConfig.WebserverConf.Image != "" { + return customConfig.WebserverConf.Image + } + return nginxDefaultImageAddress +} +func getNginxContainerName(customConfig *config.CustomConfig) string { + if customConfig != nil && customConfig.WebserverConf.ContainerName != "" { + return customConfig.WebserverConf.ContainerName + } + return nginxDefaultContainerName +} diff --git a/pkg/hash/hash.go b/pkg/hash/hash.go deleted file mode 100644 index 4b1dee2..0000000 --- a/pkg/hash/hash.go +++ /dev/null @@ -1,16 +0,0 @@ -package hash - -import "crypto/rand" - -// TODO: go for interface -func GenerateRandomString(length int) string { - const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - - randomBytes := make([]byte, length) - rand.Read(randomBytes) - for i := range randomBytes { - randomBytes[i] = charset[int(randomBytes[i])%len(charset)] - } - - return string(randomBytes) -} diff --git a/pkg/random_generator/random_generator.go b/pkg/random_generator/random_generator.go new file mode 100644 index 0000000..a7bf89a --- /dev/null +++ b/pkg/random_generator/random_generator.go @@ -0,0 +1,30 @@ +package random_generator + +import ( + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "fmt" +) + +func GenerateRandomString(length int) (string, error) { + const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + + randomBytes := make([]byte, length) + _, err := rand.Read(randomBytes) + if err != nil { + return "", err + } + for i := range randomBytes { + randomBytes[i] = charset[int(randomBytes[i])%len(charset)] + } + + return string(randomBytes), nil +} + +func GenerateRandomName(baseName string, salt string) string { + tuple := fmt.Sprintf("%s-%s", baseName, salt) + sum := sha256.Sum256([]byte(tuple)) + subByte := sum[:8] + return fmt.Sprintf("%s-%s", baseName, hex.EncodeToString(subByte)) +} diff --git a/tests/e2e/adaptive-scaling/00-assert.yaml b/tests/e2e/adaptive-scaling/00-assert.yaml new file mode 100644 index 0000000..6934eb9 --- /dev/null +++ b/tests/e2e/adaptive-scaling/00-assert.yaml @@ -0,0 +1,7 @@ +apiVersion: authenticator.snappcloud.io/v1alpha1 +kind: BasicAuthenticator +metadata: + name: basicauthenticator-sample +status: + readyReplicas: 2 + diff --git a/tests/e2e/adaptive-scaling/00-install.yaml b/tests/e2e/adaptive-scaling/00-install.yaml new file mode 100644 index 0000000..6b23552 --- /dev/null +++ b/tests/e2e/adaptive-scaling/00-install.yaml @@ -0,0 +1,52 @@ +apiVersion: authenticator.snappcloud.io/v1alpha1 +kind: BasicAuthenticator +metadata: + labels: + app.kubernetes.io/name: basicauthenticator + app.kubernetes.io/instance: basicauthenticator-sample + app.kubernetes.io/part-of: basicauthenticator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: basicauthenticator + name: basicauthenticator-sample +spec: + type: deployment + replicas: 1 + appPort: 8080 + appService: my-service + adaptiveScale: true + authenticatorPort: 8080 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: curl-deployment + labels: + test: fal + foo: bar +spec: + replicas: 4 + selector: + matchLabels: + foo: bar + template: + metadata: + labels: + foo: bar + spec: + containers: + - name: curl-container + image: curlimages/curl:latest + command: ["sleep", "infinity"] +--- +apiVersion: v1 +kind: Service +metadata: + name: my-service +spec: + selector: + foo: bar + ports: + - protocol: TCP + port: 8080 + targetPort: 8080 + type: ClusterIP diff --git a/tests/e2e/adaptive-scaling/01-assert.yaml b/tests/e2e/adaptive-scaling/01-assert.yaml new file mode 100644 index 0000000..b9c985f --- /dev/null +++ b/tests/e2e/adaptive-scaling/01-assert.yaml @@ -0,0 +1,7 @@ +apiVersion: authenticator.snappcloud.io/v1alpha1 +kind: BasicAuthenticator +metadata: + name: basicauthenticator-sample +status: + readyReplicas: 3 + diff --git a/tests/e2e/adaptive-scaling/01-increase-deploy-replicas.yaml b/tests/e2e/adaptive-scaling/01-increase-deploy-replicas.yaml new file mode 100644 index 0000000..aec94c5 --- /dev/null +++ b/tests/e2e/adaptive-scaling/01-increase-deploy-replicas.yaml @@ -0,0 +1,8 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: curl-deployment + labels: + foo: bar +spec: + replicas: 6 diff --git a/tests/e2e/adaptive-scaling/02-assert.yaml b/tests/e2e/adaptive-scaling/02-assert.yaml new file mode 100644 index 0000000..6934eb9 --- /dev/null +++ b/tests/e2e/adaptive-scaling/02-assert.yaml @@ -0,0 +1,7 @@ +apiVersion: authenticator.snappcloud.io/v1alpha1 +kind: BasicAuthenticator +metadata: + name: basicauthenticator-sample +status: + readyReplicas: 2 + diff --git a/tests/e2e/adaptive-scaling/02-decrease-deploy-replicas.yaml b/tests/e2e/adaptive-scaling/02-decrease-deploy-replicas.yaml new file mode 100644 index 0000000..2da005e --- /dev/null +++ b/tests/e2e/adaptive-scaling/02-decrease-deploy-replicas.yaml @@ -0,0 +1,8 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: curl-deployment + labels: + foo: bar +spec: + replicas: 4