github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/topgun/k8s/kubernetes_creds_mgmt_test.go (about)

     1  package k8s_test
     2  
     3  import (
     4  	"encoding/json"
     5  
     6  	. "github.com/pf-qiu/concourse/v6/topgun"
     7  	. "github.com/onsi/ginkgo"
     8  	. "github.com/onsi/gomega"
     9  )
    10  
    11  var _ = Describe("Kubernetes credential management", func() {
    12  	var (
    13  		atc                Endpoint
    14  		username, password = "test", "test"
    15  	)
    16  
    17  	BeforeEach(func() {
    18  		setReleaseNameAndNamespace("k8s-cm")
    19  	})
    20  
    21  	AfterEach(func() {
    22  		atc.Close()
    23  		cleanupReleases()
    24  	})
    25  
    26  	JustBeforeEach(func() {
    27  		atc = waitAndLogin(namespace, releaseName+"-web")
    28  	})
    29  
    30  	Context("/api/v1/info/creds", func() {
    31  		var parsedResponse struct {
    32  			Kubernetes struct {
    33  				ConfigPath      string `json:"config_path"`
    34  				InClusterConfig bool   `json:"in_cluster_config"`
    35  				NamespaceConfig string `json:"namespace_config"`
    36  			} `json:"kubernetes"`
    37  		}
    38  
    39  		BeforeEach(func() {
    40  			deployConcourseChart(releaseName, "--set=worker.replicas=1")
    41  		})
    42  
    43  		It("Contains kubernetes config", func() {
    44  			token, err := FetchToken("http://"+atc.Address(), username, password)
    45  			Expect(err).ToNot(HaveOccurred())
    46  
    47  			body, err := RequestCredsInfo("http://"+atc.Address(), token.AccessToken)
    48  			Expect(err).ToNot(HaveOccurred())
    49  
    50  			err = json.Unmarshal(body, &parsedResponse)
    51  			Expect(err).ToNot(HaveOccurred())
    52  
    53  			Expect(parsedResponse.Kubernetes.ConfigPath).To(BeEmpty())
    54  			Expect(parsedResponse.Kubernetes.InClusterConfig).To(BeTrue())
    55  			Expect(parsedResponse.Kubernetes.NamespaceConfig).To(Equal(releaseName + "-"))
    56  		})
    57  	})
    58  
    59  	Context("Consuming k8s credentials", func() {
    60  		var cachingSetup = func() {
    61  			deployConcourseChart(releaseName, "--set=worker.replicas=1",
    62  				"--set=concourse.web.secretCacheEnabled=true",
    63  				"--set=concourse.web.secretCacheDuration=600s",
    64  			)
    65  		}
    66  
    67  		var disableTeamNamespaces = func() {
    68  			By("creating a namespace made by the user instead of the chart")
    69  			Run(nil, "kubectl", "create", "namespace", releaseName+"-main")
    70  
    71  			By("binding the role that grants access to the secrets in our newly created namespace ")
    72  			Run(nil,
    73  				"kubectl", "create",
    74  				"--namespace", releaseName+"-main",
    75  				"rolebinding", "rb",
    76  				"--clusterrole", releaseName+"-web",
    77  				"--serviceaccount", releaseName+":"+releaseName+"-web",
    78  			)
    79  
    80  			deployConcourseChart(releaseName, "--set=worker.replicas=1",
    81  				"--set=concourse.web.secretCacheEnabled=true",
    82  				"--set=concourse.web.secretCacheDuration=600s",
    83  				"--set=concourse.web.kubernetes.createTeamNamespaces=false",
    84  			)
    85  		}
    86  
    87  		Context("using per-team credentials", func() {
    88  
    89  			const (
    90  				secretNameFoo = "foo"
    91  				secretNameCaz = "caz"
    92  			)
    93  
    94  			Context("using the default namespace created by the chart", func() {
    95  				BeforeEach(func() {
    96  					deployConcourseChart(releaseName, "--set=worker.replicas=1")
    97  				})
    98  
    99  				It("succeeds", func() {
   100  					runsBuildWithCredentialsResolved(secretNameFoo, secretNameCaz)
   101  				})
   102  			})
   103  
   104  			Context("with caching enabled", func() {
   105  				BeforeEach(cachingSetup)
   106  
   107  				It("gets cached credentials", func() {
   108  					runGetsCachedCredentials(secretNameFoo, secretNameCaz)
   109  				})
   110  			})
   111  
   112  			Context("using a user-provided namespace", func() {
   113  				BeforeEach(disableTeamNamespaces)
   114  
   115  				It("succeeds", func() {
   116  					runsBuildWithCredentialsResolved(secretNameFoo, secretNameCaz)
   117  				})
   118  
   119  				AfterEach(func() {
   120  					Run(nil, "kubectl", "delete", "namespace", releaseName+"-main", "--wait=false")
   121  				})
   122  			})
   123  
   124  		})
   125  
   126  		Context("using per-pipeline credentials", func() {
   127  
   128  			const (
   129  				secretNameFoo = "pipeline.foo"
   130  				secretNameCaz = "pipeline.caz"
   131  			)
   132  
   133  			Context("using the default namespace created by the chart", func() {
   134  				BeforeEach(func() {
   135  					deployConcourseChart(releaseName, "--set=worker.replicas=1")
   136  				})
   137  
   138  				It("succeeds", func() {
   139  					runsBuildWithCredentialsResolved(secretNameFoo, secretNameCaz)
   140  				})
   141  			})
   142  
   143  			Context("with caching enabled", func() {
   144  				BeforeEach(cachingSetup)
   145  
   146  				It("gets cached credentials", func() {
   147  					runGetsCachedCredentials(secretNameFoo, secretNameCaz)
   148  				})
   149  			})
   150  
   151  			Context("using a user-provided namespace", func() {
   152  				BeforeEach(disableTeamNamespaces)
   153  
   154  				It("succeeds", func() {
   155  					runsBuildWithCredentialsResolved(secretNameFoo, secretNameCaz)
   156  				})
   157  
   158  				AfterEach(func() {
   159  					Run(nil, "kubectl", "delete", "namespace", releaseName+"-main", "--wait=false")
   160  				})
   161  			})
   162  		})
   163  	})
   164  
   165  	Context("one-off build", func() {
   166  		BeforeEach(func() {
   167  			deployConcourseChart(releaseName, "--set=worker.replicas=1")
   168  		})
   169  
   170  		It("runs the one-off build successfully", func() {
   171  			By("creating the secret in the main team")
   172  			createCredentialSecret(releaseName, "some-secret", "main", map[string]string{"value": "mysecret"})
   173  
   174  			By("successfully running the one-off build")
   175  			fly.Run("execute",
   176  				"-c", "tasks/simple-secret.yml")
   177  		})
   178  
   179  		It("one-off build fails", func() {
   180  			By("not creating the secret")
   181  			sess := fly.Start("execute",
   182  				"-c", "tasks/simple-secret.yml")
   183  			<-sess.Exited
   184  			Expect(sess.ExitCode()).NotTo(Equal(0))
   185  		})
   186  	})
   187  
   188  })
   189  
   190  func deleteSecret(releaseName, team, secretName string) {
   191  	Run(nil, "kubectl", "--namespace="+releaseName+"-main", "delete", "secret", secretName)
   192  }
   193  
   194  func createCredentialSecret(releaseName, secretName, team string, kv map[string]string) {
   195  	args := []string{
   196  		"create",
   197  		"secret",
   198  		"generic",
   199  		secretName,
   200  		"--namespace=" + releaseName + "-" + team,
   201  	}
   202  
   203  	for key, value := range kv {
   204  		args = append(args, "--from-literal="+key+"="+value)
   205  	}
   206  
   207  	Run(nil, "kubectl", args...)
   208  }
   209  
   210  func runsBuildWithCredentialsResolved(normalSecret string, specialKeySecret string) {
   211  	By("creating credentials in k8s credential manager")
   212  	createCredentialSecret(releaseName, normalSecret, "main", map[string]string{"value": "bar"})
   213  	createCredentialSecret(releaseName, specialKeySecret, "main", map[string]string{"baz": "zaz"})
   214  
   215  	fly.Run("set-pipeline", "-n",
   216  		"-c", "pipelines/minimal-credential-management.yml",
   217  		"-p", "pipeline",
   218  	)
   219  
   220  	fly.Run("unpause-pipeline", "-p", "pipeline")
   221  
   222  	session := fly.Start("trigger-job", "-j", "pipeline/unit", "-w")
   223  	Wait(session)
   224  
   225  	By("seeing the credentials were resolved by concourse")
   226  	Expect(string(session.Out.Contents())).To(ContainSubstring("bar"))
   227  	Expect(string(session.Out.Contents())).To(ContainSubstring("zaz"))
   228  }
   229  
   230  func runGetsCachedCredentials(secretNameFoo string, secretNameCaz string) {
   231  	runsBuildWithCredentialsResolved(secretNameFoo, secretNameCaz)
   232  	deleteSecret(releaseName, "main", secretNameFoo)
   233  	deleteSecret(releaseName, "main", secretNameCaz)
   234  	By("seeing that concourse uses the cached credentials")
   235  	runsBuildWithCredentialsResolved(secretNameFoo, secretNameCaz)
   236  }