From 2c0595f5ed743ca1e5b111527b1e9b4eaf3d2669 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Tue, 15 May 2018 14:12:49 +0200 Subject: [PATCH] feat(exec): relocate config.json to data folder and re-use existing content (#1898) --- api/cmd/portainer/main.go | 6 ++-- api/exec/stack_manager.go | 64 +++++++++++++++++++++++++-------------- 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/api/cmd/portainer/main.go b/api/cmd/portainer/main.go index 63563d596..80b62457c 100644 --- a/api/cmd/portainer/main.go +++ b/api/cmd/portainer/main.go @@ -62,8 +62,8 @@ func initStore(dataStorePath string) *bolt.Store { return store } -func initStackManager(assetsPath string, signatureService portainer.DigitalSignatureService, fileService portainer.FileService) (portainer.StackManager, error) { - return exec.NewStackManager(assetsPath, signatureService, fileService) +func initStackManager(assetsPath string, dataStorePath string, signatureService portainer.DigitalSignatureService, fileService portainer.FileService) (portainer.StackManager, error) { + return exec.NewStackManager(assetsPath, dataStorePath, signatureService, fileService) } func initJWTService(authenticationEnabled bool) portainer.JWTService { @@ -232,7 +232,7 @@ func main() { log.Fatal(err) } - stackManager, err := initStackManager(*flags.Assets, digitalSignatureService, fileService) + stackManager, err := initStackManager(*flags.Assets, *flags.Data, digitalSignatureService, fileService) if err != nil { log.Fatal(err) } diff --git a/api/exec/stack_manager.go b/api/exec/stack_manager.go index d8da84df1..cf0f976b4 100644 --- a/api/exec/stack_manager.go +++ b/api/exec/stack_manager.go @@ -2,6 +2,7 @@ package exec import ( "bytes" + "encoding/json" "os" "os/exec" "path" @@ -13,28 +14,22 @@ import ( // StackManager represents a service for managing stacks. type StackManager struct { binaryPath string + dataPath string signatureService portainer.DigitalSignatureService fileService portainer.FileService } -type dockerCLIConfiguration struct { - HTTPHeaders struct { - ManagerOperationHeader string `json:"X-PortainerAgent-ManagerOperation"` - SignatureHeader string `json:"X-PortainerAgent-Signature"` - PublicKey string `json:"X-PortainerAgent-PublicKey"` - } `json:"HttpHeaders"` -} - // NewStackManager initializes a new StackManager service. // It also updates the configuration of the Docker CLI binary. -func NewStackManager(binaryPath string, signatureService portainer.DigitalSignatureService, fileService portainer.FileService) (*StackManager, error) { +func NewStackManager(binaryPath, dataPath string, signatureService portainer.DigitalSignatureService, fileService portainer.FileService) (*StackManager, error) { manager := &StackManager{ binaryPath: binaryPath, + dataPath: dataPath, signatureService: signatureService, fileService: fileService, } - err := manager.updateDockerCLIConfiguration(binaryPath) + err := manager.updateDockerCLIConfiguration(dataPath) if err != nil { return nil, err } @@ -44,7 +39,7 @@ func NewStackManager(binaryPath string, signatureService portainer.DigitalSignat // Login executes the docker login command against a list of registries (including DockerHub). func (manager *StackManager) Login(dockerhub *portainer.DockerHub, registries []portainer.Registry, endpoint *portainer.Endpoint) { - command, args := prepareDockerCommandAndArgs(manager.binaryPath, endpoint) + command, args := prepareDockerCommandAndArgs(manager.binaryPath, manager.dataPath, endpoint) for _, registry := range registries { if registry.Authentication { registryArgs := append(args, "login", "--username", registry.Username, "--password", registry.Password, registry.URL) @@ -60,7 +55,7 @@ func (manager *StackManager) Login(dockerhub *portainer.DockerHub, registries [] // Logout executes the docker logout command. func (manager *StackManager) Logout(endpoint *portainer.Endpoint) error { - command, args := prepareDockerCommandAndArgs(manager.binaryPath, endpoint) + command, args := prepareDockerCommandAndArgs(manager.binaryPath, manager.dataPath, endpoint) args = append(args, "logout") return runCommandAndCaptureStdErr(command, args, nil, "") } @@ -68,7 +63,7 @@ func (manager *StackManager) Logout(endpoint *portainer.Endpoint) error { // Deploy executes the docker stack deploy command. func (manager *StackManager) Deploy(stack *portainer.Stack, prune bool, endpoint *portainer.Endpoint) error { stackFilePath := path.Join(stack.ProjectPath, stack.EntryPoint) - command, args := prepareDockerCommandAndArgs(manager.binaryPath, endpoint) + command, args := prepareDockerCommandAndArgs(manager.binaryPath, manager.dataPath, endpoint) if prune { args = append(args, "stack", "deploy", "--prune", "--with-registry-auth", "--compose-file", stackFilePath, stack.Name) @@ -87,7 +82,7 @@ func (manager *StackManager) Deploy(stack *portainer.Stack, prune bool, endpoint // Remove executes the docker stack rm command. func (manager *StackManager) Remove(stack *portainer.Stack, endpoint *portainer.Endpoint) error { - command, args := prepareDockerCommandAndArgs(manager.binaryPath, endpoint) + command, args := prepareDockerCommandAndArgs(manager.binaryPath, manager.dataPath, endpoint) args = append(args, "stack", "rm", stack.Name) return runCommandAndCaptureStdErr(command, args, nil, "") } @@ -111,7 +106,7 @@ func runCommandAndCaptureStdErr(command string, args []string, env []string, wor return nil } -func prepareDockerCommandAndArgs(binaryPath string, endpoint *portainer.Endpoint) (string, []string) { +func prepareDockerCommandAndArgs(binaryPath, dataPath string, endpoint *portainer.Endpoint) (string, []string) { // Assume Linux as a default command := path.Join(binaryPath, "docker") @@ -120,7 +115,7 @@ func prepareDockerCommandAndArgs(binaryPath string, endpoint *portainer.Endpoint } args := make([]string, 0) - args = append(args, "--config", binaryPath) + args = append(args, "--config", dataPath) args = append(args, "-H", endpoint.URL) if !endpoint.TLSConfig.TLS && endpoint.TLSConfig.TLSSkipVerify { @@ -140,21 +135,46 @@ func prepareDockerCommandAndArgs(binaryPath string, endpoint *portainer.Endpoint return command, args } -func (manager *StackManager) updateDockerCLIConfiguration(binaryPath string) error { - config := dockerCLIConfiguration{} - config.HTTPHeaders.ManagerOperationHeader = "1" +func (manager *StackManager) updateDockerCLIConfiguration(dataPath string) error { + configFilePath := path.Join(dataPath, "config.json") + config, err := manager.retrieveConfigurationFromDisk(configFilePath) + if err != nil { + return err + } signature, err := manager.signatureService.Sign(portainer.PortainerAgentSignatureMessage) if err != nil { return err } - config.HTTPHeaders.SignatureHeader = signature - config.HTTPHeaders.PublicKey = manager.signatureService.EncodedPublicKey() - err = manager.fileService.WriteJSONToFile(path.Join(binaryPath, "config.json"), config) + if config["HttpHeaders"] == nil { + config["HttpHeaders"] = make(map[string]interface{}) + } + headersObject := config["HttpHeaders"].(map[string]interface{}) + headersObject["X-PortainerAgent-ManagerOperation"] = "1" + headersObject["X-PortainerAgent-Signature"] = signature + headersObject["X-PortainerAgent-PublicKey"] = manager.signatureService.EncodedPublicKey() + + err = manager.fileService.WriteJSONToFile(configFilePath, config) if err != nil { return err } return nil } + +func (manager *StackManager) retrieveConfigurationFromDisk(path string) (map[string]interface{}, error) { + var config map[string]interface{} + + raw, err := manager.fileService.GetFileContent(path) + if err != nil { + return make(map[string]interface{}), nil + } + + err = json.Unmarshal([]byte(raw), &config) + if err != nil { + return nil, err + } + + return config, nil +}