chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
package exec
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/http/proxy"
|
||||
"github.com/portainer/portainer/api/stacks/stackutils"
|
||||
"github.com/portainer/portainer/pkg/libstack"
|
||||
"github.com/portainer/portainer/pkg/libstack/swarm"
|
||||
)
|
||||
|
||||
// postDeployFailureCheckTimeout bounds how long Deploy waits for tasks to start or fail after being accepted by Swarm.
|
||||
const postDeployFailureCheckTimeout = 30 * time.Second
|
||||
|
||||
// SwarmStackManager represents a service for managing stacks.
|
||||
type SwarmStackManager struct {
|
||||
deployer swarm.Deployer
|
||||
proxyManager *proxy.Manager
|
||||
}
|
||||
|
||||
// NewSwarmStackManager creates a new SwarmStackManager.
|
||||
func NewSwarmStackManager(
|
||||
deployer swarm.Deployer,
|
||||
proxyManager *proxy.Manager,
|
||||
) *SwarmStackManager {
|
||||
return &SwarmStackManager{
|
||||
deployer: deployer,
|
||||
proxyManager: proxyManager,
|
||||
}
|
||||
}
|
||||
|
||||
// Deploy creates or updates a Docker Swarm stack.
|
||||
func (manager *SwarmStackManager) Deploy(
|
||||
ctx context.Context,
|
||||
stack *portainer.Stack,
|
||||
prune bool,
|
||||
pullImage bool,
|
||||
endpoint *portainer.Endpoint,
|
||||
registries []portainer.Registry,
|
||||
) error {
|
||||
url, proxy, err := fetchEndpointProxy(manager.proxyManager, endpoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch environment proxy: %w", err)
|
||||
}
|
||||
|
||||
if proxy != nil {
|
||||
defer proxy.Close()
|
||||
}
|
||||
|
||||
filePaths := stackutils.GetStackFilePaths(stack, true)
|
||||
|
||||
env := make([]string, 0, len(stack.Env))
|
||||
for _, ev := range stack.Env {
|
||||
env = append(env, ev.Name+"="+ev.Value)
|
||||
}
|
||||
|
||||
options := swarm.Options{
|
||||
ProjectName: stack.Name,
|
||||
Host: url,
|
||||
Env: env,
|
||||
Registries: portainerRegistriesToAuthConfigs(registries),
|
||||
}
|
||||
|
||||
if err := manager.deployer.Deploy(context.TODO(), filePaths, swarm.DeployOptions{
|
||||
Options: options,
|
||||
RemoveOrphans: prune,
|
||||
PullImage: pullImage,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Swarm schedules and pulls images asynchronously, so check for early failures before reporting success.
|
||||
waitCtx, cancel := context.WithTimeout(ctx, postDeployFailureCheckTimeout)
|
||||
defer cancel()
|
||||
|
||||
result := manager.deployer.WaitForStatus(waitCtx, stack.Name, options, libstack.StatusRunning)
|
||||
if result.Status == libstack.StatusError {
|
||||
return fmt.Errorf("deployment failed: %s", result.ErrorMsg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove deletes all resources belonging to a Swarm stack.
|
||||
func (manager *SwarmStackManager) Remove(
|
||||
ctx context.Context,
|
||||
stack *portainer.Stack,
|
||||
endpoint *portainer.Endpoint,
|
||||
) error {
|
||||
url, proxy, err := fetchEndpointProxy(manager.proxyManager, endpoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch environment proxy: %w", err)
|
||||
}
|
||||
|
||||
if proxy != nil {
|
||||
defer proxy.Close()
|
||||
}
|
||||
|
||||
return manager.deployer.Remove(context.TODO(), stack.Name, swarm.RemoveOptions{
|
||||
Options: swarm.Options{
|
||||
Host: url,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// NormalizeStackName returns a new stack name with unsupported characters replaced.
|
||||
func (manager *SwarmStackManager) NormalizeStackName(name string) string {
|
||||
return normalizeStackName(name)
|
||||
}
|
||||
Reference in New Issue
Block a user