Ver código fonte

output message for resolved sa

Alexander Belanger 5 anos atrás
pai
commit
9f4babb4e7

+ 34 - 0
cli/cmd/api/project.go

@@ -40,6 +40,40 @@ func (c *Client) GetProject(ctx context.Context, projectID uint) (*GetProjectRes
 	return bodyResp, nil
 }
 
+// GetProjectServiceAccountResponse is the response returned after querying for a
+// given project's service account
+type GetProjectServiceAccountResponse models.ServiceAccountExternal
+
+// GetProjectServiceAccount retrieves a project's service account by id
+func (c *Client) GetProjectServiceAccount(
+	ctx context.Context,
+	projectID uint,
+	saID uint,
+) (*GetProjectServiceAccountResponse, error) {
+	req, err := http.NewRequest(
+		"GET",
+		fmt.Sprintf("%s/projects/%d/serviceAccounts/%d", c.BaseURL, projectID, saID),
+		nil,
+	)
+
+	if err != nil {
+		return nil, err
+	}
+
+	req = req.WithContext(ctx)
+	bodyResp := &GetProjectServiceAccountResponse{}
+
+	if httpErr, err := c.sendRequest(req, bodyResp, true); httpErr != nil || err != nil {
+		if httpErr != nil {
+			return nil, fmt.Errorf("code %d, errors %v", httpErr.Code, httpErr.Errors)
+		}
+
+		return nil, err
+	}
+
+	return bodyResp, nil
+}
+
 // ListProjectClustersResponse lists the linked clusters for a project
 type ListProjectClustersResponse []models.ClusterExternal
 

+ 49 - 0
cli/cmd/api/project_test.go

@@ -142,6 +142,55 @@ func TestGetProject(t *testing.T) {
 	}
 }
 
