diff --git a/pkg/cmd/environments.go b/pkg/cmd/environments.go index fe3166ac..fc25fcd8 100644 --- a/pkg/cmd/environments.go +++ b/pkg/cmd/environments.go @@ -16,6 +16,9 @@ limitations under the License. package cmd import ( + "errors" + "fmt" + "github.com/DopplerHQ/cli/pkg/configuration" "github.com/DopplerHQ/cli/pkg/controllers" "github.com/DopplerHQ/cli/pkg/http" @@ -39,6 +42,29 @@ var environmentsGetCmd = &cobra.Command{ Run: getEnvironments, } +var environmentsCreateCmd = &cobra.Command{ + Use: "create [name] [slug]", + Short: "Create an environment", + Args: cobra.ExactArgs(2), + Run: createEnvironment, +} + +var environmentsDeleteCmd = &cobra.Command{ + Use: "delete [slug]", + Short: "Delete an environment", + Args: cobra.ExactArgs(1), + ValidArgsFunction: configEnvironmentIDsValidArgs, + Run: deleteEnvironment, +} + +var environmentsRenameCmd = &cobra.Command{ + Use: "rename [slug]", + Short: "Rename an environment", + Args: cobra.ExactArgs(1), + ValidArgsFunction: configEnvironmentIDsValidArgs, + Run: renameEnvironment, +} + func environments(cmd *cobra.Command, args []string) { jsonFlag := utils.OutputJSON localConfig := configuration.LocalConfig(cmd) @@ -80,10 +106,112 @@ func configEnvironmentIDsValidArgs(cmd *cobra.Command, args []string, toComplete return nil, cobra.ShellCompDirectiveNoFileComp } +func createEnvironment(cmd *cobra.Command, args []string) { + jsonFlag := utils.OutputJSON + localConfig := configuration.LocalConfig(cmd) + + utils.RequireValue("token", localConfig.Token.Value) + + name := args[0] + slug := args[1] + + info, err := http.CreateEnvironment(localConfig.APIHost.Value, utils.GetBool(localConfig.VerifyTLS.Value, true), localConfig.Token.Value, localConfig.EnclaveProject.Value, name, slug) + if !err.IsNil() { + utils.HandleError(err.Unwrap(), err.Message) + } + + if !utils.Silent { + printer.EnvironmentInfo(info, jsonFlag) + } +} + +func deleteEnvironment(cmd *cobra.Command, args []string) { + jsonFlag := utils.OutputJSON + yes := utils.GetBoolFlag(cmd, "yes") + localConfig := configuration.LocalConfig(cmd) + + utils.RequireValue("token", localConfig.Token.Value) + + slug := args[0] + + prompt := "Delete environment" + if slug != "" { + prompt = fmt.Sprintf("%s %s", prompt, slug) + } + + if yes || utils.ConfirmationPrompt(prompt, false) { + err := http.DeleteEnvironment(localConfig.APIHost.Value, utils.GetBool(localConfig.VerifyTLS.Value, true), localConfig.Token.Value, localConfig.EnclaveProject.Value, slug) + if !err.IsNil() { + utils.HandleError(err.Unwrap(), err.Message) + } + + if !utils.Silent { + info, err := http.GetEnvironments(localConfig.APIHost.Value, utils.GetBool(localConfig.VerifyTLS.Value, true), localConfig.Token.Value, localConfig.EnclaveProject.Value) + if !err.IsNil() { + utils.HandleError(err.Unwrap(), err.Message) + } + + printer.EnvironmentsInfo(info, jsonFlag) + } + } +} + +func renameEnvironment(cmd *cobra.Command, args []string) { + jsonFlag := utils.OutputJSON + yes := utils.GetBoolFlag(cmd, "yes") + localConfig := configuration.LocalConfig(cmd) + newName := cmd.Flag("name").Value.String() + newSlug := cmd.Flag("slug").Value.String() + + utils.RequireValue("token", localConfig.Token.Value) + + if newName == "" && newSlug == "" { + utils.HandleError(errors.New("command requires --name or --slug")) + } + + slug := args[0] + + prompt := "Rename environment" + if slug != "" { + prompt = fmt.Sprintf("%s %s", prompt, slug) + } + + if !yes { + if newSlug != "" { + utils.LogWarning("Modifying your environment's slug may break your current deploys. All configs within this environment will also be renamed.") + } + yes = utils.ConfirmationPrompt(prompt, false) + } + + if yes { + info, err := http.RenameEnvironment(localConfig.APIHost.Value, utils.GetBool(localConfig.VerifyTLS.Value, true), localConfig.Token.Value, localConfig.EnclaveProject.Value, slug, newName, newSlug) + if !err.IsNil() { + utils.HandleError(err.Unwrap(), err.Message) + } + + if !utils.Silent { + printer.EnvironmentInfo(info, jsonFlag) + } + } +} + func init() { environmentsGetCmd.Flags().StringP("project", "p", "", "project (e.g. backend)") environmentsCmd.AddCommand(environmentsGetCmd) + environmentsCreateCmd.Flags().StringP("project", "p", "", "project (e.g. backend)") + environmentsCmd.AddCommand(environmentsCreateCmd) + + environmentsDeleteCmd.Flags().StringP("project", "p", "", "project (e.g. backend)") + environmentsDeleteCmd.Flags().BoolP("yes", "y", false, "proceed without confirmation") + environmentsCmd.AddCommand(environmentsDeleteCmd) + + environmentsRenameCmd.Flags().StringP("project", "p", "", "project (e.g. backend)") + environmentsRenameCmd.Flags().BoolP("yes", "y", false, "proceed without confirmation") + environmentsRenameCmd.Flags().String("name", "", "new name") + environmentsRenameCmd.Flags().String("slug", "", "new slug") + environmentsCmd.AddCommand(environmentsRenameCmd) + environmentsCmd.Flags().StringP("project", "p", "", "project (e.g. backend)") rootCmd.AddCommand(environmentsCmd) } diff --git a/pkg/http/api.go b/pkg/http/api.go index 79f206df..4c856147 100644 --- a/pkg/http/api.go +++ b/pkg/http/api.go @@ -404,7 +404,7 @@ func UpdateProject(host string, verifyTLS bool, apiKey string, project string, n return projectInfo, Error{} } -// DeleteProject create a project +// DeleteProject delete a project func DeleteProject(host string, verifyTLS bool, apiKey string, project string) Error { var params []queryParam params = append(params, queryParam{Key: "project", Value: project}) @@ -476,6 +476,78 @@ func GetEnvironment(host string, verifyTLS bool, apiKey string, project string, return info, Error{} } +// CreateEnvironment create an environment +func CreateEnvironment(host string, verifyTLS bool, apiKey string, project string, name string, slug string) (models.EnvironmentInfo, Error) { + postBody := map[string]string{"project": project, "name": name, "slug": slug} + body, err := json.Marshal(postBody) + if err != nil { + return models.EnvironmentInfo{}, Error{Err: err, Message: "Invalid environment info"} + } + + statusCode, _, response, err := PostRequest(host, verifyTLS, apiKeyHeader(apiKey), "/v3/environments", []queryParam{}, body) + if err != nil { + return models.EnvironmentInfo{}, Error{Err: err, Message: "Unable to create environment", Code: statusCode} + } + + var result map[string]interface{} + err = json.Unmarshal(response, &result) + if err != nil { + return models.EnvironmentInfo{}, Error{Err: err, Message: "Unable to parse API response", Code: statusCode} + } + + environmentInfo := models.ParseEnvironmentInfo(result["environment"].(map[string]interface{})) + return environmentInfo, Error{} +} + +// DeleteEnvironment delete an environment +func DeleteEnvironment(host string, verifyTLS bool, apiKey string, project string, environment string) Error { + var params []queryParam + params = append(params, queryParam{Key: "project", Value: project}) + params = append(params, queryParam{Key: "environment", Value: environment}) + + statusCode, _, response, err := DeleteRequest(host, verifyTLS, apiKeyHeader(apiKey), "/v3/environments/environment", params) + if err != nil { + return Error{Err: err, Message: "Unable to delete environment", Code: statusCode} + } + + var result map[string]interface{} + err = json.Unmarshal(response, &result) + if err != nil { + return Error{Err: err, Message: "Unable to parse API response", Code: statusCode} + } + + return Error{} +} + +// RenameEnvironment rename an environment +func RenameEnvironment(host string, verifyTLS bool, apiKey string, project string, environment string, name string, slug string) (models.EnvironmentInfo, Error) { + postBody := map[string]string{"project": project, "environment": environment} + if name != "" { + postBody["name"] = name + } + if slug != "" { + postBody["slug"] = slug + } + body, err := json.Marshal(postBody) + if err != nil { + return models.EnvironmentInfo{}, Error{Err: err, Message: "Invalid environment info"} + } + + statusCode, _, response, err := PutRequest(host, verifyTLS, apiKeyHeader(apiKey), "/v3/environments/environment", []queryParam{}, body) + if err != nil { + return models.EnvironmentInfo{}, Error{Err: err, Message: "Unable to rename environment", Code: statusCode} + } + + var result map[string]interface{} + err = json.Unmarshal(response, &result) + if err != nil { + return models.EnvironmentInfo{}, Error{Err: err, Message: "Unable to parse API response", Code: statusCode} + } + + environmentInfo := models.ParseEnvironmentInfo(result["environment"].(map[string]interface{})) + return environmentInfo, Error{} +} + // GetConfigs get configs func GetConfigs(host string, verifyTLS bool, apiKey string, project string) ([]models.ConfigInfo, Error) { var params []queryParam diff --git a/pkg/http/http.go b/pkg/http/http.go index 6dea7207..4e661fb6 100644 --- a/pkg/http/http.go +++ b/pkg/http/http.go @@ -89,6 +89,26 @@ func PostRequest(host string, verifyTLS bool, headers map[string]string, uri str return statusCode, respHeaders, body, nil } +// PutRequest perform HTTP PUT +func PutRequest(host string, verifyTLS bool, headers map[string]string, uri string, params []queryParam, body []byte) (int, http.Header, []byte, error) { + url := fmt.Sprintf("%s%s", host, uri) + req, err := http.NewRequest("PUT", url, bytes.NewReader(body)) + if err != nil { + return 0, nil, nil, err + } + + for key, value := range headers { + req.Header.Set(key, value) + } + + statusCode, respHeaders, body, err := performRequest(req, verifyTLS, params) + if err != nil { + return statusCode, respHeaders, body, err + } + + return statusCode, respHeaders, body, nil +} + // DeleteRequest perform HTTP DELETE func DeleteRequest(host string, verifyTLS bool, headers map[string]string, uri string, params []queryParam) (int, http.Header, []byte, error) { url := fmt.Sprintf("%s%s", host, uri)