فهرست منبع

add get current and desired endpoints

Alexander Belanger 4 سال پیش
والد
کامیت
fb745f6147

+ 43 - 0
api/server/handlers/infra/get_current.go

@@ -0,0 +1,43 @@
+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/ee/integrations/httpbackend"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+type InfraGetCurrentHandler struct {
+	handlers.PorterHandlerWriter
+}
+
+func NewInfraGetCurrentHandler(
+	config *config.Config,
+	writer shared.ResultWriter,
+) *InfraGetCurrentHandler {
+	return &InfraGetCurrentHandler{
+		PorterHandlerWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
+	}
+}
+
+func (c *InfraGetCurrentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	infra, _ := r.Context().Value(types.InfraScope).(*models.Infra)
+
+	// TODO: move client out of this call
+	client := httpbackend.NewClient(c.Config().ServerConf.ProvisionerBackendURL)
+
+	// get the unique infra name and query from the TF HTTP backend
+	current, err := client.GetCurrentState(infra.GetUniqueName())
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	c.WriteResult(w, r, current)
+}

+ 43 - 0
api/server/handlers/infra/get_desired.go

@@ -0,0 +1,43 @@
+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/ee/integrations/httpbackend"
+	"github.com/porter-dev/porter/internal/models"
+)
+
+type InfraGetDesiredHandler struct {
+	handlers.PorterHandlerWriter
+}
+
+func NewInfraGetDesiredHandler(
+	config *config.Config,
+	writer shared.ResultWriter,
+) *InfraGetDesiredHandler {
+	return &InfraGetDesiredHandler{
+		PorterHandlerWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
+	}
+}
+
+func (c *InfraGetDesiredHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	infra, _ := r.Context().Value(types.InfraScope).(*models.Infra)
+
+	// TODO: move client out of this call
+	client := httpbackend.NewClient(c.Config().ServerConf.ProvisionerBackendURL)
+
+	// get the unique infra name and query from the TF HTTP backend
+	desired, err := client.GetDesiredState(infra.GetUniqueName())
+
+	if err != nil {
+		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+		return
+	}
+
+	c.WriteResult(w, r, desired)
+}

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

@@ -136,6 +136,62 @@ func getInfraRoutes(
 		Router:   r,
 	})
 
+	// GET /api/projects/{project_id}/infras/{infra_id}/current -> infra.NewInfraGetHandler
+	getCurrentEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/current",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.InfraScope,
+			},
+		},
+	)
+
+	getCurrentHandler := infra.NewInfraGetCurrentHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: getCurrentEndpoint,
+		Handler:  getCurrentHandler,
+		Router:   r,
+	})
+
+	// GET /api/projects/{project_id}/infras/{infra_id}/desired -> infra.NewInfraGetHandler
+	getDesiredEndpoint := factory.NewAPIEndpoint(
+		&types.APIRequestMetadata{
+			Verb:   types.APIVerbGet,
+			Method: types.HTTPVerbGet,
+			Path: &types.Path{
+				Parent:       basePath,
+				RelativePath: relPath + "/desired",
+			},
+			Scopes: []types.PermissionScope{
+				types.UserScope,
+				types.ProjectScope,
+				types.InfraScope,
+			},
+		},
+	)
+
+	getDesiredHandler := infra.NewInfraGetDesiredHandler(
+		config,
+		factory.GetResultWriter(),
+	)
+
+	routes = append(routes, &Route{
+		Endpoint: getDesiredEndpoint,
+		Handler:  getDesiredHandler,
+		Router:   r,
+	})
+
 	// DELETE /api/projects/{project_id}/infras/{infra_id} -> infra.NewInfraDeleteHandler
 	deleteEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

+ 78 - 0
ee/integrations/httpbackend/backend.go

@@ -0,0 +1,78 @@
+package httpbackend
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"time"
+)
+
+type Client struct {
+	backendURL string
+
+	httpClient *http.Client
+}
+
+func NewClient(backendURL string) *Client {
+	httpClient := &http.Client{
+		Timeout: time.Minute,
+	}
+
+	return &Client{backendURL, httpClient}
+}
+
+func (c *Client) GetCurrentState(name string) (*TFState, error) {
+	resp := &TFState{}
+
+	err := c.getRequest(fmt.Sprintf("%s/%s/tfstate", c.backendURL, name), resp)
+
+	return resp, err
+}
+
+func (c *Client) GetDesiredState(name string) (*DesiredTFState, error) {
+	resp := &DesiredTFState{}
+
+	err := c.getRequest(fmt.Sprintf("%s/%s/state", c.backendURL, name), resp)
+
+	return resp, err
+}
+
+func (c *Client) getRequest(path string, dst interface{}) error {
+	req, err := http.NewRequest(
+		"GET",
+		path,
+		nil,
+	)
+
+	if err != nil {
+		return err
+	}
+
+	req.Header.Set("Content-Type", "application/json; charset=utf-8")
+	req.Header.Set("Accept", "application/json; charset=utf-8")
+
+	res, err := c.httpClient.Do(req)
+
+	if err != nil {
+		return err
+	}
+
+	defer res.Body.Close()
+
+	if res.StatusCode < http.StatusOK || res.StatusCode >= http.StatusBadRequest {
+		resBytes, err := ioutil.ReadAll(res.Body)
+
+		if err != nil {
+			return fmt.Errorf("request failed with status code %d, but could not read body (%s)\n", res.StatusCode, err.Error())
+		}
+
+		return fmt.Errorf("request failed with status code %d: %s\n", res.StatusCode, string(resBytes))
+	}
+
+	if dst != nil {
+		return json.NewDecoder(res.Body).Decode(dst)
+	}
+
+	return nil
+}

+ 50 - 0
ee/integrations/httpbackend/types.go

@@ -0,0 +1,50 @@
+package httpbackend
+
+type TerraformEvent string
+
+const (
+	PlannedChange TerraformEvent = "planned_change"
+	ChangeSummary TerraformEvent = "change_summary"
+	ApplyStart    TerraformEvent = "apply_start"
+	ApplyProgress TerraformEvent = "apply_progress"
+	ApplyComplete TerraformEvent = "apply_complete"
+)
+
+type DesiredTFState []Resource
+
+type TFLogLine struct {
+	Level     string         `json:"@level"`
+	Message   string         `json:"@message"`
+	Timestamp string         `json:"@timestamp"`
+	Type      TerraformEvent `json:"type"`
+	Change    Change         `json:"change"`
+	Changes   Changes        `json:"changes"`
+}
+
+type Change struct {
+	Resource Resource `json:"resource"`
+	Action   string   `json:"action"`
+}
+
+type Resource struct {
+	Addr         string `json:"addr"`
+	ResourceType string `json:"resource_type"`
+	ResourceName string `json:"resource_name"`
+	Provider     string `json:"implied_provider"`
+}
+
+type Changes struct {
+	Add       int    `json:"add"`
+	Change    int    `json:"change"`
+	Remove    int    `json:"remove"`
+	Operation string `json:"operation"`
+}
+
+type TFState struct {
+	Version          int         `json:"version"`
+	TerraformVersion string      `json:"terraform_version"`
+	Serial           int         `json:"serial"`
+	Lineage          string      `json:"lineage"`
+	Outputs          interface{} `json:"outputs"`
+	Resources        interface{} `json:"resources"`
+}