// Copyright 2016-2017, Pulumi Corporation. All rights reserved. package cmd import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" "os" "github.com/pulumi/pulumi/pkg/apitype" "github.com/pulumi/pulumi/pkg/pack" "github.com/pulumi/pulumi/pkg/tokens" "github.com/pulumi/pulumi/pkg/util/contract" "github.com/pulumi/pulumi/pkg/workspace" ) // pulumiCloudEndpoint returns the endpoint for the Pulumi Cloud Management Console API. // e.g. "http://localhost:8080" or "https://api.moolumi.io". func pulumiConsoleAPI() (string, error) { envVar := os.Getenv("PULUMI_API") if envVar == "" { return "", fmt.Errorf("PULUMI_API env var not set") } return envVar, nil } // usePulumiCloudCommands returns whether or not to use the "Pulumi Cloud" version of CLI commands. func usePulumiCloudCommands() bool { _, err := pulumiConsoleAPI() return err == nil } // cloudProjectIdentifier is the set of data needed to identify a Pulumi Cloud project. This the // logical "home" of a stack on the Pulumi Cloud. type cloudProjectIdentifier struct { Owner string Repository string Project tokens.PackageName } // getCloudProjectIdentifiers returns the owner, repo name, and project name of the current package and workspace. func getCloudProjectIdentifier(w workspace.W) (*cloudProjectIdentifier, error) { cwd, err := os.Getwd() if err != nil { return nil, err } path, err := workspace.DetectPackage(cwd) if err != nil { return nil, err } pkg, err := pack.Load(path) if err != nil { return nil, err } repo := w.Repository() return &cloudProjectIdentifier{ Owner: repo.Owner, Repository: repo.Name, Project: pkg.Name, }, nil } // pulumiAPICall makes an HTTP request to the Pulumi API. func pulumiAPICall(method string, path string, body []byte, accessToken string) (*http.Response, error) { apiEndpoint, err := pulumiConsoleAPI() if err != nil { return nil, fmt.Errorf("getting Pulumi API endpoint: %v", err) } url := fmt.Sprintf("%s/api%s", apiEndpoint, path) req, err := http.NewRequest(method, url, bytes.NewBuffer(body)) if err != nil { return nil, fmt.Errorf("creating new HTTP request: %v", err) } req.Header.Set("Content-Type", "application/json") // Apply credentials if provided. if accessToken != "" { req.Header.Set("Authorization", fmt.Sprintf("token %s", accessToken)) } client := &http.Client{} resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("performing HTTP request: %v", err) } return resp, nil } // pulumiRESTCall calls the pulumi REST API marshalling reqObj to JSON and using that as // the request body (use nil for GETs), and if successful, marshalling the responseObj // as JSON and storing it in respObj (use nil for NoContent). The error return type might // be an instance of apitype.ErrorResponse, in which case will have the response code. func pulumiRESTCall(method, path string, reqObj interface{}, respObj interface{}) error { creds, err := getStoredCredentials() if err != nil { return fmt.Errorf("getting stored credentials: %v", err) } return pulumiRESTCallWithAccessToken(method, path, reqObj, respObj, creds.AccessToken) } // pulumiRESTCallWithAccessToken requires you pass in the auth token rather than reading it from the machine's config. func pulumiRESTCallWithAccessToken(method, path string, reqObj interface{}, respObj interface{}, token string) error { var reqBody []byte var err error if reqObj != nil { reqBody, err = json.Marshal(reqObj) if err != nil { return fmt.Errorf("marshalling request object as JSON: %v", err) } } resp, err := pulumiAPICall(method, path, reqBody, token) if err != nil { return fmt.Errorf("calling API: %v", err) } defer contract.IgnoreClose(resp.Body) respBody, err := ioutil.ReadAll(resp.Body) if err != nil { return fmt.Errorf("reading response from API: %v", err) } // 4xx and 5xx responses should be of type ErrorResponse. See if we can unmarshal as that // type, and if not just return the raw response text. if resp.StatusCode >= 400 && resp.StatusCode <= 599 { var errResp apitype.ErrorResponse if err = json.Unmarshal(respBody, &errResp); err != nil { errResp.Code = resp.StatusCode errResp.Message = string(respBody) } return &errResp } if respObj != nil { if err = json.Unmarshal(respBody, respObj); err != nil { return fmt.Errorf("unmarshalling response object: %v", err) } } return nil }