+func TestGetProjectServiceAccount(t *testing.T) {
+	email := "get_project_sa_test@example.com"
+	client := api.NewClient(baseURL, "cookie_get_project_sa_test.json")
+	user := initUser(email, client, t)
+	client.Login(context.Background(), &api.LoginRequest{
+		Email:    user.Email,
+		Password: "hello1234",
+	})
+	project := initProject("project-test", client, t)
+	saCandidate := initProjectCandidate(project.ID, OIDCAuthWithoutData, client, t)
+	sa := initProjectSA(project.ID, saCandidate.ID, client, t)
+
+	resp, err := client.GetProjectServiceAccount(context.Background(), project.ID, sa.ID)
+
+	if err != nil {
+		t.Fatalf("%v\n", err)
+	}
+
+	// ensure project id and metadata is correct
+	if resp.ProjectID != project.ID {
+		t.Errorf("project id incorrect: expected %d, got %d\n", project.ID, resp.ProjectID)
+	}
+
+	if resp.Kind != "connector" {
+		t.Errorf("service account kind incorrect: expected %s, got %s\n", "connector", resp.Kind)
+	}
+
+	if resp.AuthMechanism != models.OIDC {
+		t.Errorf("service account auth mechanism incorrect: expected %s, got %s\n", models.OIDC, resp.AuthMechanism)
+	}
+
+	// verify clusters
+	if len(resp.Clusters) != 1 {
+		t.Fatalf("length of clusters is not 1")
+	}
+
+	if resp.Clusters[0].ServiceAccountID != resp.ID {
+		t.Errorf("cluster's sa id is incorrect: expected %d, got %d\n", resp.ID, resp.Clusters[0].ServiceAccountID)
+	}
+
+	if resp.Clusters[0].Name != "cluster-test" {
+		t.Errorf("cluster's name is incorrect: expected %s, got %s\n", "cluster-test", resp.Clusters[0].Name)
+	}
+
+	if resp.Clusters[0].Server != "https://localhost" {
+		t.Errorf("cluster's name is incorrect: expected %s, got %s\n", "https://localhost", resp.Clusters[0].Server)
+	}
+}
+
 func TestCreateProjectCandidates(t *testing.T) {
 	email := "create_project_candidates_test@example.com"
 	client := api.NewClient(baseURL, "cookie_create_project_candidates_test.json")

+ 35 - 16
cli/cmd/connect/kubeconfig.go

@@ -52,6 +52,9 @@ func Kubeconfig(
 	}
 
 	for _, saCandidate := range saCandidates {
+		var clusters []models.ClusterExternal
+		var saID uint
+
 		if len(saCandidate.Actions) > 0 {
 			resolvers := make(api.CreateProjectServiceAccountRequest, 0)
 
@@ -134,25 +137,41 @@ func Kubeconfig(
 				return err
 			}
 
-			for _, cluster := range sa.Clusters {
-				color.New(color.FgGreen).Printf("created service account for cluster %s with id %d\n", cluster.Name, sa.ID)
+			clusters = sa.Clusters
+			saID = sa.ID
+		} else {
+			sa, err := client.GetProjectServiceAccount(
+				context.Background(),
+				projectID,
+				saCandidate.CreatedServiceAccountID,
+			)
+
+			if err != nil {
+				return err
+			}
+
+			clusters = sa.Clusters
+			saID = sa.ID
+		}
 
-				// sanity check to ensure it's working
-				namespaces, err := client.GetK8sNamespaces(
-					context.Background(),
-					projectID,
-					sa.ID,
-					cluster.ID,
-				)
+		for _, cluster := range clusters {
+			color.New(color.FgGreen).Printf("created service account for cluster %s with id %d\n", cluster.Name, saID)
 
-				if err != nil {
-					return err
-				}
+			// sanity check to ensure it's working
+			// namespaces, err := client.GetK8sNamespaces(
+			// 	context.Background(),
+			// 	projectID,
+			// 	saID,
+			// 	cluster.ID,
+			// )
 
-				for _, ns := range namespaces.Items {
-					fmt.Println(ns.ObjectMeta.GetName())
-				}
-			}
+			// if err != nil {
+			// 	return err
+			// }
+
+			// for _, ns := range namespaces.Items {
+			// 	fmt.Println(ns.ObjectMeta.GetName())
+			// }
 		}
 	}
 

+ 26 - 0
server/api/project_handler.go

@@ -110,6 +110,32 @@ func (app *App) HandleReadProject(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
+// HandleReadProjectServiceAccount reads a service account by id
+func (app *App) HandleReadProjectServiceAccount(w http.ResponseWriter, r *http.Request) {
+	id, err := strconv.ParseUint(chi.URLParam(r, "service_account_id"), 0, 64)
+
+	if err != nil || id == 0 {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	sa, err := app.repo.ServiceAccount.ReadServiceAccount(uint(id))
+
+	if err != nil {
+		app.handleErrorRead(err, ErrProjectDataRead, w)
+		return
+	}
+
+	saExt := sa.Externalize()
+
+	w.WriteHeader(http.StatusOK)
+
+	if err := json.NewEncoder(w).Encode(saExt); err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+}
+
 // HandleListProjectClusters returns a list of clusters that have linked ServiceAccounts.
 // If multiple service accounts exist for a cluster, the service account created later
 // will take precedence. This may be changed in a future release to return multiple

+ 24 - 0
server/api/project_handler_test.go

@@ -117,6 +117,30 @@ func TestHandleReadProject(t *testing.T) {
 	testProjRequests(t, readProjectTests, true)
 }
 
+var readProjectSATest = []*projTest{
+	&projTest{
+		initializers: []func(t *tester){
+			initUserDefault,
+			initProject,
+			initProjectSADefault,
+		},
+		msg:       "Read project service account",
+		method:    "GET",
+		endpoint:  "/api/projects/1/serviceAccounts/1",
+		body:      ``,
+		expStatus: http.StatusOK,
+		expBody:   `{"id":1,"project_id":1,"kind":"connector","clusters":[{"id":1,"service_account_id":1,"name":"cluster-test","server":"https://localhost"}],"auth_mechanism":"oidc"}`,
+		useCookie: true,
+		validators: []func(c *projTest, tester *tester, t *testing.T){
+			projectSABodyValidator,
+		},
+	},
+}
+
+func TestHandleReadProjectSA(t *testing.T) {
+	testProjRequests(t, readProjectSATest, true)
+}
+
 var listProjectClustersTest = []*projTest{
 	&projTest{
 		initializers: []func(t *tester){

+ 14 - 0
server/router/router.go

@@ -68,6 +68,20 @@ func New(
 			),
 		)
 
+		r.Method(
+			"GET",
+			"/projects/{project_id}/serviceAccounts/{service_account_id}",
+			auth.DoesUserHaveProjectAccess(
+				auth.DoesUserHaveServiceAccountAccess(
+					requestlog.NewHandler(a.HandleReadProjectServiceAccount, l),
+					mw.URLParam,
+					mw.URLParam,
+				),
+				mw.URLParam,
+				mw.ReadAccess,
+			),
+		)
+
 		r.Method(
 			"GET",
 			"/projects/{project_id}/clusters",