diff --git a/cmd/endpointList.go b/cmd/endpointList.go index 2acb954..ac33507 100644 --- a/cmd/endpointList.go +++ b/cmd/endpointList.go @@ -18,11 +18,12 @@ package cmd import ( "fmt" - "github.com/greenled/portainer-stack-utils/common" - "github.com/spf13/viper" "os" "text/template" + "github.com/greenled/portainer-stack-utils/common" + "github.com/spf13/viper" + "github.com/spf13/cobra" ) @@ -32,7 +33,10 @@ var endpointListCmd = &cobra.Command{ Short: "List endpoints", Aliases: []string{"ls"}, Run: func(cmd *cobra.Command, args []string) { - endpoints, err := common.GetAllEndpoints() + client, err := common.GetClient() + common.CheckError(err) + + endpoints, err := client.GetEndpoints() common.CheckError(err) if viper.GetString("endpoint.list.format") != "" { diff --git a/cmd/stackDeploy.go b/cmd/stackDeploy.go index 7090be0..67ea966 100644 --- a/cmd/stackDeploy.go +++ b/cmd/stackDeploy.go @@ -4,7 +4,6 @@ import ( "fmt" "io/ioutil" "log" - "net/http" "github.com/greenled/portainer-stack-utils/common" "github.com/joho/godotenv" @@ -27,6 +26,9 @@ var stackDeployCmd = &cobra.Command{ common.CheckError(loadingErr) } + client, clientRetrievalErr := common.GetClient() + common.CheckError(clientRetrievalErr) + stackName := args[0] retrievedStack, stackRetrievalErr := common.GetStackByName(stackName) switch stackRetrievalErr.(type) { @@ -41,7 +43,7 @@ var stackDeployCmd = &cobra.Command{ common.CheckError(loadingErr) } else { var stackFileContentRetrievalErr error - stackFileContent, stackFileContentRetrievalErr = getStackFileContent(retrievedStack.Id) + stackFileContent, stackFileContentRetrievalErr = client.GetStackFileContent(retrievedStack.Id) common.CheckError(stackFileContentRetrievalErr) } @@ -66,8 +68,8 @@ var stackDeployCmd = &cobra.Command{ } } - updateErr := updateStack(retrievedStack, newEnvironmentVariables, stackFileContent, viper.GetBool("stack.deploy.prune")) - common.CheckError(updateErr) + err := client.UpdateStack(retrievedStack, newEnvironmentVariables, stackFileContent, viper.GetBool("stack.deploy.prune"), viper.GetString("stack.deploy.endpoint")) + common.CheckError(err) case *common.StackNotFoundError: // We are deploying a new stack common.PrintVerbose(fmt.Sprintf("Stack %s not found. Deploying...", stackName)) @@ -83,12 +85,12 @@ var stackDeployCmd = &cobra.Command{ case nil: // It's a swarm cluster common.PrintVerbose(fmt.Sprintf("Swarm cluster found with id %s", swarmClusterId)) - deploymentErr := deploySwarmStack(stackName, loadedEnvironmentVariables, stackFileContent, swarmClusterId) + deploymentErr := client.CreateSwarmStack(stackName, loadedEnvironmentVariables, stackFileContent, swarmClusterId, viper.GetString("stack.deploy.endpoint")) common.CheckError(deploymentErr) case *valueNotFoundError: // It's not a swarm cluster common.PrintVerbose("Swarm cluster not found") - deploymentErr := deployComposeStack(stackName, loadedEnvironmentVariables, stackFileContent) + deploymentErr := client.CreateComposeStack(stackName, loadedEnvironmentVariables, stackFileContent, viper.GetString("stack.deploy.endpoint")) common.CheckError(deploymentErr) default: // Something else happened @@ -116,58 +118,6 @@ func init() { viper.BindPFlag("stack.deploy.prune", stackDeployCmd.Flags().Lookup("prune")) } -func deploySwarmStack(stackName string, environmentVariables []common.StackEnv, dockerComposeFileContent string, swarmClusterId string) (err error) { - client, err := common.GetClient() - if err != nil { - return - } - - reqBody := common.StackCreateRequest{ - Name: stackName, - Env: environmentVariables, - SwarmID: swarmClusterId, - StackFileContent: dockerComposeFileContent, - } - - err = client.DoJSON(fmt.Sprintf("stacks?type=%v&method=%s&endpointId=%s", 1, "string", viper.GetString("stack.deploy.endpoint")), http.MethodPost, &reqBody, nil) - - return -} - -func deployComposeStack(stackName string, environmentVariables []common.StackEnv, stackFileContent string) (err error) { - client, err := common.GetClient() - if err != nil { - return - } - - reqBody := common.StackCreateRequest{ - Name: stackName, - Env: environmentVariables, - StackFileContent: stackFileContent, - } - - err = client.DoJSON(fmt.Sprintf("stacks?type=%v&method=%s&endpointId=%s", 2, "string", viper.GetString("stack.deploy.endpoint")), http.MethodPost, &reqBody, nil) - - return -} - -func updateStack(stack common.Stack, environmentVariables []common.StackEnv, stackFileContent string, prune bool) (err error) { - client, err := common.GetClient() - if err != nil { - return - } - - reqBody := common.StackUpdateRequest{ - Env: environmentVariables, - StackFileContent: stackFileContent, - Prune: prune, - } - - err = client.DoJSON(fmt.Sprintf("stacks/%v?endpointId=%s", stack.Id, viper.GetString("stack.deploy.endpoint")), http.MethodPut, &reqBody, nil) - - return -} - func getSwarmClusterId() (id string, err error) { // Get docker information for endpoint client, err := common.GetClient() @@ -175,9 +125,7 @@ func getSwarmClusterId() (id string, err error) { return } - var result map[string]interface{} - - err = client.DoJSON(fmt.Sprintf("endpoints/%v/docker/info", viper.GetString("stack.deploy.endpoint")), http.MethodGet, nil, &result) + result, err := client.GetEndpointDockerInfo(viper.GetString("stack.deploy.endpoint")) if err != nil { return } @@ -229,24 +177,6 @@ func loadEnvironmentVariablesFile(path string) ([]common.StackEnv, error) { return variables, nil } -func getStackFileContent(stackId uint32) (content string, err error) { - client, err := common.GetClient() - if err != nil { - return - } - - var respBody common.StackFileInspectResponse - - err = client.DoJSON(fmt.Sprintf("stacks/%v/file", stackId), http.MethodGet, nil, respBody) - if err != nil { - return - } - - content = respBody.StackFileContent - - return -} - type valueNotFoundError struct{} func (e *valueNotFoundError) Error() string { diff --git a/cmd/stackList.go b/cmd/stackList.go index 62023fe..5304966 100644 --- a/cmd/stackList.go +++ b/cmd/stackList.go @@ -2,11 +2,12 @@ package cmd import ( "fmt" + "os" + "text/template" + "github.com/greenled/portainer-stack-utils/common" "github.com/spf13/cobra" "github.com/spf13/viper" - "os" - "text/template" ) // stackListCmd represents the remove command @@ -16,10 +17,10 @@ var stackListCmd = &cobra.Command{ Aliases: []string{"ls"}, Example: "psu stack list --endpoint 1", Run: func(cmd *cobra.Command, args []string) { - stacks, err := common.GetAllStacksFiltered(common.StackListFilter{ - SwarmId: viper.GetString("stack.list.swarm"), - EndpointId: viper.GetUint32("stack.list.endpoint"), - }) + client, err := common.GetClient() + common.CheckError(err) + + stacks, err := client.GetStacks(viper.GetString("stack.list.swarm"), viper.GetUint32("stack.list.endpoint")) common.CheckError(err) if viper.GetBool("stack.list.quiet") { diff --git a/cmd/stackRemove.go b/cmd/stackRemove.go index 74d5721..2eccae1 100644 --- a/cmd/stackRemove.go +++ b/cmd/stackRemove.go @@ -3,7 +3,6 @@ package cmd import ( "fmt" "log" - "net/http" "github.com/greenled/portainer-stack-utils/common" "github.com/spf13/cobra" @@ -33,7 +32,7 @@ var stackRemoveCmd = &cobra.Command{ client, err := common.GetClient() common.CheckError(err) - err = client.DoJSON(fmt.Sprintf("stacks/%d", stackId), http.MethodDelete, nil, nil) + err = client.DeleteStack(stackId) common.CheckError(err) case *common.StackNotFoundError: // The stack does not exist diff --git a/cmd/status.go b/cmd/status.go index 7a95c5c..a57e11a 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "net/http" "os" "text/template" @@ -19,8 +18,7 @@ var statusCmd = &cobra.Command{ client, err := common.GetClient() common.CheckError(err) - var respBody common.Status - err = client.DoJSON("status", http.MethodGet, nil, &respBody) + respBody, err := client.GetStatus() common.CheckError(err) if viper.GetString("status.format") != "" { diff --git a/common/client.go b/common/client.go index 8734bd3..d605564 100644 --- a/common/client.go +++ b/common/client.go @@ -130,6 +130,110 @@ func (n *PortainerClient) Authenticate(user, password string) (token string, err return } +// Get endpoints +func (n *PortainerClient) GetEndpoints() (endpoints []EndpointSubset, err error) { + PrintVerbose("Getting endpoints...") + err = n.DoJSON("endpoints", http.MethodGet, nil, &endpoints) + return +} + +// Get stacks, optionally filtered by swarmId and endpointId +func (n *PortainerClient) GetStacks(swarmId string, endpointId uint32) (stacks []Stack, err error) { + PrintVerbose("Getting stacks...") + + filter := StackListFilter{ + SwarmId: swarmId, + EndpointId: endpointId, + } + + filterJsonBytes, _ := json.Marshal(filter) + filterJsonString := string(filterJsonBytes) + + err = n.DoJSON(fmt.Sprintf("stacks?filters=%s", filterJsonString), http.MethodGet, nil, &stacks) + return +} + +// Create swarm stack +func (n *PortainerClient) CreateSwarmStack(stackName string, environmentVariables []StackEnv, stackFileContent string, swarmClusterId string, endpointId string) (err error) { + PrintVerbose("Deploying stack...") + + reqBody := StackCreateRequest{ + Name: stackName, + Env: environmentVariables, + SwarmID: swarmClusterId, + StackFileContent: stackFileContent, + } + + err = n.DoJSON(fmt.Sprintf("stacks?type=%v&method=%s&endpointId=%s", 1, "string", endpointId), http.MethodPost, &reqBody, nil) + return +} + +// Create compose stack +func (n *PortainerClient) CreateComposeStack(stackName string, environmentVariables []StackEnv, stackFileContent string, endpointId string) (err error) { + PrintVerbose("Deploying stack...") + + reqBody := StackCreateRequest{ + Name: stackName, + Env: environmentVariables, + StackFileContent: stackFileContent, + } + + err = n.DoJSON(fmt.Sprintf("stacks?type=%v&method=%s&endpointId=%s", 2, "string", endpointId), http.MethodPost, &reqBody, nil) + return +} + +// Update stack +func (n *PortainerClient) UpdateStack(stack Stack, environmentVariables []StackEnv, stackFileContent string, prune bool, endpointId string) (err error) { + PrintVerbose("Updating stack...") + + reqBody := StackUpdateRequest{ + Env: environmentVariables, + StackFileContent: stackFileContent, + Prune: prune, + } + + err = n.DoJSON(fmt.Sprintf("stacks/%v?endpointId=%s", stack.Id, endpointId), http.MethodPut, &reqBody, nil) + return +} + +// Delete stack +func (n *PortainerClient) DeleteStack(stackId uint32) (err error) { + PrintVerbose("Deleting stack...") + + err = n.DoJSON(fmt.Sprintf("stacks/%d", stackId), http.MethodDelete, nil, nil) + return +} + +// Get stack file content +func (n *PortainerClient) GetStackFileContent(stackId uint32) (content string, err error) { + PrintVerbose("Getting stack file content...") + + var respBody StackFileInspectResponse + + err = n.DoJSON(fmt.Sprintf("stacks/%v/file", stackId), http.MethodGet, nil, &respBody) + if err != nil { + return + } + + content = respBody.StackFileContent + + return +} + +// Get endpoint Docker info +func (n *PortainerClient) GetEndpointDockerInfo(endpointId string) (info map[string]interface{}, err error) { + PrintVerbose("Getting endpoint Docker info...") + + err = n.DoJSON(fmt.Sprintf("endpoints/%v/docker/info", endpointId), http.MethodGet, nil, &info) + return +} + +// Get Portainer status info +func (n *PortainerClient) GetStatus() (status Status, err error) { + err = n.DoJSON("status", http.MethodGet, nil, &status) + return +} + type clientConfig struct { Url string User string diff --git a/common/utils.go b/common/utils.go index 7a6b87a..378eedf 100644 --- a/common/utils.go +++ b/common/utils.go @@ -1,34 +1,18 @@ package common import ( - "encoding/json" "fmt" - "net/http" ) -func GetAllStacks() ([]Stack, error) { - return GetAllStacksFiltered(StackListFilter{}) -} - -func GetAllStacksFiltered(filter StackListFilter) (stacks []Stack, err error) { - PrintVerbose("Getting all stacks...") - +func GetStackByName(name string) (stack Stack, err error) { client, err := GetClient() if err != nil { return } - filterJsonBytes, _ := json.Marshal(filter) - filterJsonString := string(filterJsonBytes) - - err = client.DoJSON(fmt.Sprintf("stacks?filters=%s", filterJsonString), http.MethodGet, nil, &stacks) - return -} - -func GetStackByName(name string) (Stack, error) { - stacks, err := GetAllStacks() + stacks, err := client.GetStacks("", 0) if err != nil { - return Stack{}, err + return } PrintVerbose(fmt.Sprintf("Getting stack %s...", name)) @@ -37,9 +21,10 @@ func GetStackByName(name string) (Stack, error) { return stack, nil } } - return Stack{}, &StackNotFoundError{ + err = &StackNotFoundError{ StackName: name, } + return } type StackListFilter struct { @@ -55,15 +40,3 @@ type StackNotFoundError struct { func (e *StackNotFoundError) Error() string { return fmt.Sprintf("Stack %s not found", e.StackName) } - -func GetAllEndpoints() (endpoints []EndpointSubset, err error) { - PrintVerbose("Getting all endpoints...") - - client, err := GetClient() - if err != nil { - return - } - - err = client.DoJSON("endpoints", http.MethodGet, nil, &endpoints) - return -}