sigs.k8s.io/kubebuilder/v3@v3.14.0/test/e2e/deployimage/plugin_cluster_test.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8     http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package deployimage
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"os/exec"
    23  	"path/filepath"
    24  	"strings"
    25  	"time"
    26  
    27  	"sigs.k8s.io/kubebuilder/v3/pkg/plugin/util"
    28  
    29  	//nolint:golint
    30  	//nolint:revive
    31  	. "github.com/onsi/ginkgo/v2"
    32  
    33  	//nolint:golint
    34  	//nolint:revive
    35  	. "github.com/onsi/gomega"
    36  
    37  	"sigs.k8s.io/kubebuilder/v3/test/e2e/utils"
    38  )
    39  
    40  var _ = Describe("kubebuilder", func() {
    41  	Context("deploy image plugin", func() {
    42  		var kbc *utils.TestContext
    43  
    44  		BeforeEach(func() {
    45  			var err error
    46  			kbc, err = utils.NewTestContext(util.KubebuilderBinName, "GO111MODULE=on")
    47  			Expect(err).NotTo(HaveOccurred())
    48  			Expect(kbc.Prepare()).To(Succeed())
    49  
    50  			By("installing prometheus operator")
    51  			Expect(kbc.InstallPrometheusOperManager()).To(Succeed())
    52  
    53  			By("creating manager namespace")
    54  			err = kbc.CreateManagerNamespace()
    55  			ExpectWithOffset(1, err).NotTo(HaveOccurred())
    56  
    57  			By("labeling all namespaces to warn about restricted")
    58  			err = kbc.LabelAllNamespacesToWarnAboutRestricted()
    59  			ExpectWithOffset(1, err).NotTo(HaveOccurred())
    60  
    61  			By("enforce that namespace where the sample will be applied can only run restricted containers")
    62  			_, err = kbc.Kubectl.Command("label", "--overwrite", "ns", kbc.Kubectl.Namespace,
    63  				"pod-security.kubernetes.io/audit=restricted",
    64  				"pod-security.kubernetes.io/enforce-version=v1.24",
    65  				"pod-security.kubernetes.io/enforce=restricted")
    66  			Expect(err).To(Not(HaveOccurred()))
    67  		})
    68  
    69  		AfterEach(func() {
    70  			By("clean up API objects created during the test")
    71  			kbc.CleanupManifests(filepath.Join("config", "default"))
    72  
    73  			By("uninstalling the Prometheus manager bundle")
    74  			kbc.UninstallPrometheusOperManager()
    75  
    76  			By("removing controller image and working dir")
    77  			kbc.Destroy()
    78  		})
    79  
    80  		It("should generate a runnable project with deploy-image/v1-alpha options ", func() {
    81  			var err error
    82  
    83  			By("initializing a project with go/v3")
    84  			err = kbc.Init(
    85  				"--plugins", "go/v4",
    86  				"--project-version", "3",
    87  				"--domain", kbc.Domain,
    88  			)
    89  			ExpectWithOffset(1, err).NotTo(HaveOccurred())
    90  
    91  			By("creating API definition with deploy-image/v1-alpha plugin")
    92  			err = kbc.CreateAPI(
    93  				"--group", kbc.Group,
    94  				"--version", kbc.Version,
    95  				"--kind", kbc.Kind,
    96  				"--plugins", "deploy-image/v1-alpha",
    97  				"--image", "memcached:1.4.36-alpine",
    98  				"--image-container-port", "11211",
    99  				"--image-container-command", "memcached,-m=64,-o,modern,-v",
   100  				"--run-as-user", "1001",
   101  				"--make=false",
   102  				"--manifests=false",
   103  			)
   104  			ExpectWithOffset(1, err).NotTo(HaveOccurred())
   105  
   106  			By("uncomment kustomization.yaml to enable prometheus")
   107  			ExpectWithOffset(1, util.UncommentCode(
   108  				filepath.Join(kbc.Dir, "config", "default", "kustomization.yaml"),
   109  				"#- ../prometheus", "#")).To(Succeed())
   110  
   111  			By("uncomment kustomize files to ensure that pods are restricted")
   112  			uncommentPodStandards(kbc)
   113  
   114  			Run(kbc)
   115  		})
   116  
   117  		It("should generate a runnable project with deploy-image/v1-alpha without options ", func() {
   118  			var err error
   119  
   120  			By("initializing a project with go/v4")
   121  			err = kbc.Init(
   122  				"--plugins", "go/v4",
   123  				"--project-version", "3",
   124  				"--domain", kbc.Domain,
   125  			)
   126  			ExpectWithOffset(1, err).NotTo(HaveOccurred())
   127  
   128  			By("creating API definition with deploy-image/v1-alpha plugin")
   129  			err = kbc.CreateAPI(
   130  				"--group", kbc.Group,
   131  				"--version", kbc.Version,
   132  				"--kind", kbc.Kind,
   133  				"--plugins", "deploy-image/v1-alpha",
   134  				"--image", "busybox:1.28",
   135  				"--make=false",
   136  				"--manifests=false",
   137  			)
   138  			ExpectWithOffset(1, err).NotTo(HaveOccurred())
   139  
   140  			By("uncomment kustomization.yaml to enable prometheus")
   141  			ExpectWithOffset(1, util.UncommentCode(
   142  				filepath.Join(kbc.Dir, "config", "default", "kustomization.yaml"),
   143  				"#- ../prometheus", "#")).To(Succeed())
   144  
   145  			By("uncomment kustomize files to ensure that pods are restricted")
   146  			uncommentPodStandards(kbc)
   147  
   148  			Run(kbc)
   149  		})
   150  	})
   151  })
   152  
   153  // Run runs a set of e2e tests for a scaffolded project defined by a TestContext.
   154  func Run(kbc *utils.TestContext) {
   155  	var controllerPodName string
   156  	var err error
   157  
   158  	By("updating the go.mod")
   159  	err = kbc.Tidy()
   160  	ExpectWithOffset(1, err).NotTo(HaveOccurred())
   161  
   162  	By("run make manifests")
   163  	err = kbc.Make("manifests")
   164  	ExpectWithOffset(1, err).NotTo(HaveOccurred())
   165  
   166  	By("run make generate")
   167  	err = kbc.Make("generate")
   168  	ExpectWithOffset(1, err).NotTo(HaveOccurred())
   169  
   170  	By("run make all")
   171  	err = kbc.Make("all")
   172  	ExpectWithOffset(1, err).NotTo(HaveOccurred())
   173  
   174  	By("run make install")
   175  	err = kbc.Make("install")
   176  	ExpectWithOffset(1, err).NotTo(HaveOccurred())
   177  
   178  	By("building the controller image")
   179  	err = kbc.Make("docker-build", "IMG="+kbc.ImageName)
   180  	ExpectWithOffset(1, err).NotTo(HaveOccurred())
   181  
   182  	By("loading the controller docker image into the kind cluster")
   183  	err = kbc.LoadImageToKindCluster()
   184  	ExpectWithOffset(1, err).NotTo(HaveOccurred())
   185  
   186  	By("deploying the controller-manager")
   187  	cmd := exec.Command("make", "deploy", "IMG="+kbc.ImageName)
   188  	outputMake, err := kbc.Run(cmd)
   189  	ExpectWithOffset(1, err).NotTo(HaveOccurred())
   190  
   191  	By("validating that manager Pod/container(s) are restricted")
   192  	ExpectWithOffset(1, outputMake).NotTo(ContainSubstring("Warning: would violate PodSecurity"))
   193  
   194  	By("validating that the controller-manager pod is running as expected")
   195  	verifyControllerUp := func() error {
   196  		// Get pod name
   197  		podOutput, err := kbc.Kubectl.Get(
   198  			true,
   199  			"pods", "-l", "control-plane=controller-manager",
   200  			"-o", "go-template={{ range .items }}{{ if not .metadata.deletionTimestamp }}{{ .metadata.name }}"+
   201  				"{{ \"\\n\" }}{{ end }}{{ end }}")
   202  		ExpectWithOffset(2, err).NotTo(HaveOccurred())
   203  		podNames := util.GetNonEmptyLines(podOutput)
   204  		if len(podNames) != 1 {
   205  			return fmt.Errorf("expect 1 controller pods running, but got %d", len(podNames))
   206  		}
   207  		controllerPodName = podNames[0]
   208  		ExpectWithOffset(2, controllerPodName).Should(ContainSubstring("controller-manager"))
   209  
   210  		// Validate pod status
   211  		status, err := kbc.Kubectl.Get(
   212  			true,
   213  			"pods", controllerPodName, "-o", "jsonpath={.status.phase}")
   214  		ExpectWithOffset(2, err).NotTo(HaveOccurred())
   215  		if status != "Running" {
   216  			return fmt.Errorf("controller pod in %s status", status)
   217  		}
   218  		return nil
   219  	}
   220  	defer func() {
   221  		out, err := kbc.Kubectl.CommandInNamespace("describe", "all")
   222  		ExpectWithOffset(1, err).NotTo(HaveOccurred())
   223  		fmt.Fprintln(GinkgoWriter, out)
   224  	}()
   225  	EventuallyWithOffset(1, verifyControllerUp, time.Minute, time.Second).Should(Succeed())
   226  	By("creating an instance of the CR")
   227  	sampleFile := filepath.Join("config", "samples",
   228  		fmt.Sprintf("%s_%s_%s.yaml", kbc.Group, kbc.Version, strings.ToLower(kbc.Kind)))
   229  
   230  	sampleFilePath, err := filepath.Abs(filepath.Join(fmt.Sprintf("e2e-%s", kbc.TestSuffix), sampleFile))
   231  	Expect(err).To(Not(HaveOccurred()))
   232  
   233  	EventuallyWithOffset(1, func() error {
   234  		_, err = kbc.Kubectl.Apply(true, "-f", sampleFilePath)
   235  		return err
   236  	}, time.Minute, time.Second).Should(Succeed())
   237  
   238  	By("validating that pod(s) status.phase=Running")
   239  	getMemcachedPodStatus := func() error {
   240  		status, err := kbc.Kubectl.Get(true, "pods", "-l",
   241  			fmt.Sprintf("app.kubernetes.io/name=%s", kbc.Kind),
   242  			"-o", "jsonpath={.items[*].status}",
   243  		)
   244  		ExpectWithOffset(2, err).NotTo(HaveOccurred())
   245  		if !strings.Contains(status, "\"phase\":\"Running\"") {
   246  			return err
   247  		}
   248  		return nil
   249  	}
   250  	EventuallyWithOffset(1, getMemcachedPodStatus, time.Minute, time.Second).Should(Succeed())
   251  
   252  	By("validating that the status of the custom resource created is updated or not")
   253  	var status string
   254  	getStatus := func() error {
   255  		status, err = kbc.Kubectl.Get(true, strings.ToLower(kbc.Kind),
   256  			strings.ToLower(kbc.Kind)+"-sample",
   257  			"-o", "jsonpath={.status.conditions}")
   258  		ExpectWithOffset(2, err).NotTo(HaveOccurred())
   259  		if !strings.Contains(status, "Available") {
   260  			return errors.New(`status condition with type "Available" should be set`)
   261  		}
   262  		return nil
   263  	}
   264  	Eventually(getStatus, time.Minute, time.Second).Should(Succeed())
   265  
   266  	// Testing the finalizer
   267  	EventuallyWithOffset(1, func() error {
   268  		_, err = kbc.Kubectl.Delete(true, "-f", sampleFilePath)
   269  		return err
   270  	}, time.Minute, time.Second).Should(Succeed())
   271  
   272  	EventuallyWithOffset(1, func() error {
   273  		events, err := kbc.Kubectl.Get(true, "events", "--field-selector=type=Warning",
   274  			"-o", "jsonpath={.items[*].message}",
   275  		)
   276  		ExpectWithOffset(2, err).NotTo(HaveOccurred())
   277  		if !strings.Contains(events, "is being deleted from the namespace") {
   278  			return err
   279  		}
   280  		return nil
   281  	}, time.Minute, time.Second).Should(Succeed())
   282  }
   283  
   284  func uncommentPodStandards(kbc *utils.TestContext) {
   285  	configManager := filepath.Join(kbc.Dir, "config", "manager", "manager.yaml")
   286  
   287  	//nolint:lll
   288  	if err := util.ReplaceInFile(configManager, `# TODO(user): For common cases that do not require escalating privileges
   289          # it is recommended to ensure that all your Pods/Containers are restrictive.
   290          # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted
   291          # Please uncomment the following code if your project does NOT have to work on old Kubernetes
   292          # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ).
   293          # seccompProfile:
   294          #   type: RuntimeDefault`, `seccompProfile:
   295            type: RuntimeDefault`); err == nil {
   296  		ExpectWithOffset(1, err).NotTo(HaveOccurred())
   297  	}
   298  }