diff --git a/.github/workflows/aergia-controller.yaml b/.github/workflows/aergia-controller.yaml index aa7e795..fa65430 100644 --- a/.github/workflows/aergia-controller.yaml +++ b/.github/workflows/aergia-controller.yaml @@ -26,21 +26,6 @@ jobs: uses: actions/setup-go@v5 with: go-version: '1.23' - - name: Install kustomize, kubebuilder, helm - run: | - #kubebuilder - curl -sL https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.3.2/kubebuilder_2.3.2_linux_amd64.tar.gz | tar -xz -C /tmp/ - sudo mkdir -p /usr/local/kubebuilder/bin - sudo mv /tmp/kubebuilder_2.3.2_linux_amd64/bin/* /usr/local/kubebuilder/bin - chmod +x /usr/local/kubebuilder/bin/* - echo "/usr/local/kubebuilder/bin" >> $GITHUB_PATH - - name: Check go, kustomize, kubebuilder, helm, docker-compose, kind versions - run: | - go version - kustomize version - helm version - kubebuilder version - kind version - name: Add dependency chart repos run: | @@ -57,8 +42,9 @@ jobs: - name: Configure node IP in kind-config.yaml run: | docker network create kind - LAGOON_KIND_CIDR_BLOCK=$(docker network inspect kind | jq '. [0].IPAM.Config[0].Subnet' | tr -d '"') - export KIND_NODE_IP=$(echo ${LAGOON_KIND_CIDR_BLOCK%???} | awk -F'.' '{print $1,$2,$3,240}' OFS='.') + LAGOON_KIND_CIDR_BLOCK=$(docker network inspect kind | jq '. [0].IPAM.Config[0].Subnet' | tr -d '"') + KIND_NODE_IP=$(echo "${LAGOON_KIND_CIDR_BLOCK%???}" | awk -F'.' '{print $1,$2,$3,240}' OFS='.') + export KIND_NODE_IP envsubst < test-resources/test-suite.kind-config.yaml.tpl > test-resources/test-suite.kind-config.yaml envsubst < test/e2e/testdata/example-nginx.yaml.tpl > test/e2e/testdata/example-nginx.yaml @@ -83,6 +69,7 @@ jobs: - name: Run github/test-e2e run: | - LAGOON_KIND_CIDR_BLOCK=$(docker network inspect kind | jq '. [0].IPAM.Config[0].Subnet' | tr -d '"') - export KIND_NODE_IP=$(echo ${LAGOON_KIND_CIDR_BLOCK%???} | awk -F'.' '{print $1,$2,$3,240}' OFS='.') + LAGOON_KIND_CIDR_BLOCK=$(docker network inspect kind | jq '. [0].IPAM.Config[0].Subnet' | tr -d '"') + KIND_NODE_IP=$(echo "${LAGOON_KIND_CIDR_BLOCK%???}" | awk -F'.' '{print $1,$2,$3,240}' OFS='.') + export KIND_NODE_IP make github/test-e2e KIND_NETWORK=kind \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index 3c75ad5..2e11fc5 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -145,15 +145,8 @@ func main() { })) // read the selector file into idlerdata struct. - file, err := os.Open(selectorsFile) + selectors, err := readSelectors(selectorsFile) if err != nil { - setupLog.Error(err, "unable to open selectors file") - os.Exit(1) - } - defer file.Close() - d := yaml.NewDecoder(file) - selectors := &idler.Data{} - if err := d.Decode(&selectors); err != nil { setupLog.Error(err, "unable to decode selectors yaml") os.Exit(1) } @@ -245,16 +238,22 @@ func main() { // CLI Idler if enableCLIIdler { setupLog.Info("starting cli idler") - c.AddFunc(cliCron, func() { + _, err := c.AddFunc(cliCron, func() { idler.CLIIdler() }) + if err != nil { + setupLog.Error(err, "unable to create cli idler cronjob", "controller", "Idling") + } } // Service Idler if enableServiceIdler { setupLog.Info("starting service idler") - c.AddFunc(serviceCron, func() { + _, err := c.AddFunc(serviceCron, func() { idler.ServiceIdler() }) + if err != nil { + setupLog.Error(err, "unable to create service idler cronjob", "controller", "Idling") + } } // start crons. c.Start() @@ -280,3 +279,17 @@ func main() { os.Exit(1) } } + +func readSelectors(selectorsFile string) (*idler.Data, error) { + file, err := os.Open(selectorsFile) + if err != nil { + return nil, err + } + defer file.Close() + d := yaml.NewDecoder(file) + selectors := &idler.Data{} + if err := d.Decode(&selectors); err != nil { + return nil, err + } + return selectors, nil +} diff --git a/internal/controllers/idling_controller.go b/internal/controllers/idling_controller.go index d49e7fa..7dfeca3 100644 --- a/internal/controllers/idling_controller.go +++ b/internal/controllers/idling_controller.go @@ -49,9 +49,9 @@ func (r *IdlingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, ignoreNotFound(err) } - if val, ok := namespace.ObjectMeta.Labels["idling.amazee.io/force-scaled"]; ok && val == "true" { + if val, ok := namespace.Labels["idling.amazee.io/force-scaled"]; ok && val == "true" { opLog.Info(fmt.Sprintf("Force scaling environment %s", namespace.Name)) - r.Idler.KubernetesServiceIdler(ctx, opLog, namespace, namespace.ObjectMeta.Labels[r.Idler.Selectors.NamespaceSelectorsLabels.ProjectName], false, true) + r.Idler.KubernetesServiceIdler(ctx, opLog, namespace, namespace.Labels[r.Idler.Selectors.NamespaceSelectorsLabels.ProjectName], false, true) nsMergePatch, _ := json.Marshal(map[string]interface{}{ "metadata": map[string]interface{}{ "labels": map[string]*string{ @@ -66,9 +66,9 @@ func (r *IdlingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, nil } - if val, ok := namespace.ObjectMeta.Labels["idling.amazee.io/force-idled"]; ok && val == "true" { + if val, ok := namespace.Labels["idling.amazee.io/force-idled"]; ok && val == "true" { opLog.Info(fmt.Sprintf("Force idling environment %s", namespace.Name)) - r.Idler.KubernetesServiceIdler(ctx, opLog, namespace, namespace.ObjectMeta.Labels[r.Idler.Selectors.NamespaceSelectorsLabels.ProjectName], true, false) + r.Idler.KubernetesServiceIdler(ctx, opLog, namespace, namespace.Labels[r.Idler.Selectors.NamespaceSelectorsLabels.ProjectName], true, false) nsMergePatch, _ := json.Marshal(map[string]interface{}{ "metadata": map[string]interface{}{ "labels": map[string]*string{ @@ -83,7 +83,7 @@ func (r *IdlingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr return ctrl.Result{}, nil } - if val, ok := namespace.ObjectMeta.Labels["idling.amazee.io/unidle"]; ok && val == "true" { + if val, ok := namespace.Labels["idling.amazee.io/unidle"]; ok && val == "true" { opLog.Info(fmt.Sprintf("Unidling environment %s", namespace.Name)) r.Unidler.Unidle(ctx, &namespace, opLog) nsMergePatch, _ := json.Marshal(map[string]interface{}{ diff --git a/internal/handlers/idler/cli-kubernetes.go b/internal/handlers/idler/cli-kubernetes.go index 713ccb8..96a2a46 100644 --- a/internal/handlers/idler/cli-kubernetes.go +++ b/internal/handlers/idler/cli-kubernetes.go @@ -21,7 +21,7 @@ import ( func (h *Idler) kubernetesCLI(ctx context.Context, opLog logr.Logger, namespace corev1.Namespace) { labelRequirements := generateLabelRequirements(h.Selectors.CLI.Builds) listOption := (&client.ListOptions{}).ApplyOptions([]client.ListOption{ - client.InNamespace(namespace.ObjectMeta.Name), + client.InNamespace(namespace.Name), client.MatchingLabelsSelector{ Selector: labels.NewSelector().Add(labelRequirements...), }, @@ -31,7 +31,7 @@ func (h *Idler) kubernetesCLI(ctx context.Context, opLog logr.Logger, namespace runningBuild := false if !h.Selectors.CLI.SkipBuildCheck { if err := h.Client.List(ctx, builds, listOption); err != nil { - opLog.Error(err, fmt.Sprintf("Error getting running builds for namespace %s", namespace.ObjectMeta.Name)) + opLog.Error(err, fmt.Sprintf("Error getting running builds for namespace %s", namespace.Name)) } else { for _, build := range builds.Items { if build.Status.Phase == "Running" { @@ -47,7 +47,7 @@ func (h *Idler) kubernetesCLI(ctx context.Context, opLog logr.Logger, namespace // @TODO: eventually replace the `lagoon.sh/service=cli` check with `lagoon.sh/service-type=cli|cli-persistent` for better coverage labelRequirements := generateLabelRequirements(h.Selectors.CLI.Deployments) listOption = (&client.ListOptions{}).ApplyOptions([]client.ListOption{ - client.InNamespace(namespace.ObjectMeta.Name), + client.InNamespace(namespace.Name), client.MatchingLabelsSelector{ Selector: labels.NewSelector().Add(labelRequirements...), }, @@ -61,13 +61,13 @@ func (h *Idler) kubernetesCLI(ctx context.Context, opLog logr.Logger, namespace zeroReps := new(int32) *zeroReps = 0 if deployment.Spec.Replicas != zeroReps { - opLog.Info(fmt.Sprintf("Deployment %s has %d running replicas", deployment.ObjectMeta.Name, *deployment.Spec.Replicas)) + opLog.Info(fmt.Sprintf("Deployment %s has %d running replicas", deployment.Name, *deployment.Spec.Replicas)) } else { - opLog.Info(fmt.Sprintf("Deployment %s is already idled", deployment.ObjectMeta.Name)) + opLog.Info(fmt.Sprintf("Deployment %s is already idled", deployment.Name)) break } if h.Debug { - opLog.Info(fmt.Sprintf("Checking deployment %s for cronjobs", deployment.ObjectMeta.Name)) + opLog.Info(fmt.Sprintf("Checking deployment %s for cronjobs", deployment.Name)) } hasCrons := false @@ -77,7 +77,7 @@ func (h *Idler) kubernetesCLI(ctx context.Context, opLog logr.Logger, namespace if env.Name == "CRONJOBS" { if len(env.Value) > 0 { cronjobs := strings.Split(env.Value, `\n`) - opLog.Info(fmt.Sprintf("Deployment %s has %d cronjobs defined", deployment.ObjectMeta.Name, len(cronjobs))) + opLog.Info(fmt.Sprintf("Deployment %s has %d cronjobs defined", deployment.Name, len(cronjobs))) hasCrons = true break } @@ -89,7 +89,7 @@ func (h *Idler) kubernetesCLI(ctx context.Context, opLog logr.Logger, namespace pods := &corev1.PodList{} labelRequirements := generateLabelRequirements(h.Selectors.CLI.Pods) listOption = (&client.ListOptions{}).ApplyOptions([]client.ListOption{ - client.InNamespace(namespace.ObjectMeta.Name), + client.InNamespace(namespace.Name), client.MatchingLabelsSelector{ Selector: labels.NewSelector().Add(labelRequirements...), }, @@ -101,16 +101,16 @@ func (h *Idler) kubernetesCLI(ctx context.Context, opLog logr.Logger, namespace processCount := 0 if !h.Selectors.CLI.SkipProcessCheck { if h.Debug { - opLog.Info(fmt.Sprintf("Checking pod %s for running processes", pod.ObjectMeta.Name)) + opLog.Info(fmt.Sprintf("Checking pod %s for running processes", pod.Name)) } /* Anything running with parent PID0 is likely a user process /bin/bash -c "pgrep -P 0 | tail -n +3 | wc -l | tr -d ' '" */ var stdin io.Reader - stdout, _, err := execPod(pod.ObjectMeta.Name, namespace.ObjectMeta.Name, []string{`/bin/sh`, `-c`, `pgrep -P 0|tail -n +3|wc -l|tr -d ' '`}, stdin, false) + stdout, _, err := execPod(pod.Name, namespace.Name, []string{`/bin/sh`, `-c`, `pgrep -P 0|tail -n +3|wc -l|tr -d ' '`}, stdin, false) if err != nil { - opLog.Error(err, fmt.Sprintf("Error when trying to exec to pod %s", pod.ObjectMeta.Name)) + opLog.Error(err, fmt.Sprintf("Error when trying to exec to pod %s", pod.Name)) break } trimmed := strings.TrimSpace(string(stdout)) @@ -121,7 +121,7 @@ func (h *Idler) kubernetesCLI(ctx context.Context, opLog logr.Logger, namespace } } if processCount == 0 { - opLog.Info(fmt.Sprintf("Pod %s has no running processes, idling", pod.ObjectMeta.Name)) + opLog.Info(fmt.Sprintf("Pod %s has no running processes, idling", pod.Name)) } } if processCount == 0 { @@ -133,13 +133,13 @@ func (h *Idler) kubernetesCLI(ctx context.Context, opLog logr.Logger, namespace }, }) if err := h.Client.Patch(ctx, scaleDeployment, client.RawPatch(types.MergePatchType, mergePatch)); err != nil { - opLog.Error(err, fmt.Sprintf("Error scaling deployment %s", deployment.ObjectMeta.Name)) + opLog.Error(err, fmt.Sprintf("Error scaling deployment %s", deployment.Name)) } else { - opLog.Info(fmt.Sprintf("Deployment %s scaled to 0", deployment.ObjectMeta.Name)) + opLog.Info(fmt.Sprintf("Deployment %s scaled to 0", deployment.Name)) } metrics.CliIdleEvents.Inc() } else { - opLog.Info(fmt.Sprintf("Deployment %s would be scaled to 0", deployment.ObjectMeta.Name)) + opLog.Info(fmt.Sprintf("Deployment %s would be scaled to 0", deployment.Name)) } } } diff --git a/internal/handlers/idler/cli.go b/internal/handlers/idler/cli.go index 83ff10a..927a806 100644 --- a/internal/handlers/idler/cli.go +++ b/internal/handlers/idler/cli.go @@ -17,11 +17,10 @@ import ( func (h *Idler) CLIIdler() { ctx := context.Background() opLog := h.Log.WithName("aergia-controller").WithName("CLIIdler") - listOption := &client.ListOptions{} // in kubernetes, we can reliably check for the existence of this label so that // we only check namespaces that have been deployed by a lagoon at one point labelRequirements := generateLabelRequirements(h.Selectors.CLI.Namespace) - listOption = (&client.ListOptions{}).ApplyOptions([]client.ListOption{ + listOption := (&client.ListOptions{}).ApplyOptions([]client.ListOption{ client.MatchingLabelsSelector{ Selector: labels.NewSelector().Add(labelRequirements...), }, @@ -32,28 +31,26 @@ func (h *Idler) CLIIdler() { return } for _, namespace := range namespaces.Items { - projectAutoIdle, ok1 := namespace.ObjectMeta.Labels[h.Selectors.NamespaceSelectorsLabels.ProjectIdling] - environmentAutoIdle, ok2 := namespace.ObjectMeta.Labels[h.Selectors.NamespaceSelectorsLabels.EnvironmentIdling] + projectAutoIdle, ok1 := namespace.Labels[h.Selectors.NamespaceSelectorsLabels.ProjectIdling] + environmentAutoIdle, ok2 := namespace.Labels[h.Selectors.NamespaceSelectorsLabels.EnvironmentIdling] if ok1 && ok2 { if environmentAutoIdle == "1" && projectAutoIdle == "1" { - envOpLog := opLog.WithValues("namespace", namespace.ObjectMeta.Name). - WithValues("project", namespace.ObjectMeta.Labels[h.Selectors.NamespaceSelectorsLabels.ProjectName]). - WithValues("environment", namespace.ObjectMeta.Labels[h.Selectors.NamespaceSelectorsLabels.EnvironmentName]). + envOpLog := opLog.WithValues("namespace", namespace.Name). + WithValues("project", namespace.Labels[h.Selectors.NamespaceSelectorsLabels.ProjectName]). + WithValues("environment", namespace.Labels[h.Selectors.NamespaceSelectorsLabels.EnvironmentName]). WithValues("dry-run", h.DryRun) envOpLog.Info("Checking namespace") h.kubernetesCLI(ctx, envOpLog, namespace) - } else { - if h.Debug { - opLog.Info(fmt.Sprintf("skipping namespace %s; autoidle values are env:%s proj:%s", - namespace.ObjectMeta.Name, - environmentAutoIdle, - projectAutoIdle)) - } + } else if h.Debug { + opLog.Info(fmt.Sprintf("skipping namespace %s; autoidle values are env:%s proj:%s", + namespace.Name, + environmentAutoIdle, + projectAutoIdle)) } } else { if h.Debug { opLog.Info(fmt.Sprintf("skipping namespace %s; not in lagoon", - namespace.ObjectMeta.Name)) + namespace.Name)) } } } diff --git a/internal/handlers/idler/service-kubernetes.go b/internal/handlers/idler/service-kubernetes.go index 9afcab5..e492738 100644 --- a/internal/handlers/idler/service-kubernetes.go +++ b/internal/handlers/idler/service-kubernetes.go @@ -24,7 +24,7 @@ import ( func (h *Idler) KubernetesServiceIdler(ctx context.Context, opLog logr.Logger, namespace corev1.Namespace, lagoonProject string, forceIdle, forceScale bool) { labelRequirements := generateLabelRequirements(h.Selectors.Service.Builds) listOption := (&client.ListOptions{}).ApplyOptions([]client.ListOption{ - client.InNamespace(namespace.ObjectMeta.Name), + client.InNamespace(namespace.Name), client.MatchingLabelsSelector{ Selector: labels.NewSelector().Add(labelRequirements...), }, @@ -32,14 +32,14 @@ func (h *Idler) KubernetesServiceIdler(ctx context.Context, opLog logr.Logger, n podIntervalCheck := h.PodCheckInterval prometheusInternalCheck := h.PrometheusCheckInterval // allow namespace interval overides - if podinterval, ok := namespace.ObjectMeta.Annotations["idling.amazee.io/pod-interval"]; ok { + if podinterval, ok := namespace.Annotations["idling.amazee.io/pod-interval"]; ok { t, err := time.ParseDuration(podinterval) if err == nil { podIntervalCheck = t } } - if promethusinterval, ok := namespace.ObjectMeta.Annotations["idling.amazee.io/prometheus-interval"]; ok { + if promethusinterval, ok := namespace.Annotations["idling.amazee.io/prometheus-interval"]; ok { t, err := time.ParseDuration(promethusinterval) if err == nil { prometheusInternalCheck = t @@ -49,7 +49,7 @@ func (h *Idler) KubernetesServiceIdler(ctx context.Context, opLog logr.Logger, n runningBuild := false if !h.Selectors.Service.SkipBuildCheck { if err := h.Client.List(ctx, builds, listOption); err != nil { - opLog.Error(err, fmt.Sprintf("Error getting running builds for namespace %s", namespace.ObjectMeta.Name)) + opLog.Error(err, fmt.Sprintf("Error getting running builds for namespace %s", namespace.Name)) } else { for _, build := range builds.Items { if build.Status.Phase == "Running" || build.Status.Phase == "Pending" { @@ -65,7 +65,7 @@ func (h *Idler) KubernetesServiceIdler(ctx context.Context, opLog logr.Logger, n if !runningBuild { labelRequirements := generateLabelRequirements(h.Selectors.Service.Deployments) listOption = (&client.ListOptions{}).ApplyOptions([]client.ListOption{ - client.InNamespace(namespace.ObjectMeta.Name), + client.InNamespace(namespace.Name), client.MatchingLabelsSelector{ Selector: labels.NewSelector().Add(labelRequirements...), }, @@ -82,19 +82,17 @@ func (h *Idler) KubernetesServiceIdler(ctx context.Context, opLog logr.Logger, n zeroReps := new(int32) *zeroReps = 0 if deployment.Spec.Replicas != zeroReps { - opLog.Info(fmt.Sprintf("Deployment %s has %d running replicas", deployment.ObjectMeta.Name, *deployment.Spec.Replicas)) + opLog.Info(fmt.Sprintf("Deployment %s has %d running replicas", deployment.Name, *deployment.Spec.Replicas)) checkPods = true - } else { - if h.Debug { - opLog.Info(fmt.Sprintf("Deployment %s already idled", deployment.ObjectMeta.Name)) - } + } else if h.Debug { + opLog.Info(fmt.Sprintf("Deployment %s already idled", deployment.Name)) } if checkPods { pods := &corev1.PodList{} // pods in kubernetes have the label `h.Selectors.ServiceName` with the name of the deployment in it listOption = (&client.ListOptions{}).ApplyOptions([]client.ListOption{ - client.InNamespace(namespace.ObjectMeta.Name), - client.MatchingLabels(map[string]string{h.Selectors.ServiceName: deployment.ObjectMeta.Name}), + client.InNamespace(namespace.Name), + client.MatchingLabels(map[string]string{h.Selectors.ServiceName: deployment.Name}), }) if err := h.Client.List(ctx, pods, listOption); err != nil { // if we can't get any pods for this deployment, log it and move on to the next @@ -106,7 +104,7 @@ func (h *Idler) KubernetesServiceIdler(ctx context.Context, opLog logr.Logger, n if pod.Status.StartTime != nil { hs := time.Since(pod.Status.StartTime.Time) if h.Debug { - opLog.Info(fmt.Sprintf("Pod %s has been running for %v", pod.ObjectMeta.Name, hs)) + opLog.Info(fmt.Sprintf("Pod %s has been running for %v", pod.Name, hs)) } if hs > podIntervalCheck { // if it is, set the idle flag @@ -128,7 +126,7 @@ func (h *Idler) KubernetesServiceIdler(ctx context.Context, opLog logr.Logger, n // get the number of requests to any ingress in the exported namespace by status code promQuery := fmt.Sprintf( `round(sum(increase(nginx_ingress_controller_requests{exported_namespace="%s",status=~"2[0-9x]{2}"}[%s])) by (status))`, - namespace.ObjectMeta.Name, + namespace.Name, prometheusInternalCheck, ) result, warnings, err := v1api.Query(ctx, promQuery, time.Now()) @@ -144,7 +142,7 @@ func (h *Idler) KubernetesServiceIdler(ctx context.Context, opLog logr.Logger, n resultVal := result.(prometheusmodel.Vector) for _, elem := range resultVal { hits, _ := strconv.Atoi(elem.Value.String()) - numHits = numHits + hits + numHits += hits } } // if the hits are not 0, then the environment doesn't need to be idled @@ -209,12 +207,12 @@ func (h *Idler) idleDeployments(ctx context.Context, opLog logr.Logger, deployme }) if err := h.Client.Patch(ctx, scaleDeployment, client.RawPatch(types.MergePatchType, mergePatch)); err != nil { // log it but try and scale the rest of the deployments anyway (some idled is better than none?) - opLog.Info(fmt.Sprintf("Error scaling deployment %s", deployment.ObjectMeta.Name)) + opLog.Info(fmt.Sprintf("Error scaling deployment %s", deployment.Name)) } else { - opLog.Info(fmt.Sprintf("Deployment %s scaled to 0", deployment.ObjectMeta.Name)) + opLog.Info(fmt.Sprintf("Deployment %s scaled to 0", deployment.Name)) } } else { - opLog.Info(fmt.Sprintf("Deployment %s would be scaled to 0", deployment.ObjectMeta.Name)) + opLog.Info(fmt.Sprintf("Deployment %s would be scaled to 0", deployment.Name)) } } } @@ -228,7 +226,7 @@ func (h *Idler) patchIngress(ctx context.Context, opLog logr.Logger, namespace c if !h.Selectors.Service.SkipIngressPatch { labelRequirements := generateLabelRequirements(h.Selectors.Service.Ingress) listOption := (&client.ListOptions{}).ApplyOptions([]client.ListOption{ - client.InNamespace(namespace.ObjectMeta.Name), + client.InNamespace(namespace.Name), client.MatchingLabelsSelector{ Selector: labels.NewSelector().Add(labelRequirements...), }, @@ -256,13 +254,13 @@ func (h *Idler) patchIngress(ctx context.Context, opLog logr.Logger, namespace c }) if err := h.Client.Patch(ctx, ingressCopy, client.RawPatch(types.MergePatchType, mergePatch)); err != nil { // log it but try and patch the other ingress anyway (some idled is better than none?) - opLog.Info(fmt.Sprintf("Error patching ingress %s", ingress.ObjectMeta.Name)) - return fmt.Errorf("error patching ingress %s", ingress.ObjectMeta.Name) + opLog.Info(fmt.Sprintf("Error patching ingress %s", ingress.Name)) + return fmt.Errorf("error patching ingress %s", ingress.Name) } - opLog.Info(fmt.Sprintf("Ingress %s patched", ingress.ObjectMeta.Name)) + opLog.Info(fmt.Sprintf("Ingress %s patched", ingress.Name)) patched = true } else { - opLog.Info(fmt.Sprintf("Ingress %s would be patched", ingress.ObjectMeta.Name)) + opLog.Info(fmt.Sprintf("Ingress %s would be patched", ingress.Name)) } } if patched { diff --git a/internal/handlers/idler/service.go b/internal/handlers/idler/service.go index c7a535b..e2bd6c5 100644 --- a/internal/handlers/idler/service.go +++ b/internal/handlers/idler/service.go @@ -22,8 +22,6 @@ func (h *Idler) ServiceIdler() { ctx := context.Background() opLog := h.Log - - listOption := &client.ListOptions{} // in kubernetes, we can reliably check for the existence of this label so that // we only check namespaces that have been deployed by a lagoon at one point labelRequirements := generateLabelRequirements(h.Selectors.Service.Namespace) @@ -38,7 +36,7 @@ func (h *Idler) ServiceIdler() { // }, // }) // labelRequirements = append(labelRequirements, *selector) - listOption = (&client.ListOptions{}).ApplyOptions([]client.ListOption{ + listOption := (&client.ListOptions{}).ApplyOptions([]client.ListOption{ client.MatchingLabelsSelector{ Selector: labels.NewSelector().Add(labelRequirements...), }, @@ -52,30 +50,28 @@ func (h *Idler) ServiceIdler() { // loop over the namespaces for _, namespace := range namespaces.Items { - projectAutoIdle, ok1 := namespace.ObjectMeta.Labels[h.Selectors.NamespaceSelectorsLabels.ProjectIdling] - environmentAutoIdle, ok2 := namespace.ObjectMeta.Labels[h.Selectors.NamespaceSelectorsLabels.EnvironmentIdling] - environmentType, ok3 := namespace.ObjectMeta.Labels[h.Selectors.NamespaceSelectorsLabels.EnvironmentType] + projectAutoIdle, ok1 := namespace.Labels[h.Selectors.NamespaceSelectorsLabels.ProjectIdling] + environmentAutoIdle, ok2 := namespace.Labels[h.Selectors.NamespaceSelectorsLabels.EnvironmentIdling] + environmentType, ok3 := namespace.Labels[h.Selectors.NamespaceSelectorsLabels.EnvironmentType] if ok1 && ok2 && ok3 { if environmentAutoIdle == "1" && projectAutoIdle == "1" { - envOpLog := opLog.WithValues("namespace", namespace.ObjectMeta.Name). - WithValues("project", namespace.ObjectMeta.Labels[h.Selectors.NamespaceSelectorsLabels.ProjectName]). - WithValues("environment", namespace.ObjectMeta.Labels[h.Selectors.NamespaceSelectorsLabels.EnvironmentName]). + envOpLog := opLog.WithValues("namespace", namespace.Name). + WithValues("project", namespace.Labels[h.Selectors.NamespaceSelectorsLabels.ProjectName]). + WithValues("environment", namespace.Labels[h.Selectors.NamespaceSelectorsLabels.EnvironmentName]). WithValues("dry-run", h.DryRun) envOpLog.Info("Checking namespace") - h.KubernetesServiceIdler(ctx, envOpLog, namespace, namespace.ObjectMeta.Labels[h.Selectors.NamespaceSelectorsLabels.ProjectName], false, false) - } else { - if h.Debug { - opLog.Info(fmt.Sprintf("skipping namespace %s; type is %s, autoidle values are env:%s proj:%s", - namespace.ObjectMeta.Name, - environmentType, - environmentAutoIdle, - projectAutoIdle)) - } + h.KubernetesServiceIdler(ctx, envOpLog, namespace, namespace.Labels[h.Selectors.NamespaceSelectorsLabels.ProjectName], false, false) + } else if h.Debug { + opLog.Info(fmt.Sprintf("skipping namespace %s; type is %s, autoidle values are env:%s proj:%s", + namespace.Name, + environmentType, + environmentAutoIdle, + projectAutoIdle)) } } else { if h.Debug { opLog.Info(fmt.Sprintf("skipping namespace %s; not in lagoon", - namespace.ObjectMeta.Name)) + namespace.Name)) } } } diff --git a/internal/handlers/unidler/checks.go b/internal/handlers/unidler/checks.go index c0383d8..d278479 100644 --- a/internal/handlers/unidler/checks.go +++ b/internal/handlers/unidler/checks.go @@ -72,7 +72,7 @@ func (h *Unidler) removeCodeFromIngress(ctx context.Context, ns string, opLog lo // if the nginx.ingress.kubernetes.io/custom-http-errors annotation is set // then strip out the 503 error code that is there so that // users will see their application errors rather than the loading page - if value, ok := ingress.ObjectMeta.Annotations["nginx.ingress.kubernetes.io/custom-http-errors"]; ok { + if value, ok := ingress.Annotations["nginx.ingress.kubernetes.io/custom-http-errors"]; ok { newVals := removeStatusCode(value, "503") // if the 503 code was removed from the annotation // then patch it @@ -91,12 +91,12 @@ func (h *Unidler) removeCodeFromIngress(ctx context.Context, ns string, opLog lo patchIngress := ingress.DeepCopy() if err := h.Client.Patch(ctx, patchIngress, ctrlClient.RawPatch(types.MergePatchType, mergePatch)); err != nil { // log it but try and patch the rest of the ingressses anyway (some is better than none?) - opLog.Info(fmt.Sprintf("Error patching custom-http-errors on ingress %s - %s", ingress.ObjectMeta.Name, ns)) + opLog.Info(fmt.Sprintf("Error patching custom-http-errors on ingress %s - %s", ingress.Name, ns)) } else { if newVals == nil { - opLog.Info(fmt.Sprintf("Ingress %s custom-http-errors annotation removed - %s", ingress.ObjectMeta.Name, ns)) + opLog.Info(fmt.Sprintf("Ingress %s custom-http-errors annotation removed - %s", ingress.Name, ns)) } else { - opLog.Info(fmt.Sprintf("Ingress %s custom-http-errors annotation patched with %s - %s", ingress.ObjectMeta.Name, *newVals, ns)) + opLog.Info(fmt.Sprintf("Ingress %s custom-http-errors annotation patched with %s - %s", ingress.Name, *newVals, ns)) } } } diff --git a/internal/handlers/unidler/handler.go b/internal/handlers/unidler/handler.go index d2fdad7..d832473 100644 --- a/internal/handlers/unidler/handler.go +++ b/internal/handlers/unidler/handler.go @@ -77,7 +77,7 @@ func (h *Unidler) ingressHandler(path string) func(http.ResponseWriter, *http.Re trueClientIP := r.Header.Get("True-Client-IP") requestUserAgent := r.Header.Get("User-Agent") - allowUnidle := h.checkAccess(namespace.ObjectMeta.Annotations, ingress.ObjectMeta.Annotations, requestUserAgent, trueClientIP, xForwardedFor) + allowUnidle := h.checkAccess(namespace.Annotations, ingress.Annotations, requestUserAgent, trueClientIP, xForwardedFor) // then run checks to start to unidle the environment if allowUnidle { // if a namespace exists, it means that the custom-http-errors code is defined in the ingress object @@ -118,7 +118,7 @@ func (h *Unidler) ingressHandler(path string) func(http.ResponseWriter, *http.Re } // then return the unidle template to the user tmpl := template.Must(template.ParseFiles(file)) - tmpl.ExecuteTemplate(w, "base", pageData{ + _ = tmpl.ExecuteTemplate(w, "base", pageData{ ErrorCode: strconv.Itoa(code), FormatHeader: r.Header.Get(FormatHeader), CodeHeader: r.Header.Get(CodeHeader), @@ -154,7 +154,7 @@ func (h *Unidler) genericError(w http.ResponseWriter, r *http.Request, opLog log opLog.Info(fmt.Sprintf("Serving custom error response for code %v and format %v from file %v", code, format, file)) } tmpl := template.Must(template.ParseFiles(file)) - tmpl.ExecuteTemplate(w, "base", pageData{ + _ = tmpl.ExecuteTemplate(w, "base", pageData{ ErrorCode: strconv.Itoa(code), ErrorMessage: http.StatusText(code), FormatHeader: r.Header.Get(FormatHeader), @@ -173,14 +173,14 @@ func (h *Unidler) genericError(w http.ResponseWriter, r *http.Request, opLog log // handle verifying the namespace name is signed by our secret func (h *Unidler) verifyRequest(r *http.Request, ns *corev1.Namespace, ingress *networkv1.Ingress) (string, bool) { if h.VerifiedUnidling { - if val, ok := ingress.ObjectMeta.Annotations["idling.amazee.io/disable-request-verification"]; ok { + if val, ok := ingress.Annotations["idling.amazee.io/disable-request-verification"]; ok { t, _ := strconv.ParseBool(val) if t { return "", true } // otherwise fall through to namespace check } - if val, ok := ns.ObjectMeta.Annotations["idling.amazee.io/disable-request-verification"]; ok { + if val, ok := ns.Annotations["idling.amazee.io/disable-request-verification"]; ok { t, _ := strconv.ParseBool(val) if t { return "", true diff --git a/internal/handlers/unidler/restrictions.go b/internal/handlers/unidler/restrictions.go index 8f68566..ee1ed23 100644 --- a/internal/handlers/unidler/restrictions.go +++ b/internal/handlers/unidler/restrictions.go @@ -64,11 +64,8 @@ func checkAgentAnnotations(annotation, ua string, g []string, ns, i map[string]s // check for namespace annoation if agents, ok := ns[annotation]; ok { allow = checkAgents(strings.Split(agents, ","), ua) - } else { - // check for globals - if g != nil { - allow = checkAgents(g, ua) - } + } else if g != nil { + allow = checkAgents(g, ua) } } return allow @@ -84,11 +81,8 @@ func checkIPAnnotations(annotation, tcip string, xff, g []string, ns, i map[stri // check for namespace annoation if alist, ok := ns[annotation]; ok { allow = checkIPList(strings.Split(alist, ","), xff, tcip) - } else { - // check for globals - if g != nil { - allow = checkIPList(g, xff, tcip) - } + } else if g != nil { + allow = checkIPList(g, xff, tcip) } } return allow diff --git a/internal/handlers/unidler/unidler.go b/internal/handlers/unidler/unidler.go index bc74be6..1603e24 100644 --- a/internal/handlers/unidler/unidler.go +++ b/internal/handlers/unidler/unidler.go @@ -142,13 +142,13 @@ func (h *Unidler) Unidle(ctx context.Context, namespace *corev1.Namespace, opLog } for _, deploy := range deployments.Items { // if the idled annotation is true - lv, lok := deploy.ObjectMeta.Labels["idling.amazee.io/idled"] + lv, lok := deploy.Labels["idling.amazee.io/idled"] if lok && lv == "true" { - opLog.Info(fmt.Sprintf("Deployment %s - Replicas %v - %s", deploy.ObjectMeta.Name, *deploy.Spec.Replicas, namespace.Name)) + opLog.Info(fmt.Sprintf("Deployment %s - Replicas %v - %s", deploy.Name, *deploy.Spec.Replicas, namespace.Name)) if *deploy.Spec.Replicas == 0 { // default to scaling to 1 replica newReplicas := 1 - if value, ok := deploy.ObjectMeta.Annotations["idling.amazee.io/unidle-replicas"]; ok { + if value, ok := deploy.Annotations["idling.amazee.io/unidle-replicas"]; ok { // but if the value of the annotation is greater than 0, use what is in the annotation instead unidleReplicas, err := strconv.Atoi(value) if err == nil { @@ -175,9 +175,9 @@ func (h *Unidler) Unidle(ctx context.Context, namespace *corev1.Namespace, opLog scaleDepConf := deploy.DeepCopy() if err := h.Client.Patch(ctx, scaleDepConf, ctrlClient.RawPatch(types.MergePatchType, mergePatch)); err != nil { // log it but try and scale the rest of the deployments anyway (some idled is better than none?) - opLog.Info(fmt.Sprintf("Error scaling deployment %s - %s", deploy.ObjectMeta.Name, namespace.Name)) + opLog.Info(fmt.Sprintf("Error scaling deployment %s - %s", deploy.Name, namespace.Name)) } else { - opLog.Info(fmt.Sprintf("Deployment %s scaled to %d - %s", deploy.ObjectMeta.Name, newReplicas, namespace.Name)) + opLog.Info(fmt.Sprintf("Deployment %s scaled to %d - %s", deploy.Name, newReplicas, namespace.Name)) } } } @@ -185,8 +185,11 @@ func (h *Unidler) Unidle(ctx context.Context, namespace *corev1.Namespace, opLog // now wait for the pods of these deployments to be ready // this could still result in 503 for users until the resulting services/endpoints are active and receiving traffic for _, deploy := range deployments.Items { - opLog.Info(fmt.Sprintf("Waiting for %s to be running - %s", deploy.ObjectMeta.Name, namespace.Name)) - wait.PollUntilContextTimeout(ctx, defaultPollDuration, defaultPollTimeout, true, h.hasRunningPod(ctx, namespace.Name, deploy.Name)) + opLog.Info(fmt.Sprintf("Waiting for %s to be running - %s", deploy.Name, namespace.Name)) + err := wait.PollUntilContextTimeout(ctx, defaultPollDuration, defaultPollTimeout, true, h.hasRunningPod(ctx, namespace.Name, deploy.Name)) + if err != nil { + opLog.Error(err, "error waiting for deployments") + } } // remove the 503 code from any ingress objects that have it in this namespace h.removeCodeFromIngress(ctx, namespace.Name, opLog) diff --git a/test/utils/utils.go b/test/utils/utils.go index 0e03708..b760a4e 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -119,7 +119,7 @@ func GetProjectDir() (string, error) { if err != nil { return wd, err } - wd = strings.Replace(wd, "/test/e2e", "", -1) + wd = strings.ReplaceAll(wd, "/test/e2e", "") return wd, nil }