cmd/hub/api/environment.go (458 lines of code) (raw):

// Copyright (c) 2022 EPAM Systems, Inc. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build api package api import ( "encoding/json" "errors" "fmt" "io" "log" "net/url" "os" "strings" "github.com/epam/hubctl/cmd/hub/config" "github.com/epam/hubctl/cmd/hub/util" ) const environmentsResource = "hub/api/v1/environments" var environmentsCache = make(map[string]*Environment) func Environments(selector string, showSecrets, showMyTeams, showServiceAccount, showServiceAccountLoginToken, getCloudCredentials, showBackups, jsonFormat bool) { envs, err := environmentsBy(selector) if err != nil { log.Fatalf("Unable to query for Environment(s): %v", err) } if len(envs) == 0 { if jsonFormat { log.Print("No Environments") } else { fmt.Print("No Environments\n") } } else { if jsonFormat { var toMarshal interface{} if len(envs) == 1 { toMarshal = &envs[0] } else { toMarshal = envs } out, err := json.MarshalIndent(toMarshal, "", " ") if err != nil { log.Fatalf("Error marshalling JSON response for output: %v", err) } os.Stdout.Write(out) os.Stdout.Write([]byte("\n")) } else { fmt.Print("Environments:\n") errors := make([]error, 0) for _, env := range envs { errors = formatEnvironmentEntity(&env, showSecrets, showMyTeams, showServiceAccount, showServiceAccountLoginToken, getCloudCredentials, showBackups, errors) } if len(errors) > 0 { fmt.Print("Errors encountered:\n") for _, err := range errors { fmt.Printf("\t%v\n", err) } } } } } func formatEnvironmentEntity(env *Environment, showSecrets, showMyTeams, showServiceAccount, showServiceAccountLoginToken, getCloudCredentials, showBackups bool, errors []error) []error { title := fmt.Sprintf("%s [%s]", env.Name, env.Id) if env.Description != "" { title = fmt.Sprintf("%s - %s", title, env.Description) } fmt.Printf("\n\t%s\n", title) if len(env.Tags) > 0 { fmt.Printf("\t\tTags: %s\n", strings.Join(env.Tags, ", ")) } var dnsDomains []string if env.CloudAccount != "" { account, err := cloudAccountById(env.CloudAccount, false) if err != nil { errors = append(errors, err) } else { fmt.Printf("\t\tCloud Account: %s\n", formatCloudAccountTitle(account)) dnsDomains = append(dnsDomains, account.BaseDomain) } if getCloudCredentials && account != nil { keys, err := cloudAccountCredentials(account.Id, account.Kind) if err != nil { errors = append(errors, err) } else { formatted, err := formatCloudAccountCredentials(keys) if err != nil { errors = append(errors, err) } else { fmt.Printf("\t\tSecurity Credentials: %s\n", formatted) } } } } if len(env.Providers) > 0 { var kubes []string for _, provider := range env.Providers { if provider.Kind == "kubernetes" && provider.Name != "" { kubes = append(kubes, provider.Name) } } for _, provider := range env.Providers { if provider.Kind == "dns-domain" { for _, p := range provider.Parameters { if p.Name == "dns.baseDomain" { if domain, ok := p.Value.(string); ok { faulty := "" if !util.Contains(kubes, domain) { faulty = " (no cluster)" } dnsDomains = append(dnsDomains, domain+faulty) } } } } } } if len(dnsDomains) > 0 { fmt.Printf("\t\tDomains: %s\n", strings.Join(dnsDomains, ", ")) } resource := fmt.Sprintf("%s/%s", environmentsResource, env.Id) if len(env.Parameters) > 0 { fmt.Print("\t\tParameters:\n") for _, param := range sortParameters(env.Parameters) { formatted, err := formatParameter(resource, param, showSecrets) fmt.Printf("\t\t%s\n", formatted) if err != nil { errors = append(errors, err) } } } if len(env.Providers) > 0 { fmt.Print("\t\tProviders:\n") for i, provider := range env.Providers { fmt.Printf("\t\t %02d %s [%s]\n", i, provider.Name, provider.Kind) provides := "(none)" if len(provider.Provides) > 0 { provides = strings.Join(provider.Provides, ", ") } fmt.Printf("\t\t\tProvides: %s\n", provides) if len(provider.Parameters) > 0 { fmt.Print("\t\t\tParameters:\n") for _, param := range sortParameters(provider.Parameters) { formatted, err := formatParameter(resource, param, showSecrets) fmt.Printf("\t\t\t%s\n", formatted) if err != nil { errors = append(errors, err) } } } } } if len(env.TeamsPermissions) > 0 { formatted := formatTeams(env.TeamsPermissions) fmt.Printf("\t\tTeams: %s\n", formatted) if showMyTeams { teams, err := myTeams(env.Id) formatted := formatTeams(teams) fmt.Printf("\t\tMy Teams: %s\n", formatted) if err != nil { errors = append(errors, err) } } } if showServiceAccount { teams, err := myTeams(env.Id) if err != nil { errors = append(errors, err) } else { if len(teams) > 0 { for _, team := range teams { account, err := serviceAccount(env.Id, team.Id) if err != nil { errors = append(errors, err) } else { formatted := formatServiceAccount(team, account, showServiceAccountLoginToken) fmt.Printf("\t\tService Account: %s\n", formatted) } } } } } if showBackups { backups, err := backupsByEnvironmentId(env.Id) if err != nil { errors = append(errors, err) } if len(backups) > 0 { fmt.Print("\tBackups:\n") errs := make([]error, 0) for _, backup := range backups { errs = formatBackupEntity(&backup, false, errors) } if len(errs) > 0 { errors = append(errors, errs...) } } } return errors } func formatEnvironment(environment *Environment) { errors := formatEnvironmentEntity(environment, false, false, false, false, false, false, make([]error, 0)) if len(errors) > 0 { fmt.Print("Errors encountered formatting response:\n") for _, err := range errors { fmt.Printf("\t%v\n", err) } } } func cachedEnvironmentBy(selector string) (*Environment, error) { env, cached := environmentsCache[selector] if !cached { var err error env, err = environmentBy(selector) if err != nil { return nil, err } environmentsCache[selector] = env } return env, nil } func environmentBy(selector string) (*Environment, error) { if !util.IsUint(selector) { return environmentByName(selector) } return environmentById(selector) } func environmentsBy(selector string) ([]Environment, error) { if !util.IsUint(selector) { return environmentsByName(selector) } environment, err := environmentById(selector) if err != nil { return nil, err } if environment != nil { return []Environment{*environment}, nil } return nil, nil } func environmentById(id string) (*Environment, error) { path := fmt.Sprintf("%s/%s", environmentsResource, url.PathEscape(id)) var jsResp Environment code, err := get(hubApi(), path, &jsResp) if code == 404 { return nil, nil } if err != nil { return nil, fmt.Errorf("Error querying HubCTL Environments: %v", err) } if code != 200 { return nil, fmt.Errorf("Got %d HTTP querying HubCTL Environments, expected 200 HTTP", code) } return &jsResp, nil } func environmentByName(name string) (*Environment, error) { environments, err := environmentsByName(name) if err != nil { return nil, fmt.Errorf("Unable to query for Environment `%s`: %v", name, err) } if len(environments) == 0 { return nil, fmt.Errorf("No Environment `%s` found", name) } if len(environments) > 1 { return nil, fmt.Errorf("More than one Environment returned by name `%s`", name) } environment := environments[0] return &environment, nil } func environmentsByName(name string) ([]Environment, error) { path := environmentsResource if name != "" { path += "?name=" + url.QueryEscape(name) } var jsResp []Environment code, err := get(hubApi(), path, &jsResp) if code == 404 { return nil, nil } if err != nil { return nil, fmt.Errorf("Error querying HubCTL Environments: %v", err) } if code != 200 { return nil, fmt.Errorf("Got %d HTTP querying HubCTL Environments, expected 200 HTTP", code) } return jsResp, nil } func formatEnvironmentRef(ref *EnvironmentRef) string { return fmt.Sprintf("%s [%s]", ref.Name, ref.Id) } func myTeams(environmentId string) ([]Team, error) { path := fmt.Sprintf("%s/%s/my-teams", environmentsResource, url.PathEscape(environmentId)) var jsResp []Team code, err := get(hubApi(), path, &jsResp) if err != nil { return nil, fmt.Errorf("Error querying HubCTL Environment `%s` My Teams: %v", environmentId, err) } if code != 200 { return nil, fmt.Errorf("Got %d HTTP querying HubCTL Environment `%s` My Teams, expected 200 HTTP", code, environmentId) } return jsResp, nil } func formatTeams(teams []Team) string { formatted := make([]string, 0, len(teams)) for _, team := range teams { formatted = append(formatted, fmt.Sprintf("%s (%s)", team.Name, team.Role)) } return strings.Join(formatted, ", ") } func serviceAccount(environmentId, teamId string) (*ServiceAccount, error) { path := fmt.Sprintf("%s/%s/service-account/%s", environmentsResource, url.PathEscape(environmentId), url.PathEscape(teamId)) var jsResp ServiceAccount code, err := get(hubApi(), path, &jsResp) if err != nil { return nil, fmt.Errorf("Error querying HubCTL Team `%s` Service Account: %v", teamId, err) } if code != 200 { return nil, fmt.Errorf("Got %d HTTP querying HubCTL Team `%s` Service Account, expected 200 HTTP", code, teamId) } return &jsResp, nil } func formatServiceAccount(team Team, account *ServiceAccount, showLoginToken bool) string { formatted := fmt.Sprintf("%s (%s) %s", team.Name, team.Role, account.Name) if showLoginToken { formatted = fmt.Sprintf("%s\n\t\t\tLogin Token: %s", formatted, account.LoginToken) } return formatted } func CreateEnvironment(name, cloudAccountSelector string) { cloudAccount, err := cloudAccountBy(cloudAccountSelector) if err != nil { log.Fatalf("Unable to create HubCTL Environment: %v", err) } if cloudAccount == nil { log.Fatal("Unable to create HubCTL Environment: Cloud Account not found") } req := &EnvironmentRequest{ Name: name, CloudAccount: cloudAccount.Id, Parameters: []Parameter{}, Providers: []Provider{}, } environment, err := createEnvironment(req) if err != nil { log.Fatalf("Unable to create HubCTL Environment: %v", err) } formatEnvironment(environment) } func createEnvironment(environment *EnvironmentRequest) (*Environment, error) { var jsResp Environment code, err := post(hubApi(), environmentsResource, environment, &jsResp) if err != nil { return nil, err } if code != 200 && code != 201 { return nil, fmt.Errorf("Got %d HTTP creating HubCTL Environment, expected [200, 201] HTTP", code) } return &jsResp, nil } func DeleteEnvironment(selector string) { err := deleteEnvironment(selector) if err != nil { log.Fatalf("Unable to delete HubCTL Environment: %v", err) } } func deleteEnvironment(selector string) error { environment, err := environmentBy(selector) id := "" if err != nil { str := err.Error() if util.IsUint(selector) && (strings.Contains(str, "json: cannot unmarshal") || strings.Contains(str, "cannot parse") || config.Force) { util.Warn("%v", err) id = selector } else { return err } } else if environment == nil { return error404 } else { id = environment.Id } force := "" if config.Force { force = "?force=true" } path := fmt.Sprintf("%s/%s%s", environmentsResource, url.PathEscape(id), force) code, err := delete(hubApi(), path) if err != nil { return err } if code != 202 && code != 204 { return fmt.Errorf("Got %d HTTP deleting HubCTL Environments, expected [202, 204] HTTP", code) } return nil } func PatchEnvironment(selector string, change EnvironmentPatch) { environment, err := patchEnvironment(selector, change) if err != nil { log.Fatalf("Unable to patch HubCTL Environment: %v", err) } formatEnvironment(environment) } func patchEnvironment(selector string, change EnvironmentPatch) (*Environment, error) { environment, err := environmentBy(selector) if err != nil { return nil, err } if environment == nil { return nil, error404 } path := fmt.Sprintf("%s/%s", environmentsResource, url.PathEscape(environment.Id)) var jsResp Environment code, err := patch(hubApi(), path, &change, &jsResp) if err != nil { return nil, err } if code != 200 { return nil, fmt.Errorf("Got %d HTTP patching HubCTL Environment, expected 200 HTTP", code) } return &jsResp, nil } func RawPatchEnvironment(selector string, body io.Reader) { environment, err := rawPatchEnvironment(selector, body) if err != nil { log.Fatalf("Unable to patch HubCTL Environment: %v", err) } formatEnvironment(environment) } func rawPatchEnvironment(selector string, body io.Reader) (*Environment, error) { environment, err := environmentBy(selector) if err != nil && !config.Force { return nil, err } if environment == nil && !config.Force { return nil, error404 } environmentId := "" if environment == nil { if util.IsUint(selector) { environmentId = selector } else { return nil, errors.New("Specify environment by Id") } } else { environmentId = environment.Id } path := fmt.Sprintf("%s/%s", environmentsResource, url.PathEscape(environmentId)) var jsResp Environment code, err := patch2(hubApi(), path, body, &jsResp) if err != nil { return nil, err } if code != 200 { return nil, fmt.Errorf("Got %d HTTP patching HubCTL Environment, expected 200 HTTP", code) } return &jsResp, nil }