Przeglądaj źródła

fix merge conflicts

Anukul Sangwan 4 lat temu
rodzic
commit
b4b4813003

+ 195 - 0
api/server/handlers/infra/delete.go

@@ -0,0 +1,195 @@
+package infra
+
+import (
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/kubernetes"
+	"github.com/porter-dev/porter/internal/kubernetes/provisioner"
+	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/internal/repository"
+)
+
+type InfraDeleteHandler struct {
+	handlers.PorterHandlerReadWriter
+}
+
+func NewInfraDeleteHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *InfraDeleteHandler {
+	return &InfraDeleteHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+	}
+}
+
+func (c *InfraDeleteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	infra, _ := r.Context().Value(types.InfraScope).(*models.Infra)
+
+	request := &types.DeleteInfraRequest{}
+
+	if ok := c.DecodeAndValidate(w, r, request); !ok {
+		return
+	}
+
+	infra.Status = types.StatusDestroying
+	infra, err := c.Repo().Infra().UpdateInfra(infra)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	switch infra.Kind {
+	case types.InfraECR:
+		err = destroyECR(c.Repo(), c.Config(), infra, request.Name)
+	case types.InfraEKS:
+		err = destroyEKS(c.Repo(), c.Config(), infra, request.Name)
+	case types.InfraDOCR:
+		err = destroyDOCR(c.Repo(), c.Config(), infra, request.Name)
+	case types.InfraDOKS:
+		err = destroyDOKS(c.Repo(), c.Config(), infra, request.Name)
+	case types.InfraGKE:
+		err = destroyGKE(c.Repo(), c.Config(), infra, request.Name)
+	}
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+}
+
+func destroyECR(repo repository.Repository, conf *config.Config, infra *models.Infra, name string) error {
+	awsInt, err := repo.AWSIntegration().ReadAWSIntegration(infra.ProjectID, infra.AWSIntegrationID)
+
+	if err != nil {
+		return err
+	}
+
+	_, err = conf.ProvisionerAgent.ProvisionECR(
+		&kubernetes.SharedProvisionOpts{
+			ProjectID:           infra.ProjectID,
+			Repo:                repo,
+			Infra:               infra,
+			Operation:           provisioner.Destroy,
+			PGConf:              conf.DBConf,
+			RedisConf:           conf.RedisConf,
+			ProvImageTag:        conf.ServerConf.ProvisionerImageTag,
+			ProvImagePullSecret: conf.ServerConf.ProvisionerImagePullSecret,
+		},
+		awsInt,
+		name,
+	)
+
+	return err
+}
+
+func destroyEKS(repo repository.Repository, conf *config.Config, infra *models.Infra, name string) error {
+	awsInt, err := repo.AWSIntegration().ReadAWSIntegration(infra.ProjectID, infra.AWSIntegrationID)
+
+	if err != nil {
+		return err
+	}
+
+	_, err = conf.ProvisionerAgent.ProvisionEKS(
+		&kubernetes.SharedProvisionOpts{
+			ProjectID:           infra.ProjectID,
+			Repo:                repo,
+			Infra:               infra,
+			Operation:           provisioner.Destroy,
+			PGConf:              conf.DBConf,
+			RedisConf:           conf.RedisConf,
+			ProvImageTag:        conf.ServerConf.ProvisionerImageTag,
+			ProvImagePullSecret: conf.ServerConf.ProvisionerImagePullSecret,
+		},
+		awsInt,
+		name,
+		"",
+	)
+
+	return err
+}
+
+func destroyDOCR(repo repository.Repository, conf *config.Config, infra *models.Infra, name string) error {
+	doInt, err := repo.OAuthIntegration().ReadOAuthIntegration(infra.ProjectID, infra.DOIntegrationID)
+
+	if err != nil {
+		return err
+	}
+
+	_, err = conf.ProvisionerAgent.ProvisionDOCR(
+		&kubernetes.SharedProvisionOpts{
+			ProjectID:           infra.ProjectID,
+			Repo:                repo,
+			Infra:               infra,
+			Operation:           provisioner.Destroy,
+			PGConf:              conf.DBConf,
+			RedisConf:           conf.RedisConf,
+			ProvImageTag:        conf.ServerConf.ProvisionerImageTag,
+			ProvImagePullSecret: conf.ServerConf.ProvisionerImagePullSecret,
+		},
+		doInt,
+		conf.DOConf,
+		name,
+		"",
+	)
+
+	return err
+}
+
+func destroyDOKS(repo repository.Repository, conf *config.Config, infra *models.Infra, name string) error {
+	doInt, err := repo.OAuthIntegration().ReadOAuthIntegration(infra.ProjectID, infra.DOIntegrationID)
+
+	if err != nil {
+		return err
+	}
+
+	_, err = conf.ProvisionerAgent.ProvisionDOKS(
+		&kubernetes.SharedProvisionOpts{
+			ProjectID:           infra.ProjectID,
+			Repo:                repo,
+			Infra:               infra,
+			Operation:           provisioner.Destroy,
+			PGConf:              conf.DBConf,
+			RedisConf:           conf.RedisConf,
+			ProvImageTag:        conf.ServerConf.ProvisionerImageTag,
+			ProvImagePullSecret: conf.ServerConf.ProvisionerImagePullSecret,
+		},
+		doInt,
+		conf.DOConf,
+		"",
+		name,
+	)
+
+	return err
+}
+
+func destroyGKE(repo repository.Repository, conf *config.Config, infra *models.Infra, name string) error {
+	gcpInt, err := repo.GCPIntegration().ReadGCPIntegration(infra.ProjectID, infra.GCPIntegrationID)
+
+	if err != nil {
+		return err
+	}
+
+	_, err = conf.ProvisionerAgent.ProvisionGKE(
+		&kubernetes.SharedProvisionOpts{
+			ProjectID:           infra.ProjectID,
+			Repo:                repo,
+			Infra:               infra,
+			Operation:           provisioner.Destroy,
+			PGConf:              conf.DBConf,
+			RedisConf:           conf.RedisConf,
+			ProvImageTag:        conf.ServerConf.ProvisionerImageTag,
+			ProvImagePullSecret: conf.ServerConf.ProvisionerImagePullSecret,
+		},
+		gcpInt,
+		name,
+	)
+
+	return err
+}

