mirror of
https://gitlab.com/psuapp/psu.git
synced 2024-08-30 18:12:34 +00:00
Fix inconsistencies with EndpointID and SwarmID filtering
This commit is contained in:
parent
0c3e255bf2
commit
b3f1989a25
@ -36,13 +36,13 @@ type PortainerClient interface {
|
||||
GetStacks(swarmId string, endpointId uint32) ([]Stack, error)
|
||||
|
||||
// Create swarm stack
|
||||
CreateSwarmStack(stackName string, environmentVariables []StackEnv, stackFileContent string, swarmClusterId string, endpointId string) error
|
||||
CreateSwarmStack(stackName string, environmentVariables []StackEnv, stackFileContent string, swarmClusterId string, endpointId uint32) error
|
||||
|
||||
// Create compose stack
|
||||
CreateComposeStack(stackName string, environmentVariables []StackEnv, stackFileContent string, endpointId string) error
|
||||
CreateComposeStack(stackName string, environmentVariables []StackEnv, stackFileContent string, endpointId uint32) error
|
||||
|
||||
// Update stack
|
||||
UpdateStack(stack Stack, environmentVariables []StackEnv, stackFileContent string, prune bool, endpointId string) error
|
||||
UpdateStack(stack Stack, environmentVariables []StackEnv, stackFileContent string, prune bool, endpointId uint32) error
|
||||
|
||||
// Delete stack
|
||||
DeleteStack(stackId uint32) error
|
||||
@ -51,7 +51,7 @@ type PortainerClient interface {
|
||||
GetStackFileContent(stackId uint32) (content string, err error)
|
||||
|
||||
// Get endpoint Docker info
|
||||
GetEndpointDockerInfo(endpointId string) (info map[string]interface{}, err error)
|
||||
GetEndpointDockerInfo(endpointId uint32) (info map[string]interface{}, err error)
|
||||
|
||||
// Get Portainer status info
|
||||
GetStatus() (Status, error)
|
||||
@ -231,7 +231,7 @@ func (n *portainerClientImp) GetStacks(swarmId string, endpointId uint32) (stack
|
||||
return
|
||||
}
|
||||
|
||||
func (n *portainerClientImp) CreateSwarmStack(stackName string, environmentVariables []StackEnv, stackFileContent string, swarmClusterId string, endpointId string) (err error) {
|
||||
func (n *portainerClientImp) CreateSwarmStack(stackName string, environmentVariables []StackEnv, stackFileContent string, swarmClusterId string, endpointId uint32) (err error) {
|
||||
reqBody := StackCreateRequest{
|
||||
Name: stackName,
|
||||
Env: environmentVariables,
|
||||
@ -239,29 +239,29 @@ func (n *portainerClientImp) CreateSwarmStack(stackName string, environmentVaria
|
||||
StackFileContent: stackFileContent,
|
||||
}
|
||||
|
||||
err = n.doJSON(fmt.Sprintf("stacks?type=%v&method=%s&endpointId=%s", 1, "string", endpointId), http.MethodPost, &reqBody, nil)
|
||||
err = n.doJSON(fmt.Sprintf("stacks?type=%v&method=%s&endpointId=%v", 1, "string", endpointId), http.MethodPost, &reqBody, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func (n *portainerClientImp) CreateComposeStack(stackName string, environmentVariables []StackEnv, stackFileContent string, endpointId string) (err error) {
|
||||
func (n *portainerClientImp) CreateComposeStack(stackName string, environmentVariables []StackEnv, stackFileContent string, endpointId uint32) (err error) {
|
||||
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)
|
||||
err = n.doJSON(fmt.Sprintf("stacks?type=%v&method=%s&endpointId=%v", 2, "string", endpointId), http.MethodPost, &reqBody, nil)
|
||||
return
|
||||
}
|
||||
|
||||
func (n *portainerClientImp) UpdateStack(stack Stack, environmentVariables []StackEnv, stackFileContent string, prune bool, endpointId string) (err error) {
|
||||
func (n *portainerClientImp) UpdateStack(stack Stack, environmentVariables []StackEnv, stackFileContent string, prune bool, endpointId uint32) (err error) {
|
||||
reqBody := StackUpdateRequest{
|
||||
Env: environmentVariables,
|
||||
StackFileContent: stackFileContent,
|
||||
Prune: prune,
|
||||
}
|
||||
|
||||
err = n.doJSON(fmt.Sprintf("stacks/%v?endpointId=%s", stack.Id, endpointId), http.MethodPut, &reqBody, nil)
|
||||
err = n.doJSON(fmt.Sprintf("stacks/%v?endpointId=%v", stack.Id, endpointId), http.MethodPut, &reqBody, nil)
|
||||
return
|
||||
}
|
||||
|
||||
@ -283,7 +283,7 @@ func (n *portainerClientImp) GetStackFileContent(stackId uint32) (content string
|
||||
return
|
||||
}
|
||||
|
||||
func (n *portainerClientImp) GetEndpointDockerInfo(endpointId string) (info map[string]interface{}, err error) {
|
||||
func (n *portainerClientImp) GetEndpointDockerInfo(endpointId uint32) (info map[string]interface{}, err error) {
|
||||
err = n.doJSON(fmt.Sprintf("endpoints/%v/docker/info", endpointId), http.MethodGet, nil, &info)
|
||||
return
|
||||
}
|
||||
|
@ -31,10 +31,22 @@ var stackDeployCmd = &cobra.Command{
|
||||
common.CheckError(clientRetrievalErr)
|
||||
|
||||
stackName := args[0]
|
||||
endpointId := viper.GetUint32("stack.deploy.endpoint")
|
||||
endpointSwarmClusterId, selectionErr := common.GetEndpointSwarmClusterId(endpointId)
|
||||
switch selectionErr.(type) {
|
||||
case nil:
|
||||
// It's a swarm cluster
|
||||
case *common.StackClusterNotFoundError:
|
||||
// It's not a swarm cluster
|
||||
default:
|
||||
// Something else happened
|
||||
common.CheckError(selectionErr)
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"stack": stackName,
|
||||
}).Debug("Getting stack")
|
||||
retrievedStack, stackRetrievalErr := common.GetStackByName(stackName)
|
||||
retrievedStack, stackRetrievalErr := common.GetStackByName(stackName, endpointSwarmClusterId, endpointId)
|
||||
switch stackRetrievalErr.(type) {
|
||||
case nil:
|
||||
// We are updating an existing stack
|
||||
@ -80,7 +92,7 @@ var stackDeployCmd = &cobra.Command{
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"stack": retrievedStack.Name,
|
||||
}).Info("Updating stack")
|
||||
err := portainerClient.UpdateStack(retrievedStack, newEnvironmentVariables, stackFileContent, viper.GetBool("stack.deploy.prune"), viper.GetString("stack.deploy.endpoint"))
|
||||
err := portainerClient.UpdateStack(retrievedStack, newEnvironmentVariables, stackFileContent, viper.GetBool("stack.deploy.prune"), endpointId)
|
||||
common.CheckError(err)
|
||||
case *common.StackNotFoundError:
|
||||
// We are deploying a new stack
|
||||
@ -96,19 +108,16 @@ var stackDeployCmd = &cobra.Command{
|
||||
stackFileContent, loadingErr := loadStackFile(viper.GetString("stack.deploy.stack-file"))
|
||||
common.CheckError(loadingErr)
|
||||
|
||||
swarmClusterId, selectionErr := getSwarmClusterId()
|
||||
endpointId := viper.GetString("stack.deploy.endpoint")
|
||||
switch selectionErr.(type) {
|
||||
case nil:
|
||||
if endpointSwarmClusterId != "" {
|
||||
// It's a swarm cluster
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"stack": stackName,
|
||||
"endpoint": endpointId,
|
||||
"cluster": swarmClusterId,
|
||||
"cluster": endpointSwarmClusterId,
|
||||
}).Info("Creating stack")
|
||||
deploymentErr := portainerClient.CreateSwarmStack(stackName, loadedEnvironmentVariables, stackFileContent, swarmClusterId, endpointId)
|
||||
deploymentErr := portainerClient.CreateSwarmStack(stackName, loadedEnvironmentVariables, stackFileContent, endpointSwarmClusterId, endpointId)
|
||||
common.CheckError(deploymentErr)
|
||||
case *valueNotFoundError:
|
||||
} else {
|
||||
// It's not a swarm cluster
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"stack": stackName,
|
||||
@ -116,9 +125,6 @@ var stackDeployCmd = &cobra.Command{
|
||||
}).Info("Creating stack")
|
||||
deploymentErr := portainerClient.CreateComposeStack(stackName, loadedEnvironmentVariables, stackFileContent, endpointId)
|
||||
common.CheckError(deploymentErr)
|
||||
default:
|
||||
// Something else happened
|
||||
common.CheckError(stackRetrievalErr)
|
||||
}
|
||||
default:
|
||||
// Something else happened
|
||||
@ -131,7 +137,7 @@ func init() {
|
||||
stackCmd.AddCommand(stackDeployCmd)
|
||||
|
||||
stackDeployCmd.Flags().StringP("stack-file", "c", "", "Path to a file with the content of the stack.")
|
||||
stackDeployCmd.Flags().String("endpoint", "1", "Endpoint ID.")
|
||||
stackDeployCmd.Flags().Uint32("endpoint", 1, "Endpoint ID.")
|
||||
stackDeployCmd.Flags().StringP("env-file", "e", "", "Path to a file with environment variables used during stack deployment.")
|
||||
stackDeployCmd.Flags().Bool("replace-env", false, "Replace environment variables instead of merging them.")
|
||||
stackDeployCmd.Flags().BoolP("prune", "r", false, "Prune services that are no longer referenced (only available for Swarm stacks).")
|
||||
@ -142,43 +148,6 @@ func init() {
|
||||
viper.BindPFlag("stack.deploy.prune", stackDeployCmd.Flags().Lookup("prune"))
|
||||
}
|
||||
|
||||
func getSwarmClusterId() (id string, err error) {
|
||||
// Get docker information for endpoint
|
||||
client, err := common.GetClient()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
endpointId := viper.GetString("stack.deploy.endpoint")
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"endpoint": endpointId,
|
||||
}).Debug("Getting endpoint's Docker info")
|
||||
result, err := client.GetEndpointDockerInfo(endpointId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get swarm (if any) information for endpoint
|
||||
swarmClusterId, err := selectValue(result, []string{"Swarm", "Cluster", "ID"})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
id = swarmClusterId.(string)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func selectValue(jsonMap map[string]interface{}, jsonPath []string) (interface{}, error) {
|
||||
value := jsonMap[jsonPath[0]]
|
||||
if value == nil {
|
||||
return nil, &valueNotFoundError{}
|
||||
} else if len(jsonPath) > 1 {
|
||||
return selectValue(value.(map[string]interface{}), jsonPath[1:])
|
||||
} else {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
func loadStackFile(path string) (string, error) {
|
||||
loadedStackFileContentBytes, readingErr := ioutil.ReadFile(path)
|
||||
if readingErr != nil {
|
||||
@ -204,9 +173,3 @@ func loadEnvironmentVariablesFile(path string) ([]client.StackEnv, error) {
|
||||
|
||||
return variables, nil
|
||||
}
|
||||
|
||||
type valueNotFoundError struct{}
|
||||
|
||||
func (e *valueNotFoundError) Error() string {
|
||||
return "Value not found"
|
||||
}
|
||||
|
@ -5,8 +5,11 @@ import (
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/greenled/portainer-stack-utils/common"
|
||||
"github.com/greenled/portainer-stack-utils/client"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/greenled/portainer-stack-utils/common"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@ -18,17 +21,40 @@ var stackListCmd = &cobra.Command{
|
||||
Aliases: []string{"ls"},
|
||||
Example: "psu stack list --endpoint 1",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
client, err := common.GetClient()
|
||||
portainerClient, err := common.GetClient()
|
||||
common.CheckError(err)
|
||||
|
||||
swarmId := viper.GetString("stack.list.swarm")
|
||||
endpointId := viper.GetUint32("stack.list.endpoint")
|
||||
var endpointSwarmClusterId string
|
||||
var stacks []client.Stack
|
||||
if endpointId != 0 {
|
||||
var selectionErr error
|
||||
endpointSwarmClusterId, selectionErr = common.GetEndpointSwarmClusterId(endpointId)
|
||||
switch selectionErr.(type) {
|
||||
case nil:
|
||||
// It's a swarm cluster
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"endpoint": endpointId,
|
||||
"swarm": endpointSwarmClusterId,
|
||||
}).Debug("Getting stacks")
|
||||
stacks, err = portainerClient.GetStacks(endpointSwarmClusterId, endpointId)
|
||||
common.CheckError(err)
|
||||
case *common.StackClusterNotFoundError:
|
||||
// It's not a swarm cluster
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"swarm": swarmId,
|
||||
"endpoint": endpointId,
|
||||
}).Debug("Getting stacks")
|
||||
stacks, err := client.GetStacks(swarmId, endpointId)
|
||||
stacks, err = portainerClient.GetStacks("", endpointId)
|
||||
common.CheckError(err)
|
||||
default:
|
||||
// Something else happened
|
||||
common.CheckError(selectionErr)
|
||||
}
|
||||
} else {
|
||||
logrus.Debug("Getting stacks")
|
||||
stacks, err = portainerClient.GetStacks("", 0)
|
||||
common.CheckError(err)
|
||||
}
|
||||
|
||||
if viper.GetBool("stack.list.quiet") {
|
||||
// Print only stack names
|
||||
@ -79,11 +105,9 @@ var stackListCmd = &cobra.Command{
|
||||
func init() {
|
||||
stackCmd.AddCommand(stackListCmd)
|
||||
|
||||
stackListCmd.Flags().String("swarm", "", "Filter by swarm ID.")
|
||||
stackListCmd.Flags().String("endpoint", "", "Filter by endpoint ID.")
|
||||
stackListCmd.Flags().Uint32("endpoint", 0, "Filter by endpoint ID.")
|
||||
stackListCmd.Flags().BoolP("quiet", "q", false, "Only display stack names.")
|
||||
stackListCmd.Flags().String("format", "", "Format output using a Go template.")
|
||||
viper.BindPFlag("stack.list.swarm", stackListCmd.Flags().Lookup("swarm"))
|
||||
viper.BindPFlag("stack.list.endpoint", stackListCmd.Flags().Lookup("endpoint"))
|
||||
viper.BindPFlag("stack.list.quiet", stackListCmd.Flags().Lookup("quiet"))
|
||||
viper.BindPFlag("stack.list.format", stackListCmd.Flags().Lookup("format"))
|
||||
|
@ -16,7 +16,7 @@ var stackRemoveCmd = &cobra.Command{
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
stackName := args[0]
|
||||
stack, err := common.GetStackByName(stackName)
|
||||
stack, err := common.GetStackByName(stackName, "", 0)
|
||||
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
|
@ -3,16 +3,18 @@ package common
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/greenled/portainer-stack-utils/client"
|
||||
)
|
||||
|
||||
func GetStackByName(name string) (stack client.Stack, err error) {
|
||||
client, err := GetClient()
|
||||
func GetStackByName(name string, swarmId string, endpointId uint32) (stack client.Stack, err error) {
|
||||
portainerClient, err := GetClient()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
stacks, err := client.GetStacks("", 0)
|
||||
stacks, err := portainerClient.GetStacks(swarmId, endpointId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -28,6 +30,46 @@ func GetStackByName(name string) (stack client.Stack, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func GetEndpointSwarmClusterId(endpointId uint32) (endpointSwarmClusterId string, err error) {
|
||||
// Get docker information for endpoint
|
||||
portainerClient, err := GetClient()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"endpoint": endpointId,
|
||||
}).Debug("Getting endpoint's Docker info")
|
||||
result, err := portainerClient.GetEndpointDockerInfo(endpointId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get swarm (if any) information for endpoint
|
||||
id, selectionErr := selectValue(result, []string{"Swarm", "Cluster", "ID"})
|
||||
switch selectionErr.(type) {
|
||||
case nil:
|
||||
endpointSwarmClusterId = id.(string)
|
||||
case *valueNotFoundError:
|
||||
err = &StackClusterNotFoundError{}
|
||||
default:
|
||||
err = selectionErr
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func selectValue(jsonMap map[string]interface{}, jsonPath []string) (interface{}, error) {
|
||||
value := jsonMap[jsonPath[0]]
|
||||
if value == nil {
|
||||
return nil, &valueNotFoundError{}
|
||||
} else if len(jsonPath) > 1 {
|
||||
return selectValue(value.(map[string]interface{}), jsonPath[1:])
|
||||
} else {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Custom customerrors
|
||||
type StackNotFoundError struct {
|
||||
StackName string
|
||||
@ -36,3 +78,15 @@ type StackNotFoundError struct {
|
||||
func (e *StackNotFoundError) Error() string {
|
||||
return fmt.Sprintf("Stack %s not found", e.StackName)
|
||||
}
|
||||
|
||||
type valueNotFoundError struct{}
|
||||
|
||||
func (e *valueNotFoundError) Error() string {
|
||||
return "Value not found"
|
||||
}
|
||||
|
||||
type StackClusterNotFoundError struct{}
|
||||
|
||||
func (e *StackClusterNotFoundError) Error() string {
|
||||
return "Stack cluster not found"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user