func getEnvNamesByOperatorEndpoint(operatorEndpoint string) ([]string, bool, error) {
	cliConfig, err := readCLIConfig()
	if err != nil {
		return nil, false, err
	}
	var envNames []string
	isDefaultEnv := false
	for _, env := range cliConfig.Environments {
		if env.OperatorEndpoint != "" && s.LastSplit(env.OperatorEndpoint, "//") == s.LastSplit(operatorEndpoint, "//") {
			envNames = append(envNames, env.Name)
			if cliConfig.DefaultEnvironment != nil && env.Name == *cliConfig.DefaultEnvironment {
				isDefaultEnv = true
			}
		}
	}
	return envNames, isDefaultEnv, nil
}
returns the list of environment names, and whether any of them were the default
package cmd
import (
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"github.com/cortexlabs/cortex/cli/cluster"
	"github.com/cortexlabs/cortex/cli/types/cliconfig"
	cr "github.com/cortexlabs/cortex/pkg/lib/configreader"
	"github.com/cortexlabs/cortex/pkg/lib/errors"
	"github.com/cortexlabs/cortex/pkg/lib/exit"
	"github.com/cortexlabs/cortex/pkg/lib/files"
	"github.com/cortexlabs/cortex/pkg/lib/json"
	"github.com/cortexlabs/cortex/pkg/lib/print"
	"github.com/cortexlabs/cortex/pkg/lib/prompt"
	s "github.com/cortexlabs/cortex/pkg/lib/strings"
	"github.com/cortexlabs/cortex/pkg/lib/urls"
	"github.com/cortexlabs/cortex/pkg/operator/schema"
	"github.com/cortexlabs/yaml"
)
var _cliConfigValidation = &cr.StructValidation{
	TreatNullAsEmpty: true,
	StructFieldValidations: []*cr.StructFieldValidation{
		{
			StructField: "Telemetry",
			BoolPtrValidation: &cr.BoolPtrValidation{
				Required: false,
			},
		},
		{
			StructField: "DefaultEnvironment",
			StringPtrValidation: &cr.StringPtrValidation{
				Required:          false,
				AllowExplicitNull: true,
			},
		},
		{
			StructField: "Environments",
			StructListValidation: &cr.StructListValidation{
				AllowExplicitNull: true,
				StructValidation: &cr.StructValidation{
					AllowExtraFields: true,  
					StructFieldValidations: []*cr.StructFieldValidation{
						{
							StructField: "Name",
							StringValidation: &cr.StringValidation{
								Required:  true,
								MaxLength: 63,
							},
						},
						{
							Key: "provider",
							StringValidation: &cr.StringValidation{
								AllowEmpty: true,
								Validator: func(provider string) (string, error) {
									if provider == "" || provider == "aws" {
										return "", nil
									}
									if provider == "gcp" || provider == "local" {
										return "", ErrorInvalidLegacyProvider(provider, _cliConfigPath)
									}
									return "", ErrorInvalidProvider(provider, _cliConfigPath)
								},
							},
						},
						{
							StructField: "OperatorEndpoint",
							StringValidation: &cr.StringValidation{
								Required:  true,
								Validator: cliconfig.CortexEndpointValidator,
							},
						},
					},
				},
			},
		},
	},
}
func getEnvFromFlag(envFlag string) (string, error) {
	if envFlag != "" {
		return envFlag, nil
	}
	defaultEnv, err := getDefaultEnv()
	if err != nil {
		return "", err
	}
	if defaultEnv != nil {
		return *defaultEnv, nil
	}
	envs, err := listConfiguredEnvs()
	if err != nil {
		return "", err
	}
	if len(envs) == 0 {
		return "", ErrorNoAvailableEnvironment()
	}
	return "", ErrorEnvironmentNotSet()
}
func promptForExistingEnvName(promptMsg string) string {
	configuredEnvNames, err := listConfiguredEnvNames()
	if err != nil {
		exit.Error(err)
	}
	fmt.Printf("your currently configured environments are: %s\n\n", strings.Join(configuredEnvNames, ", "))
	envNameContainer := &struct {
		EnvironmentName string
	}{}
	err = cr.ReadPrompt(envNameContainer, &cr.PromptValidation{
		PromptItemValidations: []*cr.PromptItemValidation{
			{
				StructField: "EnvironmentName",
				PromptOpts: &prompt.Options{
					Prompt: promptMsg,
				},
				StringValidation: &cr.StringValidation{
					Required:      true,
					AllowedValues: configuredEnvNames,
				},
			},
		},
	})
	if err != nil {
		exit.Error(err)
	}
	return envNameContainer.EnvironmentName
}
func promptEnv(env *cliconfig.Environment, defaults cliconfig.Environment) error {
	if env.OperatorEndpoint == "" {
		fmt.Print("you can get your cortex operator endpoint using `cortex cluster info` if you already have a cortex cluster running, otherwise run `cortex cluster up` to create a cortex cluster\n\n")
	}
	validator := func(endpoint string) (string, error) {
		operatorURL, err := validateOperatorEndpoint(endpoint)
		if err != nil {
			return "", err
		}
		return operatorURL, nil
	}
	for true {
		err := cr.ReadPrompt(env, &cr.PromptValidation{
			SkipNonEmptyFields: true,
			PromptItemValidations: []*cr.PromptItemValidation{
				{
					StructField: "Name",
					PromptOpts: &prompt.Options{
						Prompt: "name of environment to create or update",
					},
					StringValidation: &cr.StringValidation{
						Required: true,
					},
				},
				{
					StructField: "OperatorEndpoint",
					PromptOpts: &prompt.Options{
						Prompt: "cortex operator endpoint",
					},
					StringValidation: &cr.StringValidation{
						Required:  true,
						Default:   defaults.OperatorEndpoint,
						Validator: validator,
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	}
	return nil
}
func validateOperatorEndpoint(endpoint string) (string, error) {
	url, err := cliconfig.CortexEndpointValidator(endpoint)
	if err != nil {
		return "", err
	}
	parsedURL, err := urls.Parse(url)
	if err != nil {
		return "", err
	}
	url = parsedURL.String()
	req, err := http.NewRequest("GET", urls.Join(url, "/verifycortex"), nil)
	if err != nil {
		return "", errors.Wrap(err, "verifying operator endpoint", url)
	}
	client := http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		},
	}
	response, err := client.Do(req)
	if err != nil {
		return "", ErrorInvalidOperatorEndpoint(url)
	}
	defer response.Body.Close()
	if response.StatusCode != 200 {
		return "", ErrorInvalidOperatorEndpoint(url)
	}
	bodyBytes, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return "", errors.Wrap(err, _errStrRead)
	}
	var verifyCortex schema.VerifyCortexResponse
	if err = json.Unmarshal(bodyBytes, &verifyCortex); err != nil {
		return "", errors.Wrap(err, endpoint, string(bodyBytes))
	}
	return url, nil
}
func getDefaultEnv() (*string, error) {
	cliConfig, err := readCLIConfig()
	if err != nil {
		return nil, err
	}
	if cliConfig.DefaultEnvironment != nil {
		return cliConfig.DefaultEnvironment, nil
	}
	if len(cliConfig.Environments) == 1 {
		defaultEnv := cliConfig.Environments[0].Name
		err := setDefaultEnv(defaultEnv)
		if err != nil {
			return nil, err
		}
		return &defaultEnv, nil
	}
	return nil, nil
}
func setDefaultEnv(envName string) error {
	cliConfig, err := readCLIConfig()
	if err != nil {
		return err
	}
	envExists, err := isEnvConfigured(envName)
	if err != nil {
		return err
	}
	if !envExists {
		return cliconfig.ErrorEnvironmentNotConfigured(envName)
	}
	cliConfig.DefaultEnvironment = &envName
	if err := writeCLIConfig(cliConfig); err != nil {
		return err
	}
	return nil
}
func renameEnv(oldEnvName string, newEnvName string) error {
	cliConfig, err := readCLIConfig()
	if err != nil {
		return err
	}
	renamedEnv := false
	for _, env := range cliConfig.Environments {
		if env.Name == newEnvName {
			return cliconfig.ErrorEnvironmentAlreadyConfigured(newEnvName)
		}
		if env.Name == oldEnvName {
			env.Name = newEnvName
			renamedEnv = true
		}
	}
	if !renamedEnv {
		return cliconfig.ErrorEnvironmentNotConfigured(oldEnvName)
	}
	if cliConfig.DefaultEnvironment != nil && *cliConfig.DefaultEnvironment == oldEnvName {
		cliConfig.DefaultEnvironment = &newEnvName
	}
	if err := writeCLIConfig(cliConfig); err != nil {
		return err
	}
	return nil
}
func readTelemetryConfig() (bool, error) {
	cliConfig, err := readCLIConfig()
	if err != nil {
		return false, err
	}
	if cliConfig.Telemetry != nil && *cliConfig.Telemetry == false {
		return false, nil
	}
	return true, nil
}
func isTelemetryEnabled() bool {
	enabled, err := readTelemetryConfig()
	if err != nil {
		return false
	}
	return enabled
}
func readEnv(envName string) (*cliconfig.Environment, error) {
	cliConfig, err := readCLIConfig()
	if err != nil {
		return nil, err
	}
	for _, env := range cliConfig.Environments {
		if env.Name == envName {
			return env, nil
		}
	}
	return nil, nil
}
func ReadOrConfigureEnv(envName string) (cliconfig.Environment, error) {
	existingEnv, err := readEnv(envName)
	if err != nil {
		return cliconfig.Environment{}, err
	}
	if existingEnv != nil {
		return *existingEnv, nil
	}
	promptStr := fmt.Sprintf("the %s environment is not configured; do you already have a Cortex cluster running?", envName)
	yesMsg := fmt.Sprintf("please configure the %s environment to point to your running cluster:\n", envName)
	noMsg := "you can create a cluster by running the `cortex cluster up` command"
	prompt.YesOrExit(promptStr, yesMsg, noMsg)
	env, err := configureEnv(envName, cliconfig.Environment{})
	if err != nil {
		return cliconfig.Environment{}, err
	}
	return env, nil
}
func getEnvConfigDefaults(envName string) cliconfig.Environment {
	defaults := cliconfig.Environment{}
	prevEnv, err := readEnv(envName)
	if err == nil && prevEnv != nil {
		defaults = *prevEnv
	}
	if defaults.OperatorEndpoint == "" && os.Getenv("CORTEX_OPERATOR_ENDPOINT") != "" {
		defaults.OperatorEndpoint = os.Getenv("CORTEX_OPERATOR_ENDPOINT")
	}
	return defaults
}
func configureEnv(envName string, fieldsToSkipPrompt cliconfig.Environment) (cliconfig.Environment, error) {
	env := cliconfig.Environment{
		Name:             envName,
		OperatorEndpoint: fieldsToSkipPrompt.OperatorEndpoint,
	}
	defaults := getEnvConfigDefaults(env.Name)
	err := promptEnv(&env, defaults)
	if err != nil {
		return cliconfig.Environment{}, err
	}
	if err := env.Validate(); err != nil {
		return cliconfig.Environment{}, err
	}
	if err := addEnvToCLIConfig(env, false); err != nil {
		return cliconfig.Environment{}, err
	}
	print.BoldFirstLine(fmt.Sprintf("configured %s environment", env.Name))
	return env, nil
}
func MustGetOperatorConfig(envName string) cluster.OperatorConfig {
	clientID := clientID()
	env, err := readEnv(envName)
	if err != nil {
		exit.Error(err)
	}
	if env == nil {
		exit.Error(ErrorEnvironmentNotFound(envName))
	}
	operatorConfig := cluster.OperatorConfig{
		Telemetry: isTelemetryEnabled(),
		ClientID:  clientID,
		EnvName:   env.Name,
	}
	if env.OperatorEndpoint == "" {
		exit.Error(ErrorFieldNotFoundInEnvironment(cliconfig.OperatorEndpointKey, env.Name))
	}
	operatorConfig.OperatorEndpoint = env.OperatorEndpoint
	return operatorConfig
}
func listConfiguredEnvs() ([]*cliconfig.Environment, error) {
	cliConfig, err := readCLIConfig()
	if err != nil {
		return nil, err
	}
	return cliConfig.Environments, nil
}
func listConfiguredEnvNames() ([]string, error) {
	envList, err := listConfiguredEnvs()
	if err != nil {
		return nil, err
	}
	envNames := make([]string, len(envList))
	for i, env := range envList {
		envNames[i] = env.Name
	}
	return envNames, nil
}
func isEnvConfigured(envName string) (bool, error) {
	envList, err := listConfiguredEnvs()
	if err != nil {
		return false, err
	}
	for _, env := range envList {
		if env.Name == envName {
			return true, nil
		}
	}
	return false, nil
}
func addEnvToCLIConfig(newEnv cliconfig.Environment, setAsDefault bool) error {
	cliConfig, err := readCLIConfig()
	if err != nil {
		return errors.Wrap(err, "unable to configure cli environment")
	}
	replaced := false
	for i, prevEnv := range cliConfig.Environments {
		if prevEnv.Name == newEnv.Name {
			cliConfig.Environments[i] = &newEnv
			replaced = true
			break
		}
	}
	if !replaced {
		cliConfig.Environments = append(cliConfig.Environments, &newEnv)
	}
	if setAsDefault {
		cliConfig.DefaultEnvironment = &newEnv.Name
	}
	if err := writeCLIConfig(cliConfig); err != nil {
		return errors.Wrap(err, "unable to configure cli environment")
	}
	return nil
}
func removeEnvFromCLIConfig(envName string) error {
	cliConfig, err := readCLIConfig()
	if err != nil {
		return err
	}
	prevDefault, err := getDefaultEnv()
	if err != nil {
		return err
	}
	var updatedEnvs []*cliconfig.Environment
	deleted := false
	for _, env := range cliConfig.Environments {
		if env.Name == envName {
			deleted = true
			continue
		}
		updatedEnvs = append(updatedEnvs, env)
	}
	if !deleted {
		return cliconfig.ErrorEnvironmentNotConfigured(envName)
	}
	cliConfig.Environments = updatedEnvs
	if prevDefault != nil && envName == *prevDefault {
		cliConfig.DefaultEnvironment = nil
	}
	if len(cliConfig.Environments) == 1 {
		cliConfig.DefaultEnvironment = &cliConfig.Environments[0].Name
	}
	if err := writeCLIConfig(cliConfig); err != nil {
		return err
	}
	return nil
}