+ 52 - 0
api/server/handlers/infra/stream_logs.go

@@ -0,0 +1,52 @@
+package infra
+
+import (
+	"net/http"
+
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/adapter"
+	"github.com/porter-dev/porter/internal/kubernetes/provisioner"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+type InfraStreamLogsHandler struct {
+	handlers.PorterHandlerWriter
+}
+
+func NewInfraStreamLogsHandler(
+	config *config.Config,
+	writer shared.ResultWriter,
+) *InfraStreamLogsHandler {
+	return &InfraStreamLogsHandler{
+		PorterHandlerWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
+	}
+}
+
+func (c *InfraStreamLogsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	conn, err := c.Config().WSUpgrader.Upgrade(w, r, nil)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	infra, _ := r.Context().Value(types.InfraScope).(*models.Infra)
+
+	client, err := adapter.NewRedisClient(c.Config().RedisConf)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	err = provisioner.ResourceStream(client, infra.GetUniqueName(), conn)
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+}

+ 57 - 0
api/server/router/infra.go

@@ -80,5 +80,62 @@ func getInfraRoutes(
 		Router:   r,
 	})
 
+	// GET /api/projects/{project_id}/infras/{infra_id}/logs -> infra.NewInfraStreamLogsHandler
+	streamLogsEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/logs",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.InfraScope,
+			},
+		},
+	)
+
+	streamLogsHandler := infra.NewInfraGetHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: streamLogsEndpoint,
+		Handler:  streamLogsHandler,
+		Router:   r,
+	})
+
+	// DELETE /api/projects/{project_id}/infras/{infra_id} -> infra.NewInfraDeleteHandler
+	deleteEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbDelete,
+			Method: types.HTTPVerbDelete,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath,
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.InfraScope,
+			},
+		},
+	)
+
+	deleteHandler := infra.NewInfraDeleteHandler(
+		config,
+		factory.GetDecoderValidator(),
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: deleteEndpoint,
+		Handler:  deleteHandler,
+		Router:   r,
+	})
+
 	return routes, newPath
 }

+ 3 - 65
api/types/provision.go

@@ -6,17 +6,6 @@ type CreateECRInfraRequest struct {
 	AWSIntegrationID uint   `json:"aws_integration_id" form:"required"`
 }
 
-// ToInfra converts the form to a gorm aws infra model
-// func (ce *CreateECRInfra) ToInfra() (*models.Infra, error) {
-// 	return &models.Infra{
-// 		Kind:             types.InfraECR,
-// 		ProjectID:        ce.ProjectID,
-// 		Suffix:           stringWithCharset(6, randCharset),
-// 		Status:           types.StatusCreating,
-// 		AWSIntegrationID: ce.AWSIntegrationID,
-// 	}, nil
-// }
-
 type CreateEKSInfraRequest struct {
 	EKSName          string `json:"eks_name" form:"required"`
 	MachineType      string `json:"machine_type"`
@@ -24,50 +13,17 @@ type CreateEKSInfraRequest struct {
 	AWSIntegrationID uint   `json:"aws_integration_id" form:"required"`
 }
 
-// ToInfra converts the form to a gorm aws infra model
-// func (ce *CreateEKSInfra) ToInfra() (*models.Infra, error) {
-// 	return &models.Infra{
-// 		Kind:             types.InfraEKS,
-// 		ProjectID:        ce.ProjectID,
-// 		Suffix:           stringWithCharset(6, randCharset),
-// 		Status:           types.StatusCreating,
-// 		AWSIntegrationID: ce.AWSIntegrationID,
-// 	}, nil
-// }
-
 type CreateGCRInfraRequest struct {
 	ProjectID        uint `json:"project_id" form:"required"`
 	GCPIntegrationID uint `json:"gcp_integration_id" form:"required"`
 }
 
-// ToInfra converts the form to a gorm aws infra model
-// func (ce *CreateGCRInfra) ToInfra() (*models.Infra, error) {
-// 	return &models.Infra{
-// 		Kind:             types.InfraGCR,
-// 		ProjectID:        ce.ProjectID,
-// 		Suffix:           stringWithCharset(6, randCharset),
-// 		Status:           types.StatusCreating,
-// 		GCPIntegrationID: ce.GCPIntegrationID,
-// 	}, nil
-// }
-
 type CreateGKEInfraRequest struct {
 	GKEName          string `json:"gke_name" form:"required"`
 	ProjectID        uint   `json:"project_id" form:"required"`
 	GCPIntegrationID uint   `json:"gcp_integration_id" form:"required"`
 }
 
-// ToInfra converts the form to a gorm aws infra model
-// func (ce *CreateGKEInfra) ToInfra() (*models.Infra, error) {
-// 	return &models.Infra{
-// 		Kind:             types.InfraGKE,
-// 		ProjectID:        ce.ProjectID,
-// 		Suffix:           stringWithCharset(6, randCharset),
-// 		Status:           types.StatusCreating,
-// 		GCPIntegrationID: ce.GCPIntegrationID,
-// 	}, nil
-// }
-
 type CreateDOCRInfraRequest struct {
 	DOCRName             string `json:"docr_name" form:"required"`
 	DOCRSubscriptionTier string `json:"docr_subscription_tier" form:"required"`
@@ -75,17 +31,6 @@ type CreateDOCRInfraRequest struct {
 	DOIntegrationID      uint   `json:"do_integration_id" form:"required"`
 }
 
-// ToInfra converts the form to a gorm infra model
-// func (de *CreateDOCRInfra) ToInfra() (*models.Infra, error) {
-// 	return &models.Infra{
-// 		Kind:            types.InfraDOCR,
-// 		ProjectID:       de.ProjectID,
-// 		Suffix:          stringWithCharset(6, randCharset),
-// 		Status:          types.StatusCreating,
-// 		DOIntegrationID: de.DOIntegrationID,
-// 	}, nil
-// }
-
 type CreateDOKSInfraRequest struct {
 	DORegion        string `json:"do_region" form:"required"`
 	DOKSName        string `json:"doks_name" form:"required"`
@@ -93,13 +38,6 @@ type CreateDOKSInfraRequest struct {
 	DOIntegrationID uint   `json:"do_integration_id" form:"required"`
 }
 
-// ToInfra converts the form to a gorm infra model
-// func (de *CreateDOKSInfra) ToInfra() (*models.Infra, error) {
-// 	return &models.Infra{
-// 		Kind:            types.InfraDOKS,
-// 		ProjectID:       de.ProjectID,
-// 		Suffix:          stringWithCharset(6, randCharset),
-// 		Status:          types.StatusCreating,
-// 		DOIntegrationID: de.DOIntegrationID,
-// 	}, nil
-// }
+type DeleteInfraRequest struct {
+	Name string `json:"name" form:"required"`
+}

+ 6 - 45
dashboard/src/main/home/Home.tsx

@@ -428,55 +428,16 @@ class Home extends Component<PropsType, StateType> {
           if (!cluster.infra_id) continue;
 
           // Handle destroying infra we've provisioned
-          switch (cluster.service) {
-            case "eks":
-              api
-                .destroyEKS(
-                  "<token>",
-                  { eks_name: cluster.name },
+          api.destroyInfra(
+            "<token>",
+                  { name: cluster.name },
                   {
                     project_id: currentProject.id,
                     infra_id: cluster.infra_id,
                   }
-                )
-                .then(() =>
-                  console.log("destroyed provisioned infra:", cluster.infra_id)
-                )
-                .catch(console.log);
-              break;
-
-            case "gke":
-              api
-                .destroyGKE(
-                  "<token>",
-                  { gke_name: cluster.name },
-                  {
-                    project_id: currentProject.id,
-                    infra_id: cluster.infra_id,
-                  }
-                )
-                .then(() =>
-                  console.log("destroyed provisioned infra:", cluster.infra_id)
-                )
-                .catch(console.log);
-              break;
-
-            case "doks":
-              api
-                .destroyDOKS(
-                  "<token>",
-                  { doks_name: cluster.name },
-                  {
-                    project_id: currentProject.id,
-                    infra_id: cluster.infra_id,
-                  }
-                )
-                .then(() =>
-                  console.log("destroyed provisioned infra:", cluster.infra_id)
-                )
-                .catch(console.log);
-              break;
-          }
+          ).then(() =>
+            console.log("destroyed provisioned infra:", cluster.infra_id)
+          ).catch(console.log);
         }
       })
       .catch(console.log);

+ 6 - 38
dashboard/src/main/home/modals/UpdateClusterModal.tsx

@@ -59,48 +59,16 @@ class UpdateClusterModal extends Component<PropsType, StateType> {
         }
 
         // Handle destroying infra we've provisioned
-        switch (currentCluster.service) {
-          case "eks":
-            api
-              .destroyEKS(
-                "<token>",
-                { eks_name: currentCluster.name },
+        api.destroyInfra(
+          "<token>",
+                { name: currentCluster.name },
                 {
                   project_id: currentProject.id,
                   infra_id: currentCluster.infra_id,
                 }
-              )
-              .then(() => console.log("destroyed provisioned infra."))
-              .catch(this.catchErr);
-            break;
-          case "gke":
-            api
-              .destroyGKE(
-                "<token>",
-                { gke_name: currentCluster.name },
-                {
-                  project_id: currentProject.id,
-                  infra_id: currentCluster.infra_id,
-                }
-              )
-              .then(() => console.log("destroyed provisioned infra."))
-              .catch(this.catchErr);
-            break;
-
-          case "doks":
-            api
-              .destroyDOKS(
-                "<token>",
-                { doks_name: currentCluster.name },
-                {
-                  project_id: currentProject.id,
-                  infra_id: currentCluster.infra_id,
-                }
-              )
-              .then(() => console.log("destroyed provisioned infra."))
-              .catch(this.catchErr);
-            break;
-        }
+        ).then(() =>
+          console.log("destroyed provisioned infra:", currentCluster.infra_id)
+        ).catch(console.log);
 
         this.props.setRefreshClusters(true);
         this.setState({ status: "successful", showDeleteOverlay: false });

+ 1 - 1
dashboard/src/main/home/provisioner/ProvisionerLogs.tsx

@@ -193,7 +193,7 @@ class ProvisionerLogs extends Component<PropsType, StateType> {
 
     let protocol = window.location.protocol == "https:" ? "wss" : "ws";
     this.ws = new WebSocket(
-      `${protocol}://${window.location.host}/api/projects/${currentProject.id}/provision/${selectedInfra.kind}/${selectedInfra.id}/logs`
+      `${protocol}://${window.location.host}/api/projects/${currentProject.id}/infras/${selectedInfra.id}/logs`
     );
 
     this.setupWebsocket();

+ 11 - 49
dashboard/src/shared/api.tsx

@@ -330,18 +330,6 @@ const deployAddon = baseApi<
   return `/api/projects/${id}/clusters/${cluster_id}/namespaces/${namespace}/addons?repo_url=${repo_url}`;
 });
 
-const destroyCluster = baseApi<
-  {
-    eks_name: string;
-  },
-  {
-    project_id: number;
-    infra_id: number;
-  }
->("POST", (pathParams) => {
-  return `/api/projects/${pathParams.project_id}/infra/${pathParams.infra_id}/eks/destroy`;
-});
-
 const detectBuildpack = baseApi<
   {},
   {
@@ -687,40 +675,16 @@ const getReleaseToken = baseApi<
   return `/api/projects/${id}/clusters/${cluster_id}/namespaces/${namespace}/releases/${name}/webhook`;
 });
 
-const destroyEKS = baseApi<
-  {
-    eks_name: string;
-  },
-  {
-    project_id: number;
-    infra_id: number;
-  }
->("POST", (pathParams) => {
-  return `/api/projects/${pathParams.project_id}/infra/${pathParams.infra_id}/eks/destroy`;
-});
-
-const destroyGKE = baseApi<
-  {
-    gke_name: string;
-  },
-  {
-    project_id: number;
-    infra_id: number;
-  }
->("POST", (pathParams) => {
-  return `/api/projects/${pathParams.project_id}/infra/${pathParams.infra_id}/gke/destroy`;
-});
-
-const destroyDOKS = baseApi<
-  {
-    doks_name: string;
-  },
-  {
-    project_id: number;
-    infra_id: number;
-  }
->("POST", (pathParams) => {
-  return `/api/projects/${pathParams.project_id}/infra/${pathParams.infra_id}/doks/destroy`;
+const destroyInfra = baseApi<
+{
+  name: string;
+},
+{
+  project_id: number;
+  infra_id: number;
+}
+>("DELETE", (pathParams) => {
+return `/api/projects/${pathParams.project_id}/infras/${pathParams.infra_id}`;
 });
 
 const getRepoIntegrations = baseApi("GET", "/api/integrations/repo");
@@ -1080,9 +1044,7 @@ export default {
   createSubdomain,
   deployTemplate,
   deployAddon,
-  destroyEKS,
-  destroyGKE,
-  destroyDOKS,
+  destroyInfra,
   detectBuildpack,
   getBranchContents,
   getBranches,

+ 7 - 7
docs/developing/backend-refactor-status.md

@@ -61,17 +61,17 @@
 | <li>- [ ] `POST /api/projects/{project_id}/helmrepos`                                                                       |             |                 |             |                  |
 | <li>- [ ] `GET /api/projects/{project_id}/helmrepos/{helm_id}/charts`                                                       |             |                 |             |                  |
 | <li>- [x] `GET /api/projects/{project_id}/infra`                                                                            | AS          |                 |             | yes              |
-| <li>- [ ] `POST /api/projects/{project_id}/infra/{infra_id}/docr/destroy`                                                   |             |                 |             |                  |
-| <li>- [ ] `POST /api/projects/{project_id}/infra/{infra_id}/doks/destroy`                                                   |             |                 |             |                  |
-| <li>- [ ] `POST /api/projects/{project_id}/infra/{infra_id}/ecr/destroy`                                                    |             |                 |             |                  |
-| <li>- [ ] `POST /api/projects/{project_id}/infra/{infra_id}/eks/destroy`                                                    |             |                 |             |                  |
-| <li>- [ ] `POST /api/projects/{project_id}/infra/{infra_id}/gke/destroy`                                                    |             |                 |             |                  |
-| <li>- [ ] `POST /api/projects/{project_id}/infra/{infra_id}/test/destroy`                                                   |             |                 |             |                  |
 | <li>- [x] `POST /api/projects/{project_id}/integrations/aws`                                                                | AS          |                 | yes         | yes              |
 | <li>- [x] `POST /api/projects/{project_id}/integrations/aws/{aws_integration_id}/overwrite`                                 | AS          | yes             |             | yes              |
 | <li>- [x] `POST /api/projects/{project_id}/integrations/basic`                                                              | AS          |                 | yes         |                  |
 | <li>- [x] `POST /api/projects/{project_id}/integrations/gcp`                                                                | AS          |                 | yes         | yes              |
 | <li>- [x] `GET /api/projects/{project_id}/integrations/oauth`                                                               | AS          |                 | yes         | yes              |
+| <li>- [X] `POST /api/projects/{project_id}/infra/{infra_id}/docr/destroy`                                                   | AB          |                 |             |                  |
+| <li>- [X] `POST /api/projects/{project_id}/infra/{infra_id}/doks/destroy`                                                   | AB          |                 |             |                  |
+| <li>- [X] `POST /api/projects/{project_id}/infra/{infra_id}/ecr/destroy`                                                    | AB          |                 |             |                  |
+| <li>- [X] `POST /api/projects/{project_id}/infra/{infra_id}/eks/destroy`                                                    | AB          |                 |             |                  |
+| <li>- [X] `POST /api/projects/{project_id}/infra/{infra_id}/gke/destroy`                                                    | AB          |                 |             |                  |
+| <li>- [X] `POST /api/projects/{project_id}/infra/{infra_id}/test/destroy`                                                   | AB          |                 |             |                  |
 | <li>- [x] `GET /api/projects/{project_id}/invites`                                                                          | AS          |                 |             | yes              |
 | <li>- [x] `POST /api/projects/{project_id}/invites`                                                                         | AS          |                 |             | yes              |
 | <li>- [x] `POST /api/projects/{project_id}/invites/{invite_id}`                                                             | AS          |                 |             | yes              |
@@ -111,7 +111,7 @@
 | <li>- [X] `POST /api/projects/{project_id}/provision/gcr`                                                                   | AB          |                 |             |                  |
 | <li>- [X] `POST /api/projects/{project_id}/provision/gke`                                                                   | AB          |                 |             |                  |
 | <li>- [X] `POST /api/projects/{project_id}/provision/test`                                                                  | N/A         |                 |             |                  |
-| <li>- [ ] `GET /api/projects/{project_id}/provision/{kind}/{infra_id}/logs`                                                 |             |                 |             |                  |
+| <li>- [X] `GET /api/projects/{project_id}/provision/{kind}/{infra_id}/logs`                                                 | AB          |                 |             |                  |
 | <li>- [X] `POST /api/projects/{project_id}/registries`                                                                      | AB          |                 |             |                  |
 | <li>- [X] `GET /api/projects/{project_id}/registries`                                                                       | AB          |                 |             |                  |
 | <li>- [X] `GET /api/projects/{project_id}/registries/dockerhub/token`                                                       | AB          |                 |             |                  |