diff --git a/api/adminmonitor/admin_monitor.go b/api/adminmonitor/admin_monitor.go index 2162e1cad..e30995971 100644 --- a/api/adminmonitor/admin_monitor.go +++ b/api/adminmonitor/admin_monitor.go @@ -19,24 +19,22 @@ const RedirectReasonAdminInitTimeout string = "AdminInitTimeout" type Monitor struct { timeout time.Duration datastore dataservices.DataStore - shutdownCtx context.Context cancellationFunc context.CancelFunc mu sync.RWMutex adminInitDisabled bool } // New creates a monitor that when started will wait for the timeout duration and then shutdown the application unless it has been initialized. -func New(timeout time.Duration, datastore dataservices.DataStore, shutdownCtx context.Context) *Monitor { +func New(timeout time.Duration, datastore dataservices.DataStore) *Monitor { return &Monitor{ timeout: timeout, datastore: datastore, - shutdownCtx: shutdownCtx, adminInitDisabled: false, } } -// Starts starts the monitor. Active monitor could be stopped or shuttted down by cancelling the shutdown context. -func (m *Monitor) Start() { +// Start starts the monitor. The monitor will stop when ctx is cancelled, or when Stop is called. +func (m *Monitor) Start(ctx context.Context) { m.mu.Lock() defer m.mu.Unlock() @@ -44,7 +42,7 @@ func (m *Monitor) Start() { return } - cancellationCtx, cancellationFunc := context.WithCancel(context.Background()) + cancellationCtx, cancellationFunc := context.WithCancel(ctx) m.cancellationFunc = cancellationFunc go func() { @@ -69,8 +67,6 @@ func (m *Monitor) Start() { } case <-cancellationCtx.Done(): log.Debug().Msg("canceling initialization monitor") - case <-m.shutdownCtx.Done(): - log.Debug().Msg("shutting down initialization monitor") } }() } diff --git a/api/adminmonitor/admin_monitor_test.go b/api/adminmonitor/admin_monitor_test.go index 08727fc2b..20ecb4ecd 100644 --- a/api/adminmonitor/admin_monitor_test.go +++ b/api/adminmonitor/admin_monitor_test.go @@ -1,7 +1,6 @@ package adminmonitor import ( - "context" "testing" "time" @@ -11,21 +10,21 @@ import ( ) func Test_stopWithoutStarting(t *testing.T) { - monitor := New(1*time.Minute, nil, nil) + monitor := New(1*time.Minute, nil) monitor.Stop() } func Test_stopCouldBeCalledMultipleTimes(t *testing.T) { - monitor := New(1*time.Minute, nil, nil) + monitor := New(1*time.Minute, nil) monitor.Stop() monitor.Stop() } func Test_startOrStopCouldBeCalledMultipleTimesConcurrently(t *testing.T) { - monitor := New(1*time.Minute, nil, context.Background()) + monitor := New(1*time.Minute, nil) - go monitor.Start() - monitor.Start() + go monitor.Start(t.Context()) + monitor.Start(t.Context()) go monitor.Stop() monitor.Stop() @@ -34,8 +33,8 @@ func Test_startOrStopCouldBeCalledMultipleTimesConcurrently(t *testing.T) { } func Test_canStopStartedMonitor(t *testing.T) { - monitor := New(1*time.Minute, nil, context.Background()) - monitor.Start() + monitor := New(1*time.Minute, nil) + monitor.Start(t.Context()) assert.NotNil(t, monitor.cancellationFunc, "cancellation function is missing in started monitor") monitor.Stop() @@ -46,8 +45,8 @@ func Test_start_shouldDisableInstanceAfterTimeout_ifNotInitialized(t *testing.T) timeout := 10 * time.Millisecond datastore := i.NewDatastore(i.WithUsers([]portainer.User{})) - monitor := New(timeout, datastore, context.Background()) - monitor.Start() + monitor := New(timeout, datastore) + monitor.Start(t.Context()) <-time.After(20 * timeout) assert.True(t, monitor.WasInstanceDisabled(), "monitor should have been timeout and instance is disabled") diff --git a/api/chisel/service_test.go b/api/chisel/service_test.go index e8fa71a5a..286463044 100644 --- a/api/chisel/service_test.go +++ b/api/chisel/service_test.go @@ -1,7 +1,6 @@ package chisel import ( - "context" "net" "net/http" "testing" @@ -54,6 +53,6 @@ func TestPingAgentPanic(t *testing.T) { s.activeTunnels[endpoint.ID].Port = ln.Addr().(*net.TCPAddr).Port require.Error(t, s.pingAgent(endpoint.ID)) - require.NoError(t, srv.Shutdown(context.Background())) + require.NoError(t, srv.Shutdown(t.Context())) require.ErrorIs(t, <-errCh, http.ErrServerClosed) } diff --git a/api/cmd/portainer/main.go b/api/cmd/portainer/main.go index e6f176086..ca3b24f6f 100644 --- a/api/cmd/portainer/main.go +++ b/api/cmd/portainer/main.go @@ -216,13 +216,12 @@ func initSnapshotService( dataStore dataservices.DataStore, dockerClientFactory *dockerclient.ClientFactory, kubernetesClientFactory *kubecli.ClientFactory, - shutdownCtx context.Context, pendingActionsService *pendingactions.PendingActionsService, ) (portainer.SnapshotService, error) { dockerSnapshotter := docker.NewSnapshotter(dockerClientFactory) kubernetesSnapshotter := kubernetes.NewSnapshotter(kubernetesClientFactory) - snapshotService, err := snapshot.NewService(snapshotIntervalFromFlag, dataStore, dockerSnapshotter, kubernetesSnapshotter, shutdownCtx, pendingActionsService) + snapshotService, err := snapshot.NewService(snapshotIntervalFromFlag, dataStore, dockerSnapshotter, kubernetesSnapshotter, pendingActionsService) if err != nil { return nil, err } @@ -338,8 +337,7 @@ func loadEncryptionSecretKey(keyfilename string) []byte { return hash[:] } -func buildServer(flags *portainer.CLIFlags) portainer.Server { - shutdownCtx, shutdownTrigger := context.WithCancel(context.Background()) +func buildServer(flags *portainer.CLIFlags, shutdownCtx context.Context, shutdownTrigger context.CancelFunc) portainer.Server { if flags.FeatureFlags != nil { featureflags.Parse(*flags.FeatureFlags, portainer.SupportedFeatureFlags) @@ -461,12 +459,12 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server { pendingActionsService.RegisterHandler(actions.DeletePortainerK8sRegistrySecrets, handlers.NewHandlerDeleteRegistrySecrets(authorizationService, dataStore, kubernetesClientFactory)) pendingActionsService.RegisterHandler(actions.PostInitMigrateEnvironment, handlers.NewHandlerPostInitMigrateEnvironment(authorizationService, dataStore, kubernetesClientFactory, dockerClientFactory, *flags.Assets, kubernetesDeployer)) - snapshotService, err := initSnapshotService(*flags.SnapshotInterval, dataStore, dockerClientFactory, kubernetesClientFactory, shutdownCtx, pendingActionsService) + snapshotService, err := initSnapshotService(*flags.SnapshotInterval, dataStore, dockerClientFactory, kubernetesClientFactory, pendingActionsService) if err != nil { log.Fatal().Err(err).Msg("failed initializing snapshot service") } - snapshotService.Start() + snapshotService.Start(shutdownCtx) proxyManager.NewProxyFactory(dataStore, signatureService, reverseTunnelService, dockerClientFactory, kubernetesClientFactory, kubernetesTokenCacheManager, gitService, snapshotService, jwtService) @@ -604,7 +602,6 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server { DockerClientFactory: dockerClientFactory, KubernetesClientFactory: kubernetesClientFactory, Scheduler: scheduler, - ShutdownCtx: shutdownCtx, ShutdownTrigger: shutdownTrigger, StackDeployer: stackDeployer, UpgradeService: upgradeService, @@ -626,7 +623,8 @@ func main() { logs.SetLoggingMode(*flags.LogMode) for { - server := buildServer(flags) + shutdownCtx, shutdownTrigger := context.WithCancel(context.Background()) + server := buildServer(flags, shutdownCtx, shutdownTrigger) log.Info(). Str("version", portainer.APIVersion). @@ -638,7 +636,7 @@ func main() { Str("go_version", build.GoVersion). Msg("starting Portainer") - err := server.Start() + err := server.Start(shutdownCtx) log.Info().Err(err).Msg("HTTP server exited") } diff --git a/api/docker/stats/container_stats_test.go b/api/docker/stats/container_stats_test.go index fcb4de51f..e25cec395 100644 --- a/api/docker/stats/container_stats_test.go +++ b/api/docker/stats/container_stats_test.go @@ -78,7 +78,7 @@ func TestCalculateContainerStats(t *testing.T) { // Call the function and measure time startTime := time.Now() - stats, err := CalculateContainerStats(context.Background(), mockClient, false, containers) + stats, err := CalculateContainerStats(t.Context(), mockClient, false, containers) require.NoError(t, err, "failed to calculate container stats") duration := time.Since(startTime) @@ -118,7 +118,7 @@ func TestCalculateContainerStatsAllErrors(t *testing.T) { mockClient.On("ContainerInspect", mock.Anything, "container2").Return(container.InspectResponse{}, errors.New("permission denied")) // Call the function - stats, err := CalculateContainerStats(context.Background(), mockClient, false, containers) + stats, err := CalculateContainerStats(t.Context(), mockClient, false, containers) // Assert that an error was returned require.Error(t, err, "should return error when all containers fail to inspect") diff --git a/api/exec/compose_stack_integration_test.go b/api/exec/compose_stack_integration_test.go index 23f48ebb4..39e3ed97e 100644 --- a/api/exec/compose_stack_integration_test.go +++ b/api/exec/compose_stack_integration_test.go @@ -1,7 +1,6 @@ package exec import ( - "context" "os" "os/exec" "path/filepath" @@ -50,9 +49,7 @@ func Test_UpAndDown(t *testing.T) { w := NewComposeStackManager(deployer, nil, nil) - ctx := context.TODO() - - if err := w.Up(ctx, stack, endpoint, portainer.ComposeUpOptions{}); err != nil { + if err := w.Up(t.Context(), stack, endpoint, portainer.ComposeUpOptions{}); err != nil { t.Fatalf("Error calling docker-compose up: %s", err) } @@ -60,7 +57,7 @@ func Test_UpAndDown(t *testing.T) { t.Fatal("container should exist") } - if err := w.Down(ctx, stack, endpoint); err != nil { + if err := w.Down(t.Context(), stack, endpoint); err != nil { t.Fatalf("Error calling docker-compose down: %s", err) } diff --git a/api/exec/exectest/kubernetes_mocks.go b/api/exec/exectest/kubernetes_mocks.go index 7d2afac73..3b2de189d 100644 --- a/api/exec/exectest/kubernetes_mocks.go +++ b/api/exec/exectest/kubernetes_mocks.go @@ -1,6 +1,8 @@ package exectest import ( + "context" + portainer "github.com/portainer/portainer/api" ) @@ -13,14 +15,14 @@ func NewKubernetesDeployer() *kubernetesMockDeployer { return &kubernetesMockDeployer{} } -func (deployer *kubernetesMockDeployer) Deploy(userID portainer.UserID, endpoint *portainer.Endpoint, manifestFiles []string, namespace string) (string, error) { +func (deployer *kubernetesMockDeployer) Deploy(_ context.Context, userID portainer.UserID, endpoint *portainer.Endpoint, manifestFiles []string, namespace string) (string, error) { return "", nil } -func (deployer *kubernetesMockDeployer) Remove(userID portainer.UserID, endpoint *portainer.Endpoint, manifestFiles []string, namespace string) (string, error) { +func (deployer *kubernetesMockDeployer) Remove(_ context.Context, userID portainer.UserID, endpoint *portainer.Endpoint, manifestFiles []string, namespace string) (string, error) { return "", nil } -func (deployer *kubernetesMockDeployer) Restart(userID portainer.UserID, endpoint *portainer.Endpoint, manifestFiles []string, namespace string) (string, error) { +func (deployer *kubernetesMockDeployer) Restart(_ context.Context, userID portainer.UserID, endpoint *portainer.Endpoint, manifestFiles []string, namespace string) (string, error) { return "", nil } diff --git a/api/exec/kubernetes_deploy.go b/api/exec/kubernetes_deploy.go index b89c15e3c..db6d9b4aa 100644 --- a/api/exec/kubernetes_deploy.go +++ b/api/exec/kubernetes_deploy.go @@ -76,16 +76,16 @@ func (deployer *KubernetesDeployer) getToken(userID portainer.UserID, endpoint * } // Deploy upserts Kubernetes resources defined in manifest(s) -func (deployer *KubernetesDeployer) Deploy(userID portainer.UserID, endpoint *portainer.Endpoint, resources []string, namespace string) (string, error) { - return deployer.command("apply", userID, endpoint, resources, namespace) +func (deployer *KubernetesDeployer) Deploy(ctx context.Context, userID portainer.UserID, endpoint *portainer.Endpoint, resources []string, namespace string) (string, error) { + return deployer.command(ctx, "apply", userID, endpoint, resources, namespace) } // Remove deletes Kubernetes resources defined in manifest(s) -func (deployer *KubernetesDeployer) Remove(userID portainer.UserID, endpoint *portainer.Endpoint, resources []string, namespace string) (string, error) { - return deployer.command("delete", userID, endpoint, resources, namespace) +func (deployer *KubernetesDeployer) Remove(ctx context.Context, userID portainer.UserID, endpoint *portainer.Endpoint, resources []string, namespace string) (string, error) { + return deployer.command(ctx, "delete", userID, endpoint, resources, namespace) } -func (deployer *KubernetesDeployer) command(operation string, userID portainer.UserID, endpoint *portainer.Endpoint, resources []string, namespace string) (string, error) { +func (deployer *KubernetesDeployer) command(ctx context.Context, operation string, userID portainer.UserID, endpoint *portainer.Endpoint, resources []string, namespace string) (string, error) { token, err := deployer.getToken(userID, endpoint, endpoint.Type == portainer.KubernetesLocalEnvironment) if err != nil { return "", errors.Wrap(err, "failed generating a user token") @@ -120,7 +120,7 @@ func (deployer *KubernetesDeployer) command(operation string, userID portainer.U return "", errors.Errorf("unsupported operation: %s", operation) } - output, err := operationFunc(context.Background(), resources) + output, err := operationFunc(ctx, resources) if err != nil { return "", errors.Wrapf(err, "failed to execute kubectl %s command", operation) } diff --git a/api/exec/swarm_stack.go b/api/exec/swarm_stack.go index d43bd24ab..c8bfbbd2a 100644 --- a/api/exec/swarm_stack.go +++ b/api/exec/swarm_stack.go @@ -2,6 +2,7 @@ package exec import ( "bytes" + "context" "errors" "os" "os/exec" @@ -53,7 +54,7 @@ func NewSwarmStackManager( } // Login executes the docker login command against a list of registries (including DockerHub). -func (manager *SwarmStackManager) Login(registries []portainer.Registry, endpoint *portainer.Endpoint) error { +func (manager *SwarmStackManager) Login(ctx context.Context, registries []portainer.Registry, endpoint *portainer.Endpoint) error { command, args, err := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.configPath, endpoint) if err != nil { return err @@ -67,7 +68,7 @@ func (manager *SwarmStackManager) Login(registries []portainer.Registry, endpoin } registryArgs := append(args, "login", "--username", username, "--password", password, registry.URL) - if err := runCommandAndCaptureStdErr(command, registryArgs, nil, ""); err != nil { + if err := runCommandAndCaptureStdErr(ctx, command, registryArgs, nil, ""); err != nil { log.Warn(). Err(err). Str("RegistryName", registry.Name). @@ -80,7 +81,7 @@ func (manager *SwarmStackManager) Login(registries []portainer.Registry, endpoin } // Logout executes the docker logout command. -func (manager *SwarmStackManager) Logout(endpoint *portainer.Endpoint) error { +func (manager *SwarmStackManager) Logout(ctx context.Context, endpoint *portainer.Endpoint) error { command, args, err := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.configPath, endpoint) if err != nil { return err @@ -88,11 +89,11 @@ func (manager *SwarmStackManager) Logout(endpoint *portainer.Endpoint) error { args = append(args, "logout") - return runCommandAndCaptureStdErr(command, args, nil, "") + return runCommandAndCaptureStdErr(ctx, command, args, nil, "") } // Deploy executes the docker stack deploy command. -func (manager *SwarmStackManager) Deploy(stack *portainer.Stack, prune bool, pullImage bool, endpoint *portainer.Endpoint) error { +func (manager *SwarmStackManager) Deploy(ctx context.Context, stack *portainer.Stack, prune bool, pullImage bool, endpoint *portainer.Endpoint) error { filePaths := stackutils.GetStackFilePaths(stack, true) command, args, err := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.configPath, endpoint) if err != nil { @@ -117,11 +118,11 @@ func (manager *SwarmStackManager) Deploy(stack *portainer.Stack, prune bool, pul env = append(env, envvar.Name+"="+envvar.Value) } - return runCommandAndCaptureStdErr(command, args, env, stack.ProjectPath) + return runCommandAndCaptureStdErr(ctx, command, args, env, stack.ProjectPath) } // Remove executes the docker stack rm command. -func (manager *SwarmStackManager) Remove(stack *portainer.Stack, endpoint *portainer.Endpoint) error { +func (manager *SwarmStackManager) Remove(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error { command, args, err := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.configPath, endpoint) if err != nil { return err @@ -129,13 +130,13 @@ func (manager *SwarmStackManager) Remove(stack *portainer.Stack, endpoint *porta args = append(args, "stack", "rm", "--detach=false", stack.Name) - return runCommandAndCaptureStdErr(command, args, nil, "") + return runCommandAndCaptureStdErr(ctx, command, args, nil, "") } -func runCommandAndCaptureStdErr(command string, args []string, env []string, workingDir string) error { +func runCommandAndCaptureStdErr(ctx context.Context, command string, args []string, env []string, workingDir string) error { var stderr bytes.Buffer - cmd := exec.Command(command, args...) + cmd := exec.CommandContext(ctx, command, args...) cmd.Stderr = &stderr if workingDir != "" { diff --git a/api/git/azure_integration_test.go b/api/git/azure_integration_test.go index b128c4347..727dfaa47 100644 --- a/api/git/azure_integration_test.go +++ b/api/git/azure_integration_test.go @@ -1,7 +1,6 @@ package git import ( - "context" "fmt" "os" "path/filepath" @@ -21,7 +20,7 @@ func TestService_ClonePublicRepository_Azure(t *testing.T) { ensureIntegrationTest(t) pat := getRequiredValue(t, "AZURE_DEVOPS_PAT") - service := NewService(context.TODO()) + service := NewService(t.Context()) type args struct { repositoryURLFormat string @@ -60,6 +59,7 @@ func TestService_ClonePublicRepository_Azure(t *testing.T) { dst := t.TempDir() repositoryUrl := fmt.Sprintf(tt.args.repositoryURLFormat, tt.args.password) err := service.CloneRepository( + t.Context(), dst, repositoryUrl, tt.args.referenceName, @@ -77,11 +77,12 @@ func TestService_ClonePrivateRepository_Azure(t *testing.T) { ensureIntegrationTest(t) pat := getRequiredValue(t, "AZURE_DEVOPS_PAT") - service := NewService(context.TODO()) + service := NewService(t.Context()) dst := t.TempDir() err := service.CloneRepository( + t.Context(), dst, privateAzureRepoURL, "refs/heads/main", @@ -97,9 +98,10 @@ func TestService_LatestCommitID_Azure(t *testing.T) { ensureIntegrationTest(t) pat := getRequiredValue(t, "AZURE_DEVOPS_PAT") - service := NewService(context.TODO()) + service := NewService(t.Context()) id, err := service.LatestCommitID( + t.Context(), privateAzureRepoURL, "refs/heads/main", "", @@ -115,9 +117,10 @@ func TestService_ListRefs_Azure(t *testing.T) { accessToken := getRequiredValue(t, "AZURE_DEVOPS_PAT") username := getRequiredValue(t, "AZURE_DEVOPS_USERNAME") - service := NewService(context.TODO()) + service := NewService(t.Context()) refs, err := service.ListRefs( + t.Context(), privateAzureRepoURL, username, accessToken, @@ -133,13 +136,13 @@ func TestService_ListRefs_Azure_Concurrently(t *testing.T) { accessToken := getRequiredValue(t, "AZURE_DEVOPS_PAT") username := getRequiredValue(t, "AZURE_DEVOPS_USERNAME") - service := newService(context.TODO(), repositoryCacheSize, 200*time.Millisecond) + service := newService(t.Context(), repositoryCacheSize, 200*time.Millisecond) go func() { - _, _ = service.ListRefs(privateAzureRepoURL, username, accessToken, false, false) + _, _ = service.ListRefs(t.Context(), privateAzureRepoURL, username, accessToken, false, false) }() - _, err := service.ListRefs(privateAzureRepoURL, username, accessToken, false, false) + _, err := service.ListRefs(t.Context(), privateAzureRepoURL, username, accessToken, false, false) require.NoError(t, err) time.Sleep(2 * time.Second) @@ -162,7 +165,7 @@ func TestService_ListFiles_Azure(t *testing.T) { matchedCount int } - service := newService(context.TODO(), 0, 0) + service := newService(t.Context(), 0, 0) accessToken := getRequiredValue(t, "AZURE_DEVOPS_PAT") username := getRequiredValue(t, "AZURE_DEVOPS_USERNAME") @@ -273,6 +276,7 @@ func TestService_ListFiles_Azure(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { paths, err := service.ListFiles( + t.Context(), tt.args.repositoryUrl, tt.args.referenceName, tt.args.username, @@ -303,10 +307,11 @@ func TestService_ListFiles_Azure_Concurrently(t *testing.T) { accessToken := getRequiredValue(t, "AZURE_DEVOPS_PAT") username := getRequiredValue(t, "AZURE_DEVOPS_USERNAME") - service := newService(context.TODO(), repositoryCacheSize, 200*time.Millisecond) + service := newService(t.Context(), repositoryCacheSize, 200*time.Millisecond) go func() { _, _ = service.ListFiles( + t.Context(), privateAzureRepoURL, "refs/heads/main", username, @@ -319,6 +324,7 @@ func TestService_ListFiles_Azure_Concurrently(t *testing.T) { }() _, err := service.ListFiles( + t.Context(), privateAzureRepoURL, "refs/heads/main", username, diff --git a/api/git/azure_test.go b/api/git/azure_test.go index 88e27c041..2cfcd2fae 100644 --- a/api/git/azure_test.go +++ b/api/git/azure_test.go @@ -311,7 +311,7 @@ func Test_azureDownloader_downloadZipFromAzureDevOps(t *testing.T) { Password: tt.args.password, } } - _, err := a.downloadZipFromAzureDevOps(context.Background(), option) + _, err := a.downloadZipFromAzureDevOps(t.Context(), option) require.Error(t, err) assert.Equal(t, tt.want, zipRequestAuth) }) @@ -367,7 +367,7 @@ func Test_azureDownloader_latestCommitID(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - id, err := a.LatestCommitID(context.Background(), tt.args.repositoryUrl, tt.args.referenceName, &git.ListOptions{}) + id, err := a.LatestCommitID(t.Context(), tt.args.repositoryUrl, tt.args.referenceName, &git.ListOptions{}) if (err != nil) != tt.wantErr { t.Errorf("azureDownloader.latestCommitID() error = %v, wantErr %v", err, tt.wantErr) return @@ -427,7 +427,7 @@ func Test_cloneRepository_azure(t *testing.T) { git := &testRepoManager{} s := &Service{azure: azure, git: git} - err := s.CloneRepository("", tt.url, "", "", "", false) + err := s.CloneRepository(t.Context(), "", tt.url, "", "", "", false) require.NoError(t, err) // if azure API is called, git isn't and vice versa @@ -517,7 +517,7 @@ func Test_listRefs_azure(t *testing.T) { Password: tt.args.password, } } - refs, err := client.ListRefs(context.TODO(), tt.args.repositoryUrl, option) + refs, err := client.ListRefs(t.Context(), tt.args.repositoryUrl, option) if tt.expect.err == nil { require.NoError(t, err) if tt.expect.refsCount > 0 { @@ -634,7 +634,7 @@ func Test_listFiles_azure(t *testing.T) { Password: tt.args.password, } } - paths, err := client.ListFiles(context.TODO(), false, option) + paths, err := client.ListFiles(t.Context(), false, option) if tt.expect.shouldFail { require.Error(t, err) if tt.expect.err != nil { diff --git a/api/git/backup.go b/api/git/backup.go index ce23309dc..3dbbc9b95 100644 --- a/api/git/backup.go +++ b/api/git/backup.go @@ -1,6 +1,8 @@ package git import ( + "context" + portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/filesystem" gittypes "github.com/portainer/portainer/api/git/types" @@ -23,7 +25,7 @@ type CloneOptions struct { TLSSkipVerify bool `example:"false"` } -func CloneWithBackup(gitService portainer.GitService, fileService portainer.FileService, options CloneOptions) (clean func(), err error) { +func CloneWithBackup(ctx context.Context, gitService portainer.GitService, fileService portainer.FileService, options CloneOptions) (clean func(), err error) { backupProjectPath := options.ProjectPath + "-old" cleanUp := false cleanFn := func() { @@ -43,6 +45,7 @@ func CloneWithBackup(gitService portainer.GitService, fileService portainer.File cleanUp = true if err := gitService.CloneRepository( + ctx, options.ProjectPath, options.URL, options.ReferenceName, diff --git a/api/git/git_integration_test.go b/api/git/git_integration_test.go index 09680e5fb..17a7c81ea 100644 --- a/api/git/git_integration_test.go +++ b/api/git/git_integration_test.go @@ -1,7 +1,6 @@ package git import ( - "context" "net/http" "net/http/httptest" "path/filepath" @@ -23,12 +22,13 @@ func TestService_ClonePrivateRepository_GitHub(t *testing.T) { accessToken := getRequiredValue(t, "GITHUB_PAT") username := getRequiredValue(t, "GITHUB_USERNAME") - service := newService(context.TODO(), 0, 0) + service := newService(t.Context(), 0, 0) dst := t.TempDir() repositoryUrl := privateGitRepoURL err := service.CloneRepository( + t.Context(), dst, repositoryUrl, "refs/heads/main", @@ -45,10 +45,11 @@ func TestService_LatestCommitID_GitHub(t *testing.T) { accessToken := getRequiredValue(t, "GITHUB_PAT") username := getRequiredValue(t, "GITHUB_USERNAME") - service := newService(context.TODO(), 0, 0) + service := newService(t.Context(), 0, 0) repositoryUrl := privateGitRepoURL id, err := service.LatestCommitID( + t.Context(), repositoryUrl, "refs/heads/main", username, @@ -64,10 +65,10 @@ func TestService_ListRefs_GitHub(t *testing.T) { accessToken := getRequiredValue(t, "GITHUB_PAT") username := getRequiredValue(t, "GITHUB_USERNAME") - service := newService(context.TODO(), 0, 0) + service := newService(t.Context(), 0, 0) repositoryUrl := privateGitRepoURL - refs, err := service.ListRefs(repositoryUrl, username, accessToken, false, false) + refs, err := service.ListRefs(t.Context(), repositoryUrl, username, accessToken, false, false) require.NoError(t, err) assert.GreaterOrEqual(t, len(refs), 1) } @@ -77,14 +78,14 @@ func TestService_ListRefs_Github_Concurrently(t *testing.T) { accessToken := getRequiredValue(t, "GITHUB_PAT") username := getRequiredValue(t, "GITHUB_USERNAME") - service := newService(context.TODO(), repositoryCacheSize, 200*time.Millisecond) + service := newService(t.Context(), repositoryCacheSize, 200*time.Millisecond) repositoryUrl := privateGitRepoURL go func() { - _, _ = service.ListRefs(repositoryUrl, username, accessToken, false, false) + _, _ = service.ListRefs(t.Context(), repositoryUrl, username, accessToken, false, false) }() - _, err := service.ListRefs(repositoryUrl, username, accessToken, false, false) + _, err := service.ListRefs(t.Context(), repositoryUrl, username, accessToken, false, false) require.NoError(t, err) time.Sleep(2 * time.Second) @@ -106,7 +107,7 @@ func TestService_ListFiles_GitHub(t *testing.T) { err error matchedCount int } - service := newService(context.TODO(), 0, 0) + service := newService(t.Context(), 0, 0) accessToken := getRequiredValue(t, "GITHUB_PAT") username := getRequiredValue(t, "GITHUB_USERNAME") @@ -217,6 +218,7 @@ func TestService_ListFiles_GitHub(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { paths, err := service.ListFiles( + t.Context(), tt.args.repositoryUrl, tt.args.referenceName, tt.args.username, @@ -247,10 +249,11 @@ func TestService_ListFiles_Github_Concurrently(t *testing.T) { repositoryUrl := privateGitRepoURL accessToken := getRequiredValue(t, "GITHUB_PAT") username := getRequiredValue(t, "GITHUB_USERNAME") - service := newService(context.TODO(), repositoryCacheSize, 200*time.Millisecond) + service := newService(t.Context(), repositoryCacheSize, 200*time.Millisecond) go func() { _, _ = service.ListFiles( + t.Context(), repositoryUrl, "refs/heads/main", username, @@ -263,6 +266,7 @@ func TestService_ListFiles_Github_Concurrently(t *testing.T) { }() _, err := service.ListFiles( + t.Context(), repositoryUrl, "refs/heads/main", username, @@ -283,12 +287,13 @@ func TestService_purgeCache_Github(t *testing.T) { repositoryUrl := privateGitRepoURL accessToken := getRequiredValue(t, "GITHUB_PAT") username := getRequiredValue(t, "GITHUB_USERNAME") - service := NewService(context.TODO()) + service := NewService(t.Context()) - _, err := service.ListRefs(repositoryUrl, username, accessToken, false, false) + _, err := service.ListRefs(t.Context(), repositoryUrl, username, accessToken, false, false) require.NoError(t, err) _, err = service.ListFiles( + t.Context(), repositoryUrl, "refs/heads/main", username, @@ -316,11 +321,12 @@ func TestService_purgeCacheByTTL_Github(t *testing.T) { accessToken := getRequiredValue(t, "GITHUB_PAT") username := getRequiredValue(t, "GITHUB_USERNAME") // 40*timeout is designed for giving enough time for ListRefs and ListFiles to cache the result - service := newService(context.TODO(), 2, 40*timeout) + service := newService(t.Context(), 2, 40*timeout) - _, err := service.ListRefs(repositoryUrl, username, accessToken, false, false) + _, err := service.ListRefs(t.Context(), repositoryUrl, username, accessToken, false, false) require.NoError(t, err) _, err = service.ListFiles( + t.Context(), repositoryUrl, "refs/heads/main", username, @@ -345,15 +351,15 @@ func TestService_HardRefresh_ListRefs_GitHub(t *testing.T) { accessToken := getRequiredValue(t, "GITHUB_PAT") username := getRequiredValue(t, "GITHUB_USERNAME") - service := newService(context.TODO(), 2, 0) + service := newService(t.Context(), 2, 0) repositoryUrl := privateGitRepoURL - refs, err := service.ListRefs(repositoryUrl, username, accessToken, false, false) + refs, err := service.ListRefs(t.Context(), repositoryUrl, username, accessToken, false, false) require.NoError(t, err) assert.GreaterOrEqual(t, len(refs), 1) assert.Equal(t, 1, service.repoRefCache.Len()) - _, err = service.ListRefs(repositoryUrl, username, "fake-token", false, false) + _, err = service.ListRefs(t.Context(), repositoryUrl, username, "fake-token", false, false) require.Error(t, err) assert.Equal(t, 1, service.repoRefCache.Len()) } @@ -363,15 +369,16 @@ func TestService_HardRefresh_ListRefs_And_RemoveAllCaches_GitHub(t *testing.T) { accessToken := getRequiredValue(t, "GITHUB_PAT") username := getRequiredValue(t, "GITHUB_USERNAME") - service := newService(context.TODO(), 2, 0) + service := newService(t.Context(), 2, 0) repositoryUrl := privateGitRepoURL - refs, err := service.ListRefs(repositoryUrl, username, accessToken, false, false) + refs, err := service.ListRefs(t.Context(), repositoryUrl, username, accessToken, false, false) require.NoError(t, err) assert.GreaterOrEqual(t, len(refs), 1) assert.Equal(t, 1, service.repoRefCache.Len()) files, err := service.ListFiles( + t.Context(), repositoryUrl, "refs/heads/main", username, @@ -386,6 +393,7 @@ func TestService_HardRefresh_ListRefs_And_RemoveAllCaches_GitHub(t *testing.T) { assert.Equal(t, 1, service.repoFileCache.Len()) files, err = service.ListFiles( + t.Context(), repositoryUrl, "refs/heads/test", username, @@ -399,11 +407,11 @@ func TestService_HardRefresh_ListRefs_And_RemoveAllCaches_GitHub(t *testing.T) { assert.GreaterOrEqual(t, len(files), 1) assert.Equal(t, 2, service.repoFileCache.Len()) - _, err = service.ListRefs(repositoryUrl, username, "fake-token", false, false) + _, err = service.ListRefs(t.Context(), repositoryUrl, username, "fake-token", false, false) require.Error(t, err) assert.Equal(t, 1, service.repoRefCache.Len()) - _, err = service.ListRefs(repositoryUrl, username, "fake-token", true, false) + _, err = service.ListRefs(t.Context(), repositoryUrl, username, "fake-token", true, false) require.Error(t, err) assert.Equal(t, 1, service.repoRefCache.Len()) // The relevant file caches should be removed too @@ -413,11 +421,12 @@ func TestService_HardRefresh_ListRefs_And_RemoveAllCaches_GitHub(t *testing.T) { func TestService_HardRefresh_ListFiles_GitHub(t *testing.T) { ensureIntegrationTest(t) - service := newService(context.TODO(), 2, 0) + service := newService(t.Context(), 2, 0) accessToken := getRequiredValue(t, "GITHUB_PAT") username := getRequiredValue(t, "GITHUB_USERNAME") repositoryUrl := privateGitRepoURL files, err := service.ListFiles( + t.Context(), repositoryUrl, "refs/heads/main", username, @@ -432,6 +441,7 @@ func TestService_HardRefresh_ListFiles_GitHub(t *testing.T) { assert.Equal(t, 1, service.repoFileCache.Len()) _, err = service.ListFiles( + t.Context(), repositoryUrl, "refs/heads/main", username, @@ -448,7 +458,7 @@ func TestService_HardRefresh_ListFiles_GitHub(t *testing.T) { func TestService_CloneRepository_TokenAuth(t *testing.T) { ensureIntegrationTest(t) - service := newService(context.TODO(), 2, 0) + service := newService(t.Context(), 2, 0) var requests []*http.Request testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { requests = append(requests, r) @@ -459,6 +469,7 @@ func TestService_CloneRepository_TokenAuth(t *testing.T) { // Since we aren't hitting a real git server we ignore the error _ = service.CloneRepository( + t.Context(), "test_dir", repositoryUrl, "refs/heads/main", diff --git a/api/git/git_test.go b/api/git/git_test.go index f2e08b332..529be48e6 100644 --- a/api/git/git_test.go +++ b/api/git/git_test.go @@ -1,7 +1,6 @@ package git import ( - "context" "os" "path/filepath" "testing" @@ -77,7 +76,7 @@ func Test_ClonePublicRepository_Shallow(t *testing.T) { dir := t.TempDir() t.Logf("Cloning into %s", dir) - err := service.CloneRepository(dir, repositoryURL, referenceName, "", "", false) + err := service.CloneRepository(t.Context(), dir, repositoryURL, referenceName, "", "", false) require.NoError(t, err) assert.Equal(t, 1, getCommitHistoryLength(t, dir), "cloned repo has incorrect depth") } @@ -89,7 +88,7 @@ func Test_ClonePublicRepository_NoGitDirectory(t *testing.T) { dir := t.TempDir() t.Logf("Cloning into %s", dir) - err := service.CloneRepository(dir, repositoryURL, referenceName, "", "", false) + err := service.CloneRepository(t.Context(), dir, repositoryURL, referenceName, "", "", false) require.NoError(t, err) assert.NoDirExists(t, filepath.Join(dir, ".git")) } @@ -100,7 +99,7 @@ func Test_latestCommitID(t *testing.T) { repositoryURL := setup(t) referenceName := "refs/heads/main" - id, err := service.LatestCommitID(repositoryURL, referenceName, "", "", false) + id, err := service.LatestCommitID(t.Context(), repositoryURL, referenceName, "", "", false) require.NoError(t, err) assert.Equal(t, "68dcaa7bd452494043c64252ab90db0f98ecf8d2", id) @@ -111,7 +110,7 @@ func Test_ListRefs(t *testing.T) { repositoryURL := setup(t) - fs, err := service.ListRefs(repositoryURL, "", "", false, false) + fs, err := service.ListRefs(t.Context(), repositoryURL, "", "", false, false) require.NoError(t, err) assert.Equal(t, []string{"refs/heads/main"}, fs) @@ -124,6 +123,7 @@ func Test_ListFiles(t *testing.T) { referenceName := "refs/heads/main" fs, err := service.ListFiles( + t.Context(), repositoryURL, referenceName, "", @@ -240,7 +240,7 @@ func Test_listRefsPrivateRepository(t *testing.T) { Password: tt.args.password, } } - refs, err := client.ListRefs(context.TODO(), tt.args.repositoryUrl, option) + refs, err := client.ListRefs(t.Context(), tt.args.repositoryUrl, option) if tt.expect.err == nil { require.NoError(t, err) if tt.expect.refsCount > 0 { @@ -358,7 +358,7 @@ func Test_listFilesPrivateRepository(t *testing.T) { Password: tt.args.password, } } - paths, err := client.ListFiles(context.TODO(), false, option) + paths, err := client.ListFiles(t.Context(), false, option) if tt.expect.shouldFail { require.Error(t, err) if tt.expect.err != nil { diff --git a/api/git/service.go b/api/git/service.go index 44da2375b..fdd1626eb 100644 --- a/api/git/service.go +++ b/api/git/service.go @@ -30,9 +30,8 @@ type RepoManager interface { // Service represents a service for managing Git. type Service struct { - shutdownCtx context.Context - azure RepoManager - git RepoManager + azure RepoManager + git RepoManager cacheEnabled bool // Cache the result of repository refs, key is repository URL @@ -48,7 +47,6 @@ func NewService(ctx context.Context) *Service { func newService(ctx context.Context, cacheSize int, cacheTTL time.Duration) *Service { service := &Service{ - shutdownCtx: ctx, azure: NewAzureClient(), git: NewGitClient(false), cacheEnabled: cacheSize > 0, @@ -70,20 +68,16 @@ func newService(ctx context.Context, cacheSize int, cacheTTL time.Duration) *Ser } if cacheTTL > 0 { - go service.startCacheCleanTimer(cacheTTL) + go schedule.RunOnInterval(ctx, cacheTTL, service.purgeCache, nil) } return service } -// startCacheCleanTimer starts a timer to purge caches periodically -func (service *Service) startCacheCleanTimer(d time.Duration) { - schedule.RunOnInterval(service.shutdownCtx, d, service.purgeCache, nil) -} - // CloneRepository clones a git repository using the specified URL in the specified // destination folder. func (service *Service) CloneRepository( + ctx context.Context, destination, repositoryURL, referenceName, @@ -103,7 +97,7 @@ func (service *Service) CloneRepository( gitOptions.ReferenceName = plumbing.ReferenceName(referenceName) } - return service.repoManager(repositoryURL).Download(context.TODO(), destination, gitOptions) + return service.repoManager(repositoryURL).Download(ctx, destination, gitOptions) } func (service *Service) repoManager(repositoryURL string) RepoManager { @@ -118,6 +112,7 @@ func (service *Service) repoManager(repositoryURL string) RepoManager { // LatestCommitID returns SHA1 of the latest commit of the specified reference func (service *Service) LatestCommitID( + ctx context.Context, repositoryURL, referenceName, username, @@ -129,11 +124,12 @@ func (service *Service) LatestCommitID( InsecureSkipTLS: tlsSkipVerify, } - return service.repoManager(repositoryURL).LatestCommitID(context.TODO(), repositoryURL, referenceName, listOptions) + return service.repoManager(repositoryURL).LatestCommitID(ctx, repositoryURL, referenceName, listOptions) } // ListRefs will list target repository's references without cloning the repository func (service *Service) ListRefs( + ctx context.Context, repositoryURL, username, password string, @@ -166,7 +162,7 @@ func (service *Service) ListRefs( InsecureSkipTLS: tlsSkipVerify, } - refs, err := service.repoManager(repositoryURL).ListRefs(context.TODO(), repositoryURL, options) + refs, err := service.repoManager(repositoryURL).ListRefs(ctx, repositoryURL, options) if err != nil { return nil, err } @@ -183,6 +179,7 @@ var singleflightGroup = &singleflight.Group{} // ListFiles will list all the files of the target repository with specific extensions. // If extension is not provided, it will list all the files under the target repository func (service *Service) ListFiles( + ctx context.Context, repositoryURL, referenceName, username, @@ -203,6 +200,7 @@ func (service *Service) ListFiles( fs, err, _ := singleflightGroup.Do(repoKey, func() (any, error) { return service.listFiles( + ctx, repositoryURL, referenceName, username, @@ -217,6 +215,7 @@ func (service *Service) ListFiles( } func (service *Service) listFiles( + ctx context.Context, repositoryURL, referenceName, username, @@ -259,7 +258,7 @@ func (service *Service) listFiles( Tags: git.NoTags, } - files, err := service.repoManager(repositoryURL).ListFiles(context.TODO(), dirOnly, cloneOption) + files, err := service.repoManager(repositoryURL).ListFiles(ctx, dirOnly, cloneOption) if err != nil { return nil, err } diff --git a/api/git/update/update.go b/api/git/update/update.go index 243943c85..ec8125b09 100644 --- a/api/git/update/update.go +++ b/api/git/update/update.go @@ -1,6 +1,7 @@ package update import ( + "context" "strings" "github.com/pkg/errors" @@ -13,7 +14,7 @@ import ( ) // UpdateGitObject updates a git object based on its config -func UpdateGitObject(gitService portainer.GitService, objId string, gitConfig *gittypes.RepoConfig, enableVersionFolder bool, projectPath string) (bool, string, error) { +func UpdateGitObject(ctx context.Context, gitService portainer.GitService, objId string, gitConfig *gittypes.RepoConfig, enableVersionFolder bool, projectPath string) (bool, string, error) { if gitConfig == nil { return false, "", nil } @@ -30,6 +31,7 @@ func UpdateGitObject(gitService portainer.GitService, objId string, gitConfig *g } newHash, err := gitService.LatestCommitID( + ctx, gitConfig.URL, gitConfig.ReferenceName, username, @@ -71,7 +73,7 @@ func UpdateGitObject(gitService portainer.GitService, objId string, gitConfig *g } } - if err := cloneGitRepository(gitService, cloneParams); err != nil { + if err := cloneGitRepository(ctx, gitService, cloneParams); err != nil { return false, "", errors.WithMessagef(err, "failed to do a fresh clone of %v", objId) } @@ -99,24 +101,20 @@ type gitAuth struct { password string } -func cloneGitRepository(gitService portainer.GitService, cloneParams *cloneRepositoryParameters) error { +func cloneGitRepository(ctx context.Context, gitService portainer.GitService, cloneParams *cloneRepositoryParameters) error { + username, password := "", "" if cloneParams.auth != nil { - return gitService.CloneRepository( - cloneParams.toDir, - cloneParams.url, - cloneParams.ref, - cloneParams.auth.username, - cloneParams.auth.password, - cloneParams.tlsSkipVerify, - ) + username = cloneParams.auth.username + password = cloneParams.auth.password } return gitService.CloneRepository( + ctx, cloneParams.toDir, cloneParams.url, cloneParams.ref, - "", - "", + username, + password, cloneParams.tlsSkipVerify, ) } diff --git a/api/http/handler/backup/backup_test.go b/api/http/handler/backup/backup_test.go index ac462672e..d7ec45ffe 100644 --- a/api/http/handler/backup/backup_test.go +++ b/api/http/handler/backup/backup_test.go @@ -2,7 +2,6 @@ package backup import ( "bytes" - "context" "io" "net/http" "net/http/httptest" @@ -57,7 +56,7 @@ func Test_backupHandlerWithoutPassword_shouldCreateATarballArchive(t *testing.T) w := httptest.NewRecorder() gate := offlinegate.NewOfflineGate() - adminMonitor := adminmonitor.New(time.Hour, nil, context.Background()) + adminMonitor := adminmonitor.New(time.Hour, nil) handlerErr := NewHandler( testhelpers.NewTestRequestBouncer(), @@ -102,7 +101,7 @@ func Test_backupHandlerWithPassword_shouldCreateEncryptedATarballArchive(t *test w := httptest.NewRecorder() gate := offlinegate.NewOfflineGate() - adminMonitor := adminmonitor.New(time.Hour, nil, nil) + adminMonitor := adminmonitor.New(time.Hour, nil) handlerErr := NewHandler( testhelpers.NewTestRequestBouncer(), diff --git a/api/http/handler/backup/restore.go b/api/http/handler/backup/restore.go index bbb70f9b0..4938413ee 100644 --- a/api/http/handler/backup/restore.go +++ b/api/http/handler/backup/restore.go @@ -37,7 +37,6 @@ func (h *Handler) restore(w http.ResponseWriter, r *http.Request) *httperror.Han return httperror.BadRequest("Cannot restore already initialized instance", errors.New("system already initialized")) } h.adminMonitor.Stop() - defer h.adminMonitor.Start() var payload restorePayload err = decodeForm(r, &payload) diff --git a/api/http/handler/backup/restore_test.go b/api/http/handler/backup/restore_test.go index f176264d6..2c74a27ee 100644 --- a/api/http/handler/backup/restore_test.go +++ b/api/http/handler/backup/restore_test.go @@ -2,7 +2,6 @@ package backup import ( "bytes" - "context" "fmt" "io" "mime/multipart" @@ -54,7 +53,7 @@ func Test_restoreArchive_usingCombinationOfPasswords(t *testing.T) { testhelpers.WithUsers([]portainer.User{}), testhelpers.WithEdgeJobs([]portainer.EdgeJob{}), ) - adminMonitor := adminmonitor.New(time.Hour, datastore, context.Background()) + adminMonitor := adminmonitor.New(time.Hour, datastore) h := NewHandler( testhelpers.NewTestRequestBouncer(), @@ -86,7 +85,7 @@ func Test_restoreArchive_shouldFailIfSystemWasAlreadyInitialized(t *testing.T) { testhelpers.WithUsers([]portainer.User{admin}), testhelpers.WithEdgeJobs([]portainer.EdgeJob{}), ) - adminMonitor := adminmonitor.New(time.Hour, datastore, context.Background()) + adminMonitor := adminmonitor.New(time.Hour, datastore) h := NewHandler(testhelpers.NewTestRequestBouncer(), datastore, diff --git a/api/http/handler/customtemplates/customtemplate_create.go b/api/http/handler/customtemplates/customtemplate_create.go index 104ea90a4..88ad28b93 100644 --- a/api/http/handler/customtemplates/customtemplate_create.go +++ b/api/http/handler/customtemplates/customtemplate_create.go @@ -1,6 +1,7 @@ package customtemplates import ( + "context" "errors" "net/http" "os" @@ -306,7 +307,7 @@ func (handler *Handler) createCustomTemplateFromGitRepository(r *http.Request) ( } } - commitHash, err := stackutils.DownloadGitRepository(*gitConfig, handler.GitService, getProjectPath) + commitHash, err := stackutils.DownloadGitRepository(context.TODO(), *gitConfig, handler.GitService, getProjectPath) if err != nil { return nil, err } diff --git a/api/http/handler/customtemplates/customtemplate_git_fetch.go b/api/http/handler/customtemplates/customtemplate_git_fetch.go index 465bba177..ec7f25376 100644 --- a/api/http/handler/customtemplates/customtemplate_git_fetch.go +++ b/api/http/handler/customtemplates/customtemplate_git_fetch.go @@ -1,6 +1,7 @@ package customtemplates import ( + "context" "net/http" "os" "sync" @@ -67,7 +68,7 @@ func (handler *Handler) customTemplateGitFetch(w http.ResponseWriter, r *http.Re } }() - commitHash, err := stackutils.DownloadGitRepository(*customTemplate.GitConfig, handler.GitService, func() string { + commitHash, err := stackutils.DownloadGitRepository(context.TODO(), *customTemplate.GitConfig, handler.GitService, func() string { return customTemplate.ProjectPath }) if err != nil { diff --git a/api/http/handler/customtemplates/customtemplate_git_fetch_test.go b/api/http/handler/customtemplates/customtemplate_git_fetch_test.go index 94de7d63b..d138e4487 100644 --- a/api/http/handler/customtemplates/customtemplate_git_fetch_test.go +++ b/api/http/handler/customtemplates/customtemplate_git_fetch_test.go @@ -2,6 +2,7 @@ package customtemplates import ( "bytes" + "context" "errors" "io" "io/fs" @@ -41,6 +42,7 @@ type TestGitService struct { } func (g *TestGitService) CloneRepository( + _ context.Context, destination string, repositoryURL, referenceName string, @@ -54,6 +56,7 @@ func (g *TestGitService) CloneRepository( } func (g *TestGitService) LatestCommitID( + _ context.Context, repositoryURL, referenceName, username, @@ -77,6 +80,7 @@ type InvalidTestGitService struct { } func (g *InvalidTestGitService) CloneRepository( + _ context.Context, dest, repoUrl, refName, @@ -88,6 +92,7 @@ func (g *InvalidTestGitService) CloneRepository( } func (g *InvalidTestGitService) LatestCommitID( + _ context.Context, repositoryURL, referenceName, username, diff --git a/api/http/handler/customtemplates/customtemplate_update.go b/api/http/handler/customtemplates/customtemplate_update.go index b6794e3fa..794d06a6c 100644 --- a/api/http/handler/customtemplates/customtemplate_update.go +++ b/api/http/handler/customtemplates/customtemplate_update.go @@ -1,6 +1,7 @@ package customtemplates import ( + "context" "errors" "fmt" "net/http" @@ -191,7 +192,7 @@ func (handler *Handler) customTemplateUpdate(w http.ResponseWriter, r *http.Requ } } - cleanBackup, err := git.CloneWithBackup(handler.GitService, handler.FileService, git.CloneOptions{ + cleanBackup, err := git.CloneWithBackup(context.TODO(), handler.GitService, handler.FileService, git.CloneOptions{ ProjectPath: customTemplate.ProjectPath, URL: gitConfig.URL, ReferenceName: gitConfig.ReferenceName, @@ -206,6 +207,7 @@ func (handler *Handler) customTemplateUpdate(w http.ResponseWriter, r *http.Requ defer cleanBackup() commitHash, err := handler.GitService.LatestCommitID( + context.TODO(), gitConfig.URL, gitConfig.ReferenceName, repositoryUsername, diff --git a/api/http/handler/edgestacks/edgestack_create_git.go b/api/http/handler/edgestacks/edgestack_create_git.go index acfb24143..54ba2eeda 100644 --- a/api/http/handler/edgestacks/edgestack_create_git.go +++ b/api/http/handler/edgestacks/edgestack_create_git.go @@ -1,6 +1,7 @@ package edgestacks import ( + "context" "fmt" "net/http" @@ -135,11 +136,11 @@ func (handler *Handler) createEdgeStackFromGitRepository(r *http.Request, tx dat stack.CreatedBy = stackutils.SanitizeLabel(tokenData.Username) return handler.edgeStacksService.PersistEdgeStack(tx, stack, func(stackFolder string, relatedEndpointIds []portainer.EndpointID) (composePath string, manifestPath string, projectPath string, err error) { - return handler.storeManifestFromGitRepository(tx, stackFolder, relatedEndpointIds, payload.DeploymentType, tokenData.ID, repoConfig) + return handler.storeManifestFromGitRepository(context.TODO(), tx, stackFolder, relatedEndpointIds, payload.DeploymentType, tokenData.ID, repoConfig) }) } -func (handler *Handler) storeManifestFromGitRepository(tx dataservices.DataStoreTx, stackFolder string, relatedEndpointIds []portainer.EndpointID, deploymentType portainer.EdgeStackDeploymentType, currentUserID portainer.UserID, repositoryConfig gittypes.RepoConfig) (composePath, manifestPath, projectPath string, err error) { +func (handler *Handler) storeManifestFromGitRepository(ctx context.Context, tx dataservices.DataStoreTx, stackFolder string, relatedEndpointIds []portainer.EndpointID, deploymentType portainer.EdgeStackDeploymentType, currentUserID portainer.UserID, repositoryConfig gittypes.RepoConfig) (composePath, manifestPath, projectPath string, err error) { if hasWrongType, err := hasWrongEnvironmentType(tx.Endpoint(), relatedEndpointIds, deploymentType); err != nil { return "", "", "", fmt.Errorf("unable to check for existence of non fitting environments: %w", err) } else if hasWrongType { @@ -155,6 +156,7 @@ func (handler *Handler) storeManifestFromGitRepository(tx dataservices.DataStore } if err := handler.GitService.CloneRepository( + ctx, projectPath, repositoryConfig.URL, repositoryConfig.ReferenceName, diff --git a/api/http/handler/endpointedge/endpointedge_status_inspect_test.go b/api/http/handler/endpointedge/endpointedge_status_inspect_test.go index 18d0d3c76..1e94843f0 100644 --- a/api/http/handler/endpointedge/endpointedge_status_inspect_test.go +++ b/api/http/handler/endpointedge/endpointedge_status_inspect_test.go @@ -1,7 +1,6 @@ package endpointedge import ( - "context" "fmt" "net/http" "net/http/httptest" @@ -85,10 +84,6 @@ func mustSetupHandler(t *testing.T) *Handler { _, store := datastore.MustNewTestStore(t, true, true) - ctx := context.Background() - shutdownCtx, cancelFn := context.WithCancel(ctx) - t.Cleanup(cancelFn) - jwtService, err := jwt.NewService("1h", store) if err != nil { t.Fatalf("could not start a new JWT service: %s", err) @@ -110,10 +105,10 @@ func mustSetupHandler(t *testing.T) *Handler { security.NewRequestBouncer(t.Context(), store, jwtService, apiKeyService), store, fs, - chisel.NewService(store, shutdownCtx, nil), + chisel.NewService(store, t.Context(), nil), ) - handler.ReverseTunnelService = chisel.NewService(store, shutdownCtx, nil) + handler.ReverseTunnelService = chisel.NewService(store, t.Context(), nil) return handler } diff --git a/api/http/handler/endpoints/endpoint_list_test.go b/api/http/handler/endpoints/endpoint_list_test.go index 928b53045..7ecc68851 100644 --- a/api/http/handler/endpoints/endpoint_list_test.go +++ b/api/http/handler/endpoints/endpoint_list_test.go @@ -200,7 +200,7 @@ func setupEndpointListHandler(t *testing.T, endpoints []portainer.Endpoint) *Han handler := NewHandler(bouncer) handler.DataStore = store handler.ComposeStackManager = testhelpers.NewComposeStackManager() - handler.SnapshotService, _ = snapshot.NewService("1s", store, nil, nil, nil, nil) + handler.SnapshotService, _ = snapshot.NewService("1s", store, nil, nil, nil) return handler } diff --git a/api/http/handler/endpoints/endpoint_snapshots_test.go b/api/http/handler/endpoints/endpoint_snapshots_test.go index 6a95ad008..5e242f5d6 100644 --- a/api/http/handler/endpoints/endpoint_snapshots_test.go +++ b/api/http/handler/endpoints/endpoint_snapshots_test.go @@ -1,6 +1,7 @@ package endpoints import ( + "context" "errors" "io" "net/http" @@ -87,7 +88,7 @@ type mockSnapshotService struct { snapshotEndpointShouldSucceed atomic.Bool } -func (s *mockSnapshotService) Start() { +func (s *mockSnapshotService) Start(_ context.Context) { } func (s *mockSnapshotService) SetSnapshotInterval(snapshotInterval string) error { diff --git a/api/http/handler/gitops/git_repo_file_preview.go b/api/http/handler/gitops/git_repo_file_preview.go index 7ed8a9e94..24165334c 100644 --- a/api/http/handler/gitops/git_repo_file_preview.go +++ b/api/http/handler/gitops/git_repo_file_preview.go @@ -1,6 +1,7 @@ package gitops import ( + "context" "errors" "fmt" "net/http" @@ -70,6 +71,7 @@ func (handler *Handler) gitOperationRepoFilePreview(w http.ResponseWriter, r *ht } err = handler.gitService.CloneRepository( + context.TODO(), projectPath, payload.Repository, payload.Reference, diff --git a/api/http/handler/stacks/create_compose_stack.go b/api/http/handler/stacks/create_compose_stack.go index 4e1f4b904..de4aeecdc 100644 --- a/api/http/handler/stacks/create_compose_stack.go +++ b/api/http/handler/stacks/create_compose_stack.go @@ -1,6 +1,7 @@ package stacks import ( + "context" "fmt" "net/http" "strings" @@ -147,7 +148,7 @@ func (handler *Handler) createComposeStackFromFileContent(w http.ResponseWriter, handler.StackDeployer) stackBuilderDirector := stackbuilders.NewStackBuilderDirector(composeStackBuilder) - stack, httpErr := stackBuilderDirector.Build(&stackPayload, endpoint) + stack, httpErr := stackBuilderDirector.Build(context.TODO(), &stackPayload, endpoint) if httpErr != nil { return httpErr } @@ -305,7 +306,7 @@ func (handler *Handler) createComposeStackFromGitRepository(w http.ResponseWrite handler.StackDeployer) stackBuilderDirector := stackbuilders.NewStackBuilderDirector(composeStackBuilder) - stack, httpErr := stackBuilderDirector.Build(&stackPayload, endpoint) + stack, httpErr := stackBuilderDirector.Build(context.TODO(), &stackPayload, endpoint) if httpErr != nil { return httpErr } @@ -410,7 +411,7 @@ func (handler *Handler) createComposeStackFromFileUpload(w http.ResponseWriter, handler.StackDeployer) stackBuilderDirector := stackbuilders.NewStackBuilderDirector(composeStackBuilder) - stack, httpErr := stackBuilderDirector.Build(&stackPayload, endpoint) + stack, httpErr := stackBuilderDirector.Build(context.TODO(), &stackPayload, endpoint) if httpErr != nil { return httpErr } diff --git a/api/http/handler/stacks/create_kubernetes_stack.go b/api/http/handler/stacks/create_kubernetes_stack.go index a9d0845d4..4f598019d 100644 --- a/api/http/handler/stacks/create_kubernetes_stack.go +++ b/api/http/handler/stacks/create_kubernetes_stack.go @@ -1,6 +1,7 @@ package stacks import ( + "context" "fmt" "net/http" @@ -171,7 +172,7 @@ func (handler *Handler) createKubernetesStackFromFileContent(w http.ResponseWrit } stackBuilderDirector := stackbuilders.NewStackBuilderDirector(k8sStackBuilder) - if _, err := stackBuilderDirector.Build(&stackPayload, endpoint); err != nil { + if _, err := stackBuilderDirector.Build(context.TODO(), &stackPayload, endpoint); err != nil { return err } @@ -244,7 +245,7 @@ func (handler *Handler) createKubernetesStackFromGitRepository(w http.ResponseWr user) stackBuilderDirector := stackbuilders.NewStackBuilderDirector(k8sStackBuilder) - if _, err := stackBuilderDirector.Build(&stackPayload, endpoint); err != nil { + if _, err := stackBuilderDirector.Build(context.TODO(), &stackPayload, endpoint); err != nil { return err } @@ -290,7 +291,7 @@ func (handler *Handler) createKubernetesStackFromManifestURL(w http.ResponseWrit user) stackBuilderDirector := stackbuilders.NewStackBuilderDirector(k8sStackBuilder) - if _, err := stackBuilderDirector.Build(&stackPayload, endpoint); err != nil { + if _, err := stackBuilderDirector.Build(context.TODO(), &stackPayload, endpoint); err != nil { return err } @@ -299,7 +300,7 @@ func (handler *Handler) createKubernetesStackFromManifestURL(w http.ResponseWrit }) } -func (handler *Handler) deployKubernetesStack(userID portainer.UserID, endpoint *portainer.Endpoint, stack *portainer.Stack, appLabels k.KubeAppLabels) (string, error) { +func (handler *Handler) deployKubernetesStack(ctx context.Context, userID portainer.UserID, endpoint *portainer.Endpoint, stack *portainer.Stack, appLabels k.KubeAppLabels) (string, error) { handler.stackCreationMutex.Lock() defer handler.stackCreationMutex.Unlock() @@ -311,7 +312,7 @@ func (handler *Handler) deployKubernetesStack(userID portainer.UserID, endpoint return "", errors.Wrap(err, "failed to create temp kub deployment files") } - if err := k8sDeploymentConfig.Deploy(); err != nil { + if err := k8sDeploymentConfig.Deploy(ctx); err != nil { return "", err } diff --git a/api/http/handler/stacks/create_swarm_stack.go b/api/http/handler/stacks/create_swarm_stack.go index 6a12d1670..b176605bc 100644 --- a/api/http/handler/stacks/create_swarm_stack.go +++ b/api/http/handler/stacks/create_swarm_stack.go @@ -1,6 +1,7 @@ package stacks import ( + "context" "fmt" "net/http" @@ -97,7 +98,7 @@ func (handler *Handler) createSwarmStackFromFileContent(w http.ResponseWriter, r handler.StackDeployer) stackBuilderDirector := stackbuilders.NewStackBuilderDirector(swarmStackBuilder) - stack, httpErr := stackBuilderDirector.Build(&stackPayload, endpoint) + stack, httpErr := stackBuilderDirector.Build(context.TODO(), &stackPayload, endpoint) if httpErr != nil { return httpErr } @@ -241,7 +242,7 @@ func (handler *Handler) createSwarmStackFromGitRepository(w http.ResponseWriter, handler.StackDeployer) stackBuilderDirector := stackbuilders.NewStackBuilderDirector(swarmStackBuilder) - stack, httpErr := stackBuilderDirector.Build(&stackPayload, endpoint) + stack, httpErr := stackBuilderDirector.Build(context.TODO(), &stackPayload, endpoint) if httpErr != nil { return httpErr } @@ -342,7 +343,7 @@ func (handler *Handler) createSwarmStackFromFileUpload(w http.ResponseWriter, r handler.StackDeployer) stackBuilderDirector := stackbuilders.NewStackBuilderDirector(swarmStackBuilder) - stack, httpErr := stackBuilderDirector.Build(&stackPayload, endpoint) + stack, httpErr := stackBuilderDirector.Build(context.TODO(), &stackPayload, endpoint) if httpErr != nil { return httpErr } diff --git a/api/http/handler/stacks/stack_delete.go b/api/http/handler/stacks/stack_delete.go index ea156a1b0..797b4002a 100644 --- a/api/http/handler/stacks/stack_delete.go +++ b/api/http/handler/stacks/stack_delete.go @@ -117,7 +117,7 @@ func (handler *Handler) stackDelete(w http.ResponseWriter, r *http.Request) *htt deployments.StopAutoupdate(stack.ID, stack.AutoUpdate.JobID, handler.Scheduler) } - if err := handler.deleteStack(securityContext.UserID, stack, endpoint); err != nil { + if err := handler.deleteStack(context.TODO(), securityContext.UserID, stack, endpoint); err != nil { return httperror.InternalServerError(err.Error(), err) } @@ -172,38 +172,38 @@ func (handler *Handler) deleteExternalStack(r *http.Request, w http.ResponseWrit Type: portainer.DockerSwarmStack, } - if err := handler.deleteStack(securityContext.UserID, stack, endpoint); err != nil { + if err := handler.deleteStack(context.TODO(), securityContext.UserID, stack, endpoint); err != nil { return httperror.InternalServerError("Unable to delete stack", err) } return response.Empty(w) } -func (handler *Handler) deleteStack(userID portainer.UserID, stack *portainer.Stack, endpoint *portainer.Endpoint) error { +func (handler *Handler) deleteStack(ctx context.Context, userID portainer.UserID, stack *portainer.Stack, endpoint *portainer.Endpoint) error { if stack.Type == portainer.DockerSwarmStack { stack.Name = handler.SwarmStackManager.NormalizeStackName(stack.Name) if stackutils.IsRelativePathStack(stack) { - return handler.StackDeployer.UndeployRemoteSwarmStack(stack, endpoint) + return handler.StackDeployer.UndeployRemoteSwarmStack(ctx, stack, endpoint) } - return handler.SwarmStackManager.Remove(stack, endpoint) + return handler.SwarmStackManager.Remove(ctx, stack, endpoint) } if stack.Type == portainer.DockerComposeStack { stack.Name = handler.ComposeStackManager.NormalizeStackName(stack.Name) if stackutils.IsRelativePathStack(stack) { - return handler.StackDeployer.UndeployRemoteComposeStack(stack, endpoint) + return handler.StackDeployer.UndeployRemoteComposeStack(ctx, stack, endpoint) } - return handler.ComposeStackManager.Down(context.TODO(), stack, endpoint) + return handler.ComposeStackManager.Down(ctx, stack, endpoint) } if stack.Type == portainer.KubernetesStack { manifestFiles := stackutils.GetStackFilePaths(stack, true) - out, err := handler.KubernetesDeployer.Remove(userID, endpoint, manifestFiles, stack.Namespace) + out, err := handler.KubernetesDeployer.Remove(ctx, userID, endpoint, manifestFiles, stack.Namespace) if err != nil { for _, manifest := range manifestFiles { if exists, fileExistsErr := filesystem.FileExists(manifest); fileExistsErr != nil || !exists { @@ -325,7 +325,7 @@ func (handler *Handler) stackDeleteKubernetesByName(w http.ResponseWriter, r *ht deployments.StopAutoupdate(stack.ID, stack.AutoUpdate.JobID, handler.Scheduler) } - err = handler.deleteStack(securityContext.UserID, &stack, endpoint) + err = handler.deleteStack(context.TODO(), securityContext.UserID, &stack, endpoint) if err != nil { log.Err(err).Msgf("Unable to delete Kubernetes stack `%d`", stack.ID) errs = errors.Join(errs, err) diff --git a/api/http/handler/stacks/stack_migrate.go b/api/http/handler/stacks/stack_migrate.go index 0907d9635..633e41b75 100644 --- a/api/http/handler/stacks/stack_migrate.go +++ b/api/http/handler/stacks/stack_migrate.go @@ -1,6 +1,7 @@ package stacks import ( + "context" "errors" "fmt" "net/http" @@ -154,7 +155,7 @@ func (handler *Handler) stackMigrate(w http.ResponseWriter, r *http.Request) *ht newName := stack.Name stack.Name = oldName - if err := handler.deleteStack(securityContext.UserID, stack, endpoint); err != nil { + if err := handler.deleteStack(context.TODO(), securityContext.UserID, stack, endpoint); err != nil { return httperror.InternalServerError(err.Error(), err) } @@ -207,7 +208,7 @@ func (handler *Handler) migrateComposeStack(r *http.Request, stack *portainer.St } // Deploy the stack - if err := composeDeploymentConfig.Deploy(); err != nil { + if err := composeDeploymentConfig.Deploy(context.TODO()); err != nil { return httperror.InternalServerError(err.Error(), err) } @@ -234,7 +235,7 @@ func (handler *Handler) migrateSwarmStack(r *http.Request, stack *portainer.Stac } // Deploy the stack - if err := swarmDeploymentConfig.Deploy(); err != nil { + if err := swarmDeploymentConfig.Deploy(context.TODO()); err != nil { return httperror.InternalServerError(err.Error(), err) } diff --git a/api/http/handler/stacks/stack_start.go b/api/http/handler/stacks/stack_start.go index 5c4804ded..55dd667d5 100644 --- a/api/http/handler/stacks/stack_start.go +++ b/api/http/handler/stacks/stack_start.go @@ -109,7 +109,7 @@ func (handler *Handler) stackStart(w http.ResponseWriter, r *http.Request) *http if stack.AutoUpdate != nil && stack.AutoUpdate.Interval != "" { deployments.StopAutoupdate(stack.ID, stack.AutoUpdate.JobID, handler.Scheduler) - jobID, e := deployments.StartAutoupdate(stack.ID, stack.AutoUpdate.Interval, handler.Scheduler, handler.StackDeployer, handler.DataStore, handler.GitService) + jobID, e := deployments.StartAutoupdate(context.TODO(), stack.ID, stack.AutoUpdate.Interval, handler.Scheduler, handler.StackDeployer, handler.DataStore, handler.GitService) if e != nil { return e } @@ -117,7 +117,7 @@ func (handler *Handler) stackStart(w http.ResponseWriter, r *http.Request) *http stack.AutoUpdate.JobID = jobID } - err = handler.startStack(stack, endpoint, securityContext) + err = handler.startStack(context.TODO(), stack, endpoint, securityContext) if err != nil { return httperror.InternalServerError("Unable to start stack", err) } @@ -137,6 +137,7 @@ func (handler *Handler) stackStart(w http.ResponseWriter, r *http.Request) *http } func (handler *Handler) startStack( + ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, securityContext *security.RestrictedRequestContext, @@ -158,7 +159,7 @@ func (handler *Handler) startStack( stack.Name = handler.ComposeStackManager.NormalizeStackName(stack.Name) if stackutils.IsRelativePathStack(stack) { - return handler.StackDeployer.StartRemoteComposeStack(stack, endpoint, filteredRegistries) + return handler.StackDeployer.StartRemoteComposeStack(ctx, stack, endpoint, filteredRegistries) } options := portainer.ComposeUpOptions{ @@ -167,15 +168,15 @@ func (handler *Handler) startStack( }, } - return handler.ComposeStackManager.Up(context.TODO(), stack, endpoint, options) + return handler.ComposeStackManager.Up(ctx, stack, endpoint, options) case portainer.DockerSwarmStack: stack.Name = handler.SwarmStackManager.NormalizeStackName(stack.Name) if stackutils.IsRelativePathStack(stack) { - return handler.StackDeployer.StartRemoteSwarmStack(stack, endpoint, filteredRegistries) + return handler.StackDeployer.StartRemoteSwarmStack(ctx, stack, endpoint, filteredRegistries) } - return handler.StackDeployer.DeploySwarmStack(stack, endpoint, filteredRegistries, true, true) + return handler.StackDeployer.DeploySwarmStack(ctx, stack, endpoint, filteredRegistries, true, true) } return nil diff --git a/api/http/handler/stacks/stack_stop.go b/api/http/handler/stacks/stack_stop.go index f6c22a830..11e069b58 100644 --- a/api/http/handler/stacks/stack_stop.go +++ b/api/http/handler/stacks/stack_stop.go @@ -101,7 +101,7 @@ func (handler *Handler) stackStop(w http.ResponseWriter, r *http.Request) *httpe stack.AutoUpdate.JobID = "" } - err = handler.stopStack(stack, endpoint) + err = handler.stopStack(context.TODO(), stack, endpoint) if err != nil { return httperror.InternalServerError("Unable to stop stack", err) } @@ -120,24 +120,24 @@ func (handler *Handler) stackStop(w http.ResponseWriter, r *http.Request) *httpe return response.JSON(w, stack) } -func (handler *Handler) stopStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error { +func (handler *Handler) stopStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error { switch stack.Type { case portainer.DockerComposeStack: stack.Name = handler.ComposeStackManager.NormalizeStackName(stack.Name) if stackutils.IsRelativePathStack(stack) { - return handler.StackDeployer.StopRemoteComposeStack(stack, endpoint) + return handler.StackDeployer.StopRemoteComposeStack(ctx, stack, endpoint) } - return handler.ComposeStackManager.Down(context.TODO(), stack, endpoint) + return handler.ComposeStackManager.Down(ctx, stack, endpoint) case portainer.DockerSwarmStack: stack.Name = handler.SwarmStackManager.NormalizeStackName(stack.Name) if stackutils.IsRelativePathStack(stack) { - return handler.StackDeployer.StopRemoteSwarmStack(stack, endpoint) + return handler.StackDeployer.StopRemoteSwarmStack(ctx, stack, endpoint) } - return handler.SwarmStackManager.Remove(stack, endpoint) + return handler.SwarmStackManager.Remove(ctx, stack, endpoint) } return nil diff --git a/api/http/handler/stacks/stack_update.go b/api/http/handler/stacks/stack_update.go index 24b07692f..a8c407148 100644 --- a/api/http/handler/stacks/stack_update.go +++ b/api/http/handler/stacks/stack_update.go @@ -1,6 +1,7 @@ package stacks import ( + "context" "net/http" "strconv" "time" @@ -264,7 +265,7 @@ func (handler *Handler) updateComposeStack(tx dataservices.DataStoreTx, r *http. } // Deploy the stack - if err := composeDeploymentConfig.Deploy(); err != nil { + if err := composeDeploymentConfig.Deploy(context.TODO()); err != nil { if rollbackErr := handler.FileService.RollbackStackFile(stackFolder, stack.EntryPoint); rollbackErr != nil { log.Warn().Err(rollbackErr).Msg("rollback stack file error") } @@ -340,7 +341,7 @@ func (handler *Handler) updateSwarmStack(tx dataservices.DataStoreTx, r *http.Re } // Deploy the stack - if err := swarmDeploymentConfig.Deploy(); err != nil { + if err := swarmDeploymentConfig.Deploy(context.TODO()); err != nil { if rollbackErr := handler.FileService.RollbackStackFile(stackFolder, stack.EntryPoint); rollbackErr != nil { log.Warn().Err(rollbackErr).Msg("rollback stack file error") } diff --git a/api/http/handler/stacks/stack_update_git.go b/api/http/handler/stacks/stack_update_git.go index fb4f62be2..df7c9920c 100644 --- a/api/http/handler/stacks/stack_update_git.go +++ b/api/http/handler/stacks/stack_update_git.go @@ -2,6 +2,7 @@ package stacks import ( "cmp" + "context" "net/http" "time" @@ -190,6 +191,7 @@ func (handler *Handler) stackUpdateGit(w http.ResponseWriter, r *http.Request) * } if _, err := handler.GitService.LatestCommitID( + context.TODO(), stack.GitConfig.URL, stack.GitConfig.ReferenceName, stack.GitConfig.Authentication.Username, @@ -203,7 +205,7 @@ func (handler *Handler) stackUpdateGit(w http.ResponseWriter, r *http.Request) * } if payload.AutoUpdate != nil && payload.AutoUpdate.Interval != "" { - if jobID, err := deployments.StartAutoupdate(stack.ID, stack.AutoUpdate.Interval, handler.Scheduler, handler.StackDeployer, handler.DataStore, handler.GitService); err != nil { + if jobID, err := deployments.StartAutoupdate(context.TODO(), stack.ID, stack.AutoUpdate.Interval, handler.Scheduler, handler.StackDeployer, handler.DataStore, handler.GitService); err != nil { return err } else { stack.AutoUpdate.JobID = jobID diff --git a/api/http/handler/stacks/stack_update_git_redeploy.go b/api/http/handler/stacks/stack_update_git_redeploy.go index 635fb96e3..32e608e30 100644 --- a/api/http/handler/stacks/stack_update_git_redeploy.go +++ b/api/http/handler/stacks/stack_update_git_redeploy.go @@ -1,6 +1,7 @@ package stacks import ( + "context" "net/http" "time" @@ -162,7 +163,7 @@ func (handler *Handler) stackGitRedeploy(w http.ResponseWriter, r *http.Request) TLSSkipVerify: stack.GitConfig.TLSSkipVerify, } - clean, err := git.CloneWithBackup(handler.GitService, handler.FileService, cloneOptions) + clean, err := git.CloneWithBackup(context.TODO(), handler.GitService, handler.FileService, cloneOptions) if err != nil { return httperror.InternalServerError("Unable to clone git repository directory", err) } @@ -173,7 +174,7 @@ func (handler *Handler) stackGitRedeploy(w http.ResponseWriter, r *http.Request) return err } - newHash, err := handler.GitService.LatestCommitID(stack.GitConfig.URL, stack.GitConfig.ReferenceName, repositoryUsername, repositoryPassword, stack.GitConfig.TLSSkipVerify) + newHash, err := handler.GitService.LatestCommitID(context.TODO(), stack.GitConfig.URL, stack.GitConfig.ReferenceName, repositoryUsername, repositoryPassword, stack.GitConfig.TLSSkipVerify) if err != nil { return httperror.InternalServerError("Unable get latest commit id", errors.WithMessagef(err, "failed to fetch latest commit id of the stack %v", stack.ID)) } @@ -270,7 +271,7 @@ func (handler *Handler) deployStack(r *http.Request, stack *portainer.Stack, pul return httperror.InternalServerError("Unsupported stack", errors.Errorf("unsupported stack type: %v", stack.Type)) } - if err := deploymentConfiger.Deploy(); err != nil { + if err := deploymentConfiger.Deploy(context.TODO()); err != nil { return httperror.InternalServerError(err.Error(), err) } diff --git a/api/http/handler/stacks/update_kubernetes_stack.go b/api/http/handler/stacks/update_kubernetes_stack.go index 66ada0fe7..adf3d27bc 100644 --- a/api/http/handler/stacks/update_kubernetes_stack.go +++ b/api/http/handler/stacks/update_kubernetes_stack.go @@ -1,6 +1,7 @@ package stacks import ( + "context" "net/http" "os" "strconv" @@ -81,6 +82,7 @@ func (handler *Handler) updateKubernetesStack(r *http.Request, stack *portainer. } if _, err := handler.GitService.LatestCommitID( + context.TODO(), stack.GitConfig.URL, stack.GitConfig.ReferenceName, stack.GitConfig.Authentication.Username, @@ -92,7 +94,7 @@ func (handler *Handler) updateKubernetesStack(r *http.Request, stack *portainer. } if payload.AutoUpdate != nil && payload.AutoUpdate.Interval != "" { - jobID, e := deployments.StartAutoupdate(stack.ID, stack.AutoUpdate.Interval, handler.Scheduler, handler.StackDeployer, handler.DataStore, handler.GitService) + jobID, e := deployments.StartAutoupdate(context.TODO(), stack.ID, stack.AutoUpdate.Interval, handler.Scheduler, handler.StackDeployer, handler.DataStore, handler.GitService) if e != nil { return e } @@ -145,7 +147,7 @@ func (handler *Handler) updateKubernetesStack(r *http.Request, stack *portainer. // so if the deployment failed, the original file won't be over-written stack.ProjectPath = tempFileDir - if _, err := handler.deployKubernetesStack(tokenData.ID, endpoint, stack, k.KubeAppLabels{ + if _, err := handler.deployKubernetesStack(context.TODO(), tokenData.ID, endpoint, stack, k.KubeAppLabels{ StackID: int(stack.ID), StackName: stack.Name, Owner: stack.CreatedBy, diff --git a/api/http/handler/stacks/webhook_invoke.go b/api/http/handler/stacks/webhook_invoke.go index d2f9b022f..1433957bf 100644 --- a/api/http/handler/stacks/webhook_invoke.go +++ b/api/http/handler/stacks/webhook_invoke.go @@ -1,6 +1,7 @@ package stacks import ( + "context" "errors" "net/http" @@ -38,7 +39,7 @@ func (handler *Handler) webhookInvoke(w http.ResponseWriter, r *http.Request) *h return httperror.NewError(statusCode, "Unable to find the stack by webhook ID", err) } - if err = deployments.RedeployWhenChanged(stack.ID, handler.StackDeployer, handler.DataStore, handler.GitService); err != nil { + if err = deployments.RedeployWhenChanged(context.TODO(), stack.ID, handler.StackDeployer, handler.DataStore, handler.GitService); err != nil { var StackAuthorMissingErr *deployments.StackAuthorMissingErr if errors.As(err, &StackAuthorMissingErr) { return httperror.Conflict("Autoupdate for the stack isn't available", err) diff --git a/api/http/handler/templates/template_file.go b/api/http/handler/templates/template_file.go index bb1e10aee..ab7c4a379 100644 --- a/api/http/handler/templates/template_file.go +++ b/api/http/handler/templates/template_file.go @@ -1,6 +1,7 @@ package templates import ( + "context" "net/http" "slices" @@ -72,6 +73,7 @@ func (handler *Handler) templateFile(w http.ResponseWriter, r *http.Request) *ht defer handler.cleanUp(projectPath) if err := handler.GitService.CloneRepository( + context.TODO(), projectPath, template.Repository.URL, "", diff --git a/api/http/proxy/factory/docker/transport.go b/api/http/proxy/factory/docker/transport.go index b9b54774c..ab89b35b4 100644 --- a/api/http/proxy/factory/docker/transport.go +++ b/api/http/proxy/factory/docker/transport.go @@ -444,6 +444,7 @@ func (transport *Transport) updateDefaultGitBranch(request *http.Request) error repositoryURL := remote[:len(remote)-4] latestCommitID, err := transport.gitService.LatestCommitID( + request.Context(), repositoryURL, "", "", diff --git a/api/http/proxy/factory/reverse_proxy_test.go b/api/http/proxy/factory/reverse_proxy_test.go index 6207e0910..3636e1332 100644 --- a/api/http/proxy/factory/reverse_proxy_test.go +++ b/api/http/proxy/factory/reverse_proxy_test.go @@ -1,7 +1,6 @@ package factory import ( - "context" "net/http" "net/http/httputil" "net/url" @@ -147,8 +146,8 @@ func Test_createRewriteFn(t *testing.T) { t.Run(tc.name, func(t *testing.T) { rewriteFn := createRewriteFn(tc.target) proxyRequest := httputil.ProxyRequest{ - In: tc.req.Clone(context.Background()), - Out: tc.req.Clone(context.Background()), + In: tc.req.Clone(t.Context()), + Out: tc.req.Clone(t.Context()), } rewriteFn(&proxyRequest) diff --git a/api/http/server.go b/api/http/server.go index 07bb30175..1f07c0702 100644 --- a/api/http/server.go +++ b/api/http/server.go @@ -107,7 +107,6 @@ type Server struct { KubernetesDeployer portainer.KubernetesDeployer HelmPackageManager libhelmtypes.HelmPackageManager Scheduler *scheduler.Scheduler - ShutdownCtx context.Context ShutdownTrigger context.CancelFunc StackDeployer deployments.StackDeployer UpgradeService upgrade.Service @@ -119,10 +118,10 @@ type Server struct { } // Start starts the HTTP server -func (server *Server) Start() error { +func (server *Server) Start(ctx context.Context) error { kubernetesTokenCacheManager := server.KubernetesTokenCacheManager - requestBouncer := security.NewRequestBouncer(server.ShutdownCtx, server.DataStore, server.JWTService, server.APIKeyService) + requestBouncer := security.NewRequestBouncer(ctx, server.DataStore, server.JWTService, server.APIKeyService) if !server.CSP { requestBouncer.DisableCSP() } @@ -141,8 +140,8 @@ func (server *Server) Start() error { authHandler.KubernetesTokenCacheManager = kubernetesTokenCacheManager authHandler.OAuthService = server.OAuthService - adminMonitor := adminmonitor.New(5*time.Minute, server.DataStore, server.ShutdownCtx) - adminMonitor.Start() + adminMonitor := adminmonitor.New(5*time.Minute, server.DataStore) + adminMonitor.Start(ctx) var backupHandler = backup.NewHandler( requestBouncer, @@ -219,7 +218,7 @@ func (server *Server) Start() error { ldapHandler.LDAPService = server.LDAPService motdSvc := motdservice.NewService(portainer.MessageOfTheDayURL) - motdSvc.Start(server.ShutdownCtx) + motdSvc.Start(ctx) var motdHandler = motd.NewHandler(requestBouncer, motdSvc) @@ -359,7 +358,7 @@ func (server *Server) Start() error { ErrorLog: errorLogger, } - go shutdown(server.ShutdownCtx, httpServer) + go shutdown(ctx, httpServer) err := httpServer.ListenAndServe() if err != nil && err != http.ErrServerClosed { @@ -381,7 +380,7 @@ func (server *Server) Start() error { return server.SSLService.GetRawCertificate(), nil } - go shutdown(server.ShutdownCtx, httpsServer) + go shutdown(ctx, httpsServer) go snapshot.NewBackgroundSnapshotter(server.DataStore, server.ReverseTunnelService) return httpsServer.ListenAndServeTLS("", "") diff --git a/api/internal/snapshot/snapshot.go b/api/internal/snapshot/snapshot.go index 9bad54bed..686ed5835 100644 --- a/api/internal/snapshot/snapshot.go +++ b/api/internal/snapshot/snapshot.go @@ -24,7 +24,6 @@ type Service struct { snapshotIntervalInSeconds float64 dockerSnapshotter portainer.DockerSnapshotter kubernetesSnapshotter portainer.KubernetesSnapshotter - shutdownCtx context.Context pendingActionsService *pendingactions.PendingActionsService } @@ -34,7 +33,6 @@ func NewService( dataStore dataservices.DataStore, dockerSnapshotter portainer.DockerSnapshotter, kubernetesSnapshotter portainer.KubernetesSnapshotter, - shutdownCtx context.Context, pendingActionsService *pendingactions.PendingActionsService, ) (*Service, error) { interval, err := parseSnapshotFrequency(snapshotIntervalFromFlag, dataStore) @@ -48,7 +46,6 @@ func NewService( snapshotIntervalInSeconds: interval, dockerSnapshotter: dockerSnapshotter, kubernetesSnapshotter: kubernetesSnapshotter, - shutdownCtx: shutdownCtx, pendingActionsService: pendingActionsService, }, nil } @@ -106,8 +103,8 @@ func parseSnapshotFrequency(snapshotInterval string, dataStore dataservices.Data } // Start will start a background routine to execute periodic snapshots of environments(endpoints) -func (service *Service) Start() { - go service.startSnapshotLoop() +func (service *Service) Start(ctx context.Context) { + go service.startSnapshotLoop(ctx) } // SetSnapshotInterval sets the snapshot interval and resets the service @@ -212,7 +209,7 @@ func validateContainerEngineCompatibility(endpoint *portainer.Endpoint, dockerSn return nil } -func (service *Service) startSnapshotLoop() { +func (service *Service) startSnapshotLoop(ctx context.Context) { ticker := time.NewTicker(time.Duration(service.snapshotIntervalInSeconds) * time.Second) err := service.snapshotEndpoints() @@ -227,7 +224,7 @@ func (service *Service) startSnapshotLoop() { if err != nil { log.Error().Err(err).Msg("background schedule error (environment snapshot)") } - case <-service.shutdownCtx.Done(): + case <-ctx.Done(): log.Debug().Msg("shutting down snapshotting") ticker.Stop() diff --git a/api/internal/testhelpers/git_service.go b/api/internal/testhelpers/git_service.go index 1d193de60..1024a4b8d 100644 --- a/api/internal/testhelpers/git_service.go +++ b/api/internal/testhelpers/git_service.go @@ -1,6 +1,8 @@ package testhelpers import ( + "context" + portainer "github.com/portainer/portainer/api" ) @@ -18,6 +20,7 @@ func NewGitService(cloneErr error, id string) portainer.GitService { } func (g *gitService) CloneRepository( + _ context.Context, destination, repositoryURL, referenceName, @@ -29,6 +32,7 @@ func (g *gitService) CloneRepository( } func (g *gitService) LatestCommitID( + _ context.Context, repositoryURL, referenceName, username, @@ -39,6 +43,7 @@ func (g *gitService) LatestCommitID( } func (g *gitService) ListRefs( + _ context.Context, repositoryURL, username, password string, @@ -49,6 +54,7 @@ func (g *gitService) ListRefs( } func (g *gitService) ListFiles( + _ context.Context, repositoryURL, referenceName, username, diff --git a/api/internal/testhelpers/stack_deployer.go b/api/internal/testhelpers/stack_deployer.go index 788d105a4..c8effe596 100644 --- a/api/internal/testhelpers/stack_deployer.go +++ b/api/internal/testhelpers/stack_deployer.go @@ -1,6 +1,10 @@ package testhelpers -import portainer "github.com/portainer/portainer/api" +import ( + "context" + + portainer "github.com/portainer/portainer/api" +) type TestStackDeployer struct { DeployComposeCallCount int @@ -12,50 +16,50 @@ func NewTestStackDeployer() *TestStackDeployer { return &TestStackDeployer{} } -func (d *TestStackDeployer) DeployComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, forcePullImage, forceRecreate bool) error { +func (d *TestStackDeployer) DeployComposeStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, forcePullImage, forceRecreate bool) error { d.DeployComposeCallCount++ d.LastPrune = prune return nil } -func (d *TestStackDeployer) DeploySwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, pullImage bool) error { +func (d *TestStackDeployer) DeploySwarmStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, pullImage bool) error { d.DeploySwarmCallCount++ d.LastPrune = prune return nil } -func (d *TestStackDeployer) DeployKubernetesStack(stack *portainer.Stack, endpoint *portainer.Endpoint, user *portainer.User) error { +func (d *TestStackDeployer) DeployKubernetesStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, user *portainer.User) error { return nil } -func (d *TestStackDeployer) DeployRemoteComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, forcePullImage, forceRecreate bool) error { +func (d *TestStackDeployer) DeployRemoteComposeStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, forcePullImage, forceRecreate bool) error { return nil } -func (d *TestStackDeployer) UndeployRemoteComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error { +func (d *TestStackDeployer) UndeployRemoteComposeStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error { return nil } -func (d *TestStackDeployer) StartRemoteComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry) error { +func (d *TestStackDeployer) StartRemoteComposeStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry) error { return nil } -func (d *TestStackDeployer) StopRemoteComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error { +func (d *TestStackDeployer) StopRemoteComposeStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error { return nil } -func (d *TestStackDeployer) DeployRemoteSwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, pullImage bool) error { +func (d *TestStackDeployer) DeployRemoteSwarmStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, pullImage bool) error { return nil } -func (d *TestStackDeployer) UndeployRemoteSwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error { +func (d *TestStackDeployer) UndeployRemoteSwarmStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error { return nil } -func (d *TestStackDeployer) StartRemoteSwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry) error { +func (d *TestStackDeployer) StartRemoteSwarmStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry) error { return nil } -func (d *TestStackDeployer) StopRemoteSwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error { +func (d *TestStackDeployer) StopRemoteSwarmStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error { return nil } diff --git a/api/kubernetes/cli/access_test.go b/api/kubernetes/cli/access_test.go index 06c865905..25d19da55 100644 --- a/api/kubernetes/cli/access_test.go +++ b/api/kubernetes/cli/access_test.go @@ -1,7 +1,6 @@ package cli import ( - "context" "testing" portainer "github.com/portainer/portainer/api" @@ -52,10 +51,10 @@ func Test_NamespaceAccessPoliciesDeleteNamespace_updatesPortainerConfig_whenConf }, } - _, err := k.cli.CoreV1().ConfigMaps(portainerNamespace).Create(context.Background(), config, metav1.CreateOptions{}) + _, err := k.cli.CoreV1().ConfigMaps(portainerNamespace).Create(t.Context(), config, metav1.CreateOptions{}) require.NoError(t, err, "failed to create a portainer config") defer func() { - err := k.cli.CoreV1().ConfigMaps(portainerNamespace).Delete(context.Background(), portainerConfigMapName, metav1.DeleteOptions{}) + err := k.cli.CoreV1().ConfigMaps(portainerNamespace).Delete(t.Context(), portainerConfigMapName, metav1.DeleteOptions{}) require.NoError(t, err) }() diff --git a/api/kubernetes/cli/applications_test.go b/api/kubernetes/cli/applications_test.go index c2025e5bd..a931c7f16 100644 --- a/api/kubernetes/cli/applications_test.go +++ b/api/kubernetes/cli/applications_test.go @@ -1,7 +1,6 @@ package cli import ( - "context" "testing" models "github.com/portainer/portainer/api/http/models/kubernetes" @@ -242,70 +241,70 @@ func TestGetApplications(t *testing.T) { // Create resources in the test namespace // 1. Deployment with pods deployWithPods := createTestDeployment("deploy-with-pods", namespace, 2) - _, err := fakeClient.AppsV1().Deployments(namespace).Create(context.TODO(), deployWithPods, metav1.CreateOptions{}) + _, err := fakeClient.AppsV1().Deployments(namespace).Create(t.Context(), deployWithPods, metav1.CreateOptions{}) require.NoError(t, err) replicaSet := createTestReplicaSet("rs-deploy-with-pods", namespace, "deploy-with-pods") - _, err = fakeClient.AppsV1().ReplicaSets(namespace).Create(context.TODO(), replicaSet, metav1.CreateOptions{}) + _, err = fakeClient.AppsV1().ReplicaSets(namespace).Create(t.Context(), replicaSet, metav1.CreateOptions{}) require.NoError(t, err) pod1 := createTestPod("pod1-deploy", namespace, "ReplicaSet", "rs-deploy-with-pods", true) - _, err = fakeClient.CoreV1().Pods(namespace).Create(context.TODO(), pod1, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Pods(namespace).Create(t.Context(), pod1, metav1.CreateOptions{}) require.NoError(t, err) pod2 := createTestPod("pod2-deploy", namespace, "ReplicaSet", "rs-deploy-with-pods", true) - _, err = fakeClient.CoreV1().Pods(namespace).Create(context.TODO(), pod2, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Pods(namespace).Create(t.Context(), pod2, metav1.CreateOptions{}) require.NoError(t, err) // 2. Deployment without pods (scaled to 0) deployNoPods := createTestDeployment("deploy-no-pods", namespace, 0) - _, err = fakeClient.AppsV1().Deployments(namespace).Create(context.TODO(), deployNoPods, metav1.CreateOptions{}) + _, err = fakeClient.AppsV1().Deployments(namespace).Create(t.Context(), deployNoPods, metav1.CreateOptions{}) require.NoError(t, err) // 3. StatefulSet with pods stsWithPods := createTestStatefulSet("sts-with-pods", namespace, 1) - _, err = fakeClient.AppsV1().StatefulSets(namespace).Create(context.TODO(), stsWithPods, metav1.CreateOptions{}) + _, err = fakeClient.AppsV1().StatefulSets(namespace).Create(t.Context(), stsWithPods, metav1.CreateOptions{}) require.NoError(t, err) pod3 := createTestPod("pod1-sts", namespace, "StatefulSet", "sts-with-pods", true) - _, err = fakeClient.CoreV1().Pods(namespace).Create(context.TODO(), pod3, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Pods(namespace).Create(t.Context(), pod3, metav1.CreateOptions{}) require.NoError(t, err) // 4. StatefulSet without pods stsNoPods := createTestStatefulSet("sts-no-pods", namespace, 0) - _, err = fakeClient.AppsV1().StatefulSets(namespace).Create(context.TODO(), stsNoPods, metav1.CreateOptions{}) + _, err = fakeClient.AppsV1().StatefulSets(namespace).Create(t.Context(), stsNoPods, metav1.CreateOptions{}) require.NoError(t, err) // 5. DaemonSet with pods dsWithPods := createTestDaemonSet("ds-with-pods", namespace) - _, err = fakeClient.AppsV1().DaemonSets(namespace).Create(context.TODO(), dsWithPods, metav1.CreateOptions{}) + _, err = fakeClient.AppsV1().DaemonSets(namespace).Create(t.Context(), dsWithPods, metav1.CreateOptions{}) require.NoError(t, err) pod4 := createTestPod("pod1-ds", namespace, "DaemonSet", "ds-with-pods", true) - _, err = fakeClient.CoreV1().Pods(namespace).Create(context.TODO(), pod4, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Pods(namespace).Create(t.Context(), pod4, metav1.CreateOptions{}) require.NoError(t, err) pod5 := createTestPod("pod2-ds", namespace, "DaemonSet", "ds-with-pods", true) - _, err = fakeClient.CoreV1().Pods(namespace).Create(context.TODO(), pod5, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Pods(namespace).Create(t.Context(), pod5, metav1.CreateOptions{}) require.NoError(t, err) // 6. Naked Pod (no owner reference) nakedPod := createTestPod("naked-pod", namespace, "", "", true) - _, err = fakeClient.CoreV1().Pods(namespace).Create(context.TODO(), nakedPod, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Pods(namespace).Create(t.Context(), nakedPod, metav1.CreateOptions{}) require.NoError(t, err) // 7. Resources in another namespace deployOtherNs := createTestDeployment("deploy-other-ns", defaultNamespace, 1) - _, err = fakeClient.AppsV1().Deployments(defaultNamespace).Create(context.TODO(), deployOtherNs, metav1.CreateOptions{}) + _, err = fakeClient.AppsV1().Deployments(defaultNamespace).Create(t.Context(), deployOtherNs, metav1.CreateOptions{}) require.NoError(t, err) podOtherNs := createTestPod("pod-other-ns", defaultNamespace, "Deployment", "deploy-other-ns", true) - _, err = fakeClient.CoreV1().Pods(defaultNamespace).Create(context.TODO(), podOtherNs, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Pods(defaultNamespace).Create(t.Context(), podOtherNs, metav1.CreateOptions{}) require.NoError(t, err) // 8. Add a service (dependency) service := createTestService("svc-deploy", namespace, map[string]string{"app": "deploy-with-pods"}) - _, err = fakeClient.CoreV1().Services(namespace).Create(context.TODO(), service, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Services(namespace).Create(t.Context(), service, metav1.CreateOptions{}) require.NoError(t, err) // Create the KubeClient with admin privileges @@ -362,25 +361,25 @@ func TestGetApplications(t *testing.T) { // Create resources in the allowed namespace sts1 := createTestStatefulSet("sts-allowed", namespace1, 1) - _, err := fakeClient.AppsV1().StatefulSets(namespace1).Create(context.TODO(), sts1, metav1.CreateOptions{}) + _, err := fakeClient.AppsV1().StatefulSets(namespace1).Create(t.Context(), sts1, metav1.CreateOptions{}) require.NoError(t, err) pod1 := createTestPod("pod-allowed", namespace1, "StatefulSet", "sts-allowed", true) - _, err = fakeClient.CoreV1().Pods(namespace1).Create(context.TODO(), pod1, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Pods(namespace1).Create(t.Context(), pod1, metav1.CreateOptions{}) require.NoError(t, err) // Add a StatefulSet without pods in the allowed namespace stsNoPods := createTestStatefulSet("sts-no-pods-allowed", namespace1, 0) - _, err = fakeClient.AppsV1().StatefulSets(namespace1).Create(context.TODO(), stsNoPods, metav1.CreateOptions{}) + _, err = fakeClient.AppsV1().StatefulSets(namespace1).Create(t.Context(), stsNoPods, metav1.CreateOptions{}) require.NoError(t, err) // Create resources in the restricted namespace sts2 := createTestStatefulSet("sts-restricted", namespace2, 1) - _, err = fakeClient.AppsV1().StatefulSets(namespace2).Create(context.TODO(), sts2, metav1.CreateOptions{}) + _, err = fakeClient.AppsV1().StatefulSets(namespace2).Create(t.Context(), sts2, metav1.CreateOptions{}) require.NoError(t, err) pod2 := createTestPod("pod-restricted", namespace2, "StatefulSet", "sts-restricted", true) - _, err = fakeClient.CoreV1().Pods(namespace2).Create(context.TODO(), pod2, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Pods(namespace2).Create(t.Context(), pod2, metav1.CreateOptions{}) require.NoError(t, err) // Create the KubeClient with non-admin privileges (only allowed namespace1) @@ -424,23 +423,23 @@ func TestGetApplications(t *testing.T) { // Create a deployment with pods on specific node deploy := createTestDeployment("node-deploy", namespace, 2) - _, err := fakeClient.AppsV1().Deployments(namespace).Create(context.TODO(), deploy, metav1.CreateOptions{}) + _, err := fakeClient.AppsV1().Deployments(namespace).Create(t.Context(), deploy, metav1.CreateOptions{}) require.NoError(t, err) // Create ReplicaSet for the deployment rs := createTestReplicaSet("rs-node-deploy", namespace, "node-deploy") - _, err = fakeClient.AppsV1().ReplicaSets(namespace).Create(context.TODO(), rs, metav1.CreateOptions{}) + _, err = fakeClient.AppsV1().ReplicaSets(namespace).Create(t.Context(), rs, metav1.CreateOptions{}) require.NoError(t, err) // Create 2 pods, one on the specified node, one on a different node pod1 := createTestPod("pod-on-node", namespace, "ReplicaSet", "rs-node-deploy", true) pod1.Spec.NodeName = nodeName - _, err = fakeClient.CoreV1().Pods(namespace).Create(context.TODO(), pod1, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Pods(namespace).Create(t.Context(), pod1, metav1.CreateOptions{}) require.NoError(t, err) pod2 := createTestPod("pod-other-node", namespace, "ReplicaSet", "rs-node-deploy", true) pod2.Spec.NodeName = "worker-node-2" - _, err = fakeClient.CoreV1().Pods(namespace).Create(context.TODO(), pod2, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Pods(namespace).Create(t.Context(), pod2, metav1.CreateOptions{}) require.NoError(t, err) // Create the KubeClient diff --git a/api/kubernetes/cli/cronjob_test.go b/api/kubernetes/cli/cronjob_test.go index d5d0858a7..9c2077764 100644 --- a/api/kubernetes/cli/cronjob_test.go +++ b/api/kubernetes/cli/cronjob_test.go @@ -1,7 +1,6 @@ package cli import ( - "context" "testing" models "github.com/portainer/portainer/api/http/models/kubernetes" @@ -49,7 +48,7 @@ func (kcl *KubeClient) TestFetchCronJobs(t *testing.T) { kcl.cli = kfake.NewSimpleClientset() kcl.instanceID = "test" - _, err := kcl.cli.BatchV1().CronJobs("default").Create(context.Background(), &batchv1.CronJob{ + _, err := kcl.cli.BatchV1().CronJobs("default").Create(t.Context(), &batchv1.CronJob{ ObjectMeta: metav1.ObjectMeta{Name: "test-cronjob"}, }, metav1.CreateOptions{}) if err != nil { diff --git a/api/kubernetes/cli/event_test.go b/api/kubernetes/cli/event_test.go index 3e2e7261c..5c0a4091c 100644 --- a/api/kubernetes/cli/event_test.go +++ b/api/kubernetes/cli/event_test.go @@ -1,7 +1,6 @@ package cli import ( - "context" "testing" "github.com/stretchr/testify/assert" @@ -31,7 +30,7 @@ func TestGetEvents(t *testing.T) { Message: "This event has a very serious warning", } - _, err := kcl.cli.CoreV1().Events("default").Create(context.TODO(), &event, metav1.CreateOptions{}) + _, err := kcl.cli.CoreV1().Events("default").Create(t.Context(), &event, metav1.CreateOptions{}) require.NoError(t, err, "Failed to create Event") events, err := kcl.GetEvents("default", "resourceId") @@ -60,7 +59,7 @@ func TestGetEvents(t *testing.T) { Message: "This event has a very serious warning", } - _, err := kcl.cli.CoreV1().Events("nonAdmin").Create(context.TODO(), &event, metav1.CreateOptions{}) + _, err := kcl.cli.CoreV1().Events("nonAdmin").Create(t.Context(), &event, metav1.CreateOptions{}) require.NoError(t, err, "Failed to create Event") events, err := kcl.GetEvents("nonAdmin", "resourceId") @@ -90,7 +89,7 @@ func TestGetEvents(t *testing.T) { Message: "This event has a very serious warning", } - _, err := kcl.cli.CoreV1().Events("admin").Create(context.TODO(), &event, metav1.CreateOptions{}) + _, err := kcl.cli.CoreV1().Events("admin").Create(t.Context(), &event, metav1.CreateOptions{}) require.NoError(t, err, "Failed to create Event") events, err := kcl.GetEvents("admin", "resourceId") diff --git a/api/kubernetes/cli/job_test.go b/api/kubernetes/cli/job_test.go index 425bbf6a7..d26d0094d 100644 --- a/api/kubernetes/cli/job_test.go +++ b/api/kubernetes/cli/job_test.go @@ -1,7 +1,6 @@ package cli import ( - "context" "testing" "time" @@ -49,7 +48,7 @@ func (kcl *KubeClient) TestFetchJobs(t *testing.T) { kcl.cli = kfake.NewSimpleClientset() kcl.instanceID = "test" - _, err := kcl.cli.BatchV1().Jobs("default").Create(context.Background(), &batchv1.Job{ + _, err := kcl.cli.BatchV1().Jobs("default").Create(t.Context(), &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{Name: "test-job"}, }, metav1.CreateOptions{}) if err != nil { diff --git a/api/kubernetes/cli/namespace_test.go b/api/kubernetes/cli/namespace_test.go index 1fc327d79..bd74f713d 100644 --- a/api/kubernetes/cli/namespace_test.go +++ b/api/kubernetes/cli/namespace_test.go @@ -1,7 +1,6 @@ package cli import ( - "context" "strconv" "testing" @@ -25,7 +24,7 @@ func Test_ToggleSystemState(t *testing.T) { err := kcl.ToggleSystemState(nsName, true) require.NoError(t, err) - ns, err := kcl.cli.CoreV1().Namespaces().Get(context.Background(), nsName, metav1.GetOptions{}) + ns, err := kcl.cli.CoreV1().Namespaces().Get(t.Context(), nsName, metav1.GetOptions{}) require.NoError(t, err) _, exists := ns.Labels[systemNamespaceLabel] @@ -64,7 +63,7 @@ func Test_ToggleSystemState(t *testing.T) { err := kcl.ToggleSystemState(nsName, test.isSystem) require.NoError(t, err) - ns, err := kcl.cli.CoreV1().Namespaces().Get(context.Background(), nsName, metav1.GetOptions{}) + ns, err := kcl.cli.CoreV1().Namespaces().Get(t.Context(), nsName, metav1.GetOptions{}) require.NoError(t, err) assert.Equal(t, test.isSystem, isSystemNamespace(ns)) @@ -93,7 +92,7 @@ func Test_ToggleSystemState(t *testing.T) { err := kcl.ToggleSystemState(nsName, true) require.NoError(t, err) - ns, err := kcl.cli.CoreV1().Namespaces().Get(context.Background(), nsName, metav1.GetOptions{}) + ns, err := kcl.cli.CoreV1().Namespaces().Get(t.Context(), nsName, metav1.GetOptions{}) require.NoError(t, err) labelValue, exists := ns.Labels[systemNamespaceLabel] @@ -113,7 +112,7 @@ func Test_ToggleSystemState(t *testing.T) { err := kcl.ToggleSystemState(nsName, false) require.NoError(t, err) - ns, err := kcl.cli.CoreV1().Namespaces().Get(context.Background(), nsName, metav1.GetOptions{}) + ns, err := kcl.cli.CoreV1().Namespaces().Get(t.Context(), nsName, metav1.GetOptions{}) require.NoError(t, err) labelValue, exists := ns.Labels[systemNamespaceLabel] @@ -135,7 +134,7 @@ func Test_ToggleSystemState(t *testing.T) { err := kcl.ToggleSystemState(nsName, false) require.NoError(t, err) - ns, err := kcl.cli.CoreV1().Namespaces().Get(context.Background(), nsName, metav1.GetOptions{}) + ns, err := kcl.cli.CoreV1().Namespaces().Get(t.Context(), nsName, metav1.GetOptions{}) require.NoError(t, err) labelValue, exists := ns.Labels[systemNamespaceLabel] @@ -168,7 +167,7 @@ func Test_ToggleSystemState(t *testing.T) { err := kcl.ToggleSystemState(nsName, true) require.NoError(t, err) - ns, err := kcl.cli.CoreV1().Namespaces().Get(context.Background(), nsName, metav1.GetOptions{}) + ns, err := kcl.cli.CoreV1().Namespaces().Get(t.Context(), nsName, metav1.GetOptions{}) require.NoError(t, err) labelValue, exists := ns.Labels[systemNamespaceLabel] diff --git a/api/kubernetes/cli/pod_test.go b/api/kubernetes/cli/pod_test.go index c3c8455de..694ff1dbe 100644 --- a/api/kubernetes/cli/pod_test.go +++ b/api/kubernetes/cli/pod_test.go @@ -29,7 +29,7 @@ func Test_waitForPodStatus(t *testing.T) { }, } - ctx, cancel := context.WithCancel(context.TODO()) + ctx, cancel := context.WithCancel(t.Context()) cancel() err := k.waitForPodStatus(ctx, v1.PodRunning, podSpec) if !errors.Is(err, context.Canceled) { @@ -52,16 +52,16 @@ func Test_waitForPodStatus(t *testing.T) { }, } - pod, err := k.cli.CoreV1().Pods(defaultNamespace).Create(context.Background(), podSpec, metav1.CreateOptions{}) + pod, err := k.cli.CoreV1().Pods(defaultNamespace).Create(t.Context(), podSpec, metav1.CreateOptions{}) if err != nil { t.Errorf("failed to create pod; err=%s", err) } defer func() { - err := k.cli.CoreV1().Pods(defaultNamespace).Delete(context.Background(), pod.Name, metav1.DeleteOptions{}) + err := k.cli.CoreV1().Pods(defaultNamespace).Delete(t.Context(), pod.Name, metav1.DeleteOptions{}) require.NoError(t, err) }() - ctx, cancelFunc := context.WithTimeout(context.TODO(), 0*time.Second) + ctx, cancelFunc := context.WithTimeout(t.Context(), 0*time.Second) defer cancelFunc() err = k.waitForPodStatus(ctx, v1.PodRunning, podSpec) diff --git a/api/kubernetes/cli/service_account_test.go b/api/kubernetes/cli/service_account_test.go index a3c23478b..a7a0625dd 100644 --- a/api/kubernetes/cli/service_account_test.go +++ b/api/kubernetes/cli/service_account_test.go @@ -1,7 +1,6 @@ package cli import ( - "context" "testing" portainer "github.com/portainer/portainer/api" @@ -42,12 +41,12 @@ func Test_GetServiceAccount(t *testing.T) { Name: tokenData.Username, }, } - _, err := k.cli.CoreV1().ServiceAccounts(portainerNamespace).Create(context.Background(), serviceAccount, metav1.CreateOptions{}) + _, err := k.cli.CoreV1().ServiceAccounts(portainerNamespace).Create(t.Context(), serviceAccount, metav1.CreateOptions{}) if err != nil { t.Errorf("failed to create service acount; err=%s", err) } defer func() { - err := k.cli.CoreV1().ServiceAccounts(portainerNamespace).Delete(context.Background(), serviceAccount.Name, metav1.DeleteOptions{}) + err := k.cli.CoreV1().ServiceAccounts(portainerNamespace).Delete(t.Context(), serviceAccount.Name, metav1.DeleteOptions{}) require.NoError(t, err) }() @@ -78,12 +77,12 @@ func Test_GetServiceAccount(t *testing.T) { Name: serviceAccountName, }, } - _, err := k.cli.CoreV1().ServiceAccounts(portainerNamespace).Create(context.Background(), serviceAccount, metav1.CreateOptions{}) + _, err := k.cli.CoreV1().ServiceAccounts(portainerNamespace).Create(t.Context(), serviceAccount, metav1.CreateOptions{}) if err != nil { t.Errorf("failed to create service acount; err=%s", err) } defer func() { - err := k.cli.CoreV1().ServiceAccounts(portainerNamespace).Delete(context.Background(), serviceAccount.Name, metav1.DeleteOptions{}) + err := k.cli.CoreV1().ServiceAccounts(portainerNamespace).Delete(t.Context(), serviceAccount.Name, metav1.DeleteOptions{}) require.NoError(t, err) }() @@ -192,7 +191,7 @@ func TestAddImagePullSecretToServiceAccount(t *testing.T) { kcl := newKCL(defaultSA("ns-a")) require.NoError(t, kcl.AddImagePullSecretToServiceAccount("ns-a", "default", "registry-1")) - sa, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(context.Background(), "default", metav1.GetOptions{}) + sa, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(t.Context(), "default", metav1.GetOptions{}) require.NoError(t, err) require.Len(t, sa.ImagePullSecrets, 1) assert.Equal(t, "registry-1", sa.ImagePullSecrets[0].Name) @@ -202,7 +201,7 @@ func TestAddImagePullSecretToServiceAccount(t *testing.T) { kcl := newKCL(defaultSA("ns-a", "registry-1")) require.NoError(t, kcl.AddImagePullSecretToServiceAccount("ns-a", "default", "registry-1")) - sa, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(context.Background(), "default", metav1.GetOptions{}) + sa, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(t.Context(), "default", metav1.GetOptions{}) require.NoError(t, err) assert.Len(t, sa.ImagePullSecrets, 1) }) @@ -211,7 +210,7 @@ func TestAddImagePullSecretToServiceAccount(t *testing.T) { kcl := newKCL(defaultSA("ns-a", "other-1", "other-2")) require.NoError(t, kcl.AddImagePullSecretToServiceAccount("ns-a", "default", "registry-3")) - sa, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(context.Background(), "default", metav1.GetOptions{}) + sa, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(t.Context(), "default", metav1.GetOptions{}) require.NoError(t, err) require.Len(t, sa.ImagePullSecrets, 3) assert.Equal(t, "other-1", sa.ImagePullSecrets[0].Name) @@ -232,7 +231,7 @@ func TestAddImagePullSecretToServiceAccount(t *testing.T) { kcl := newKCL(sa) require.NoError(t, kcl.AddImagePullSecretToServiceAccount("ns-a", "custom", "registry-1")) - got, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(context.Background(), "custom", metav1.GetOptions{}) + got, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(t.Context(), "custom", metav1.GetOptions{}) require.NoError(t, err) require.Len(t, got.ImagePullSecrets, 1) assert.Equal(t, "registry-1", got.ImagePullSecrets[0].Name) @@ -259,7 +258,7 @@ func TestRemoveImagePullSecretFromServiceAccount(t *testing.T) { kcl := newKCL(defaultSA("ns-a", "registry-1")) require.NoError(t, kcl.RemoveImagePullSecretFromServiceAccount("ns-a", "default", "registry-1")) - sa, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(context.Background(), "default", metav1.GetOptions{}) + sa, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(t.Context(), "default", metav1.GetOptions{}) require.NoError(t, err) assert.Empty(t, sa.ImagePullSecrets) }) @@ -268,7 +267,7 @@ func TestRemoveImagePullSecretFromServiceAccount(t *testing.T) { kcl := newKCL(defaultSA("ns-a", "other-secret")) require.NoError(t, kcl.RemoveImagePullSecretFromServiceAccount("ns-a", "default", "registry-1")) - sa, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(context.Background(), "default", metav1.GetOptions{}) + sa, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(t.Context(), "default", metav1.GetOptions{}) require.NoError(t, err) assert.Len(t, sa.ImagePullSecrets, 1) }) @@ -277,7 +276,7 @@ func TestRemoveImagePullSecretFromServiceAccount(t *testing.T) { kcl := newKCL(defaultSA("ns-a", "other-1", "registry-2", "other-3")) require.NoError(t, kcl.RemoveImagePullSecretFromServiceAccount("ns-a", "default", "registry-2")) - sa, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(context.Background(), "default", metav1.GetOptions{}) + sa, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(t.Context(), "default", metav1.GetOptions{}) require.NoError(t, err) require.Len(t, sa.ImagePullSecrets, 2) assert.Equal(t, "other-1", sa.ImagePullSecrets[0].Name) @@ -301,7 +300,7 @@ func TestRemoveImagePullSecretFromServiceAccount(t *testing.T) { kcl := newKCL(defaultSA("ns-a", "registry-1", "registry-1")) require.NoError(t, kcl.RemoveImagePullSecretFromServiceAccount("ns-a", "default", "registry-1")) - sa, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(context.Background(), "default", metav1.GetOptions{}) + sa, err := kcl.cli.CoreV1().ServiceAccounts("ns-a").Get(t.Context(), "default", metav1.GetOptions{}) require.NoError(t, err) assert.Empty(t, sa.ImagePullSecrets) }) @@ -353,7 +352,7 @@ func TestGetServiceAccount_CreatesAndFetches(t *testing.T) { sa := &v1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{Name: "fresh-sa", Namespace: "staging"}, } - _, err := kcl.cli.CoreV1().ServiceAccounts("staging").Create(context.Background(), sa, metav1.CreateOptions{}) + _, err := kcl.cli.CoreV1().ServiceAccounts("staging").Create(t.Context(), sa, metav1.CreateOptions{}) require.NoError(t, err) result, err := kcl.GetServiceAccount("staging", "fresh-sa") diff --git a/api/portainer.go b/api/portainer.go index 8c59130c6..66e598a43 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -1648,6 +1648,7 @@ type ( // GitService represents a service for managing Git GitService interface { CloneRepository( + ctx context.Context, destination string, repositoryURL, referenceName, @@ -1656,6 +1657,7 @@ type ( tlsSkipVerify bool, ) error LatestCommitID( + ctx context.Context, repositoryURL, referenceName, username, @@ -1663,6 +1665,7 @@ type ( tlsSkipVerify bool, ) (string, error) ListRefs( + ctx context.Context, repositoryURL, username, password string, @@ -1670,6 +1673,7 @@ type ( tlsSkipVerify bool, ) ([]string, error) ListFiles( + ctx context.Context, repositoryURL, referenceName, username, @@ -1826,8 +1830,8 @@ type ( // KubernetesDeployer represents a service to deploy a manifest inside a Kubernetes environment(endpoint) KubernetesDeployer interface { - Deploy(userID UserID, endpoint *Endpoint, manifestFiles []string, namespace string) (string, error) - Remove(userID UserID, endpoint *Endpoint, manifestFiles []string, namespace string) (string, error) + Deploy(ctx context.Context, userID UserID, endpoint *Endpoint, manifestFiles []string, namespace string) (string, error) + Remove(ctx context.Context, userID UserID, endpoint *Endpoint, manifestFiles []string, namespace string) (string, error) } // KubernetesSnapshotter represents a service used to create Kubernetes environment(endpoint) snapshots @@ -1863,12 +1867,12 @@ type ( // Server defines the interface to serve the API Server interface { - Start() error + Start(ctx context.Context) error } // SnapshotService represents a service for managing environment(endpoint) snapshots SnapshotService interface { - Start() + Start(ctx context.Context) SetSnapshotInterval(snapshotInterval string) error SnapshotEndpoint(endpoint *Endpoint) error FillSnapshotData(endpoint *Endpoint, includeRaw bool) error @@ -1876,10 +1880,10 @@ type ( // SwarmStackManager represents a service to manage Swarm stacks SwarmStackManager interface { - Login(registries []Registry, endpoint *Endpoint) error - Logout(endpoint *Endpoint) error - Deploy(stack *Stack, prune bool, pullImage bool, endpoint *Endpoint) error - Remove(stack *Stack, endpoint *Endpoint) error + Login(ctx context.Context, registries []Registry, endpoint *Endpoint) error + Logout(ctx context.Context, endpoint *Endpoint) error + Deploy(ctx context.Context, stack *Stack, prune bool, pullImage bool, endpoint *Endpoint) error + Remove(ctx context.Context, stack *Stack, endpoint *Endpoint) error NormalizeStackName(name string) string } ) diff --git a/api/scheduler/scheduler_test.go b/api/scheduler/scheduler_test.go index 5626f2013..8ca8b86d3 100644 --- a/api/scheduler/scheduler_test.go +++ b/api/scheduler/scheduler_test.go @@ -151,7 +151,7 @@ func Test_CanTerminateAllJobs_ByCancellingParentContext(t *testing.T) { func Test_StartJobEvery_Concurrently(t *testing.T) { synctest.Test(t, func(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 2*jobInterval) + ctx, cancel := context.WithTimeout(t.Context(), 2*jobInterval) s := NewScheduler(ctx) f := func() error { diff --git a/api/stacks/deployments/autoupdate.go b/api/stacks/deployments/autoupdate.go index 3fdcbd0c0..b201691d2 100644 --- a/api/stacks/deployments/autoupdate.go +++ b/api/stacks/deployments/autoupdate.go @@ -1,6 +1,7 @@ package deployments import ( + "context" "time" portainer "github.com/portainer/portainer/api" @@ -11,14 +12,14 @@ import ( "github.com/rs/zerolog/log" ) -func StartAutoupdate(stackID portainer.StackID, interval string, scheduler *scheduler.Scheduler, stackDeployer StackDeployer, datastore dataservices.DataStore, gitService portainer.GitService) (jobID string, e *httperror.HandlerError) { +func StartAutoupdate(ctx context.Context, stackID portainer.StackID, interval string, scheduler *scheduler.Scheduler, stackDeployer StackDeployer, datastore dataservices.DataStore, gitService portainer.GitService) (jobID string, e *httperror.HandlerError) { d, err := time.ParseDuration(interval) if err != nil { return "", httperror.BadRequest("Unable to parse stack's auto update interval", err) } jobID = scheduler.StartJobEvery(d, func() error { - return RedeployWhenChanged(stackID, stackDeployer, datastore, gitService) + return RedeployWhenChanged(ctx, stackID, stackDeployer, datastore, gitService) }) return jobID, nil diff --git a/api/stacks/deployments/deploy.go b/api/stacks/deployments/deploy.go index d63c3362d..79d3f079b 100644 --- a/api/stacks/deployments/deploy.go +++ b/api/stacks/deployments/deploy.go @@ -2,6 +2,7 @@ package deployments import ( "cmp" + "context" "fmt" "strconv" "time" @@ -33,7 +34,7 @@ var singleflightGroup = &singleflight.Group{} // RedeployWhenChanged pull and redeploy the stack when git repo changed // Stack will always be redeployed if force deployment is set to true -func RedeployWhenChanged(stackID portainer.StackID, deployer StackDeployer, datastore dataservices.DataStore, gitService portainer.GitService) error { +func RedeployWhenChanged(ctx context.Context, stackID portainer.StackID, deployer StackDeployer, datastore dataservices.DataStore, gitService portainer.GitService) error { stack, err := datastore.Stack().Read(stackID) if dataservices.IsErrObjectNotFound(err) { return scheduler.NewPermanentError(errors.WithMessagef(err, "failed to get the stack %v", stackID)) @@ -43,18 +44,18 @@ func RedeployWhenChanged(stackID portainer.StackID, deployer StackDeployer, data // Webhook if stack.AutoUpdate != nil && stack.AutoUpdate.Webhook != "" { - return redeployWhenChanged(stack, deployer, datastore, gitService, true) + return redeployWhenChanged(ctx, stack, deployer, datastore, gitService, true) } // Polling _, err, _ = singleflightGroup.Do(strconv.Itoa(int(stackID)), func() (any, error) { - return nil, redeployWhenChanged(stack, deployer, datastore, gitService, false) + return nil, redeployWhenChanged(ctx, stack, deployer, datastore, gitService, false) }) return err } -func redeployWhenChanged(stack *portainer.Stack, deployer StackDeployer, datastore dataservices.DataStore, gitService portainer.GitService, webhook bool) error { +func redeployWhenChanged(ctx context.Context, stack *portainer.Stack, deployer StackDeployer, datastore dataservices.DataStore, gitService portainer.GitService, webhook bool) error { log.Debug().Int("stack_id", int(stack.ID)).Msg("redeploying stack") if stack.GitConfig == nil { @@ -94,7 +95,7 @@ func redeployWhenChanged(stack *portainer.Stack, deployer StackDeployer, datasto if webhook { go func() { - if err := redeployWhenChangedSecondStage(stack, deployer, datastore, gitService, user, endpoint); err != nil { + if err := redeployWhenChangedSecondStage(ctx, stack, deployer, datastore, gitService, user, endpoint); err != nil { log.Error().Err(err). Int("stack_id", int(stack.ID)). Str("stack", stack.Name). @@ -107,10 +108,11 @@ func redeployWhenChanged(stack *portainer.Stack, deployer StackDeployer, datasto return nil } - return redeployWhenChangedSecondStage(stack, deployer, datastore, gitService, user, endpoint) + return redeployWhenChangedSecondStage(ctx, stack, deployer, datastore, gitService, user, endpoint) } func redeployWhenChangedSecondStage( + ctx context.Context, stack *portainer.Stack, deployer StackDeployer, datastore dataservices.DataStore, @@ -121,7 +123,7 @@ func redeployWhenChangedSecondStage( var gitCommitChangedOrForceUpdate bool if !stack.FromAppTemplate { - updated, newHash, err := update.UpdateGitObject(gitService, fmt.Sprintf("stack:%d", stack.ID), stack.GitConfig, false, stack.ProjectPath) + updated, newHash, err := update.UpdateGitObject(ctx, gitService, fmt.Sprintf("stack:%d", stack.ID), stack.GitConfig, false, stack.ProjectPath) if err != nil { return err } @@ -159,9 +161,9 @@ func redeployWhenChangedSecondStage( switch stack.Type { case portainer.DockerComposeStack: if stackutils.IsRelativePathStack(stack) { - err = deployer.DeployRemoteComposeStack(stack, endpoint, registries, true, true, false) + err = deployer.DeployRemoteComposeStack(ctx, stack, endpoint, registries, true, true, false) } else { - err = deployer.DeployComposeStack(stack, endpoint, registries, true, true, false) + err = deployer.DeployComposeStack(ctx, stack, endpoint, registries, true, true, false) } if err != nil { @@ -169,9 +171,9 @@ func redeployWhenChangedSecondStage( } case portainer.DockerSwarmStack: if stackutils.IsRelativePathStack(stack) { - err = deployer.DeployRemoteSwarmStack(stack, endpoint, registries, true, true) + err = deployer.DeployRemoteSwarmStack(ctx, stack, endpoint, registries, true, true) } else { - err = deployer.DeploySwarmStack(stack, endpoint, registries, true, true) + err = deployer.DeploySwarmStack(ctx, stack, endpoint, registries, true, true) } if err != nil { return errors.WithMessagef(err, "failed to deploy a docker compose stack %v", stack.ID) @@ -179,7 +181,7 @@ func redeployWhenChangedSecondStage( case portainer.KubernetesStack: log.Debug().Int("stack_id", int(stack.ID)).Msg("deploying a kube app") - if err := deployer.DeployKubernetesStack(stack, endpoint, user); err != nil { + if err := deployer.DeployKubernetesStack(ctx, stack, endpoint, user); err != nil { return errors.WithMessagef(err, "failed to deploy a kubernetes app stack %v", stack.ID) } default: diff --git a/api/stacks/deployments/deploy_test.go b/api/stacks/deployments/deploy_test.go index 4eb147931..fb8defb00 100644 --- a/api/stacks/deployments/deploy_test.go +++ b/api/stacks/deployments/deploy_test.go @@ -8,6 +8,7 @@ import ( "strconv" "strings" "testing" + "time" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/crypto" @@ -78,41 +79,41 @@ vJUUCFYm8+9p6gTVOcoMit+eGSwa81PCPEs1TnU1PV/PaDFeUhn/mg== type noopDeployer struct{} // without unpacker -func (s noopDeployer) DeploySwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, pullImage bool) error { +func (s noopDeployer) DeploySwarmStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, pullImage bool) error { return nil } -func (s noopDeployer) DeployComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, forcePullImage, forceRecreate bool) error { +func (s noopDeployer) DeployComposeStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, forcePullImage, forceRecreate bool) error { return nil } -func (s noopDeployer) DeployKubernetesStack(stack *portainer.Stack, endpoint *portainer.Endpoint, user *portainer.User) error { +func (s noopDeployer) DeployKubernetesStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, user *portainer.User) error { return nil } // with unpacker -func (s noopDeployer) DeployRemoteComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, forcePullImage, forceRecreate bool) error { +func (s noopDeployer) DeployRemoteComposeStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, forcePullImage, forceRecreate bool) error { return nil } -func (s noopDeployer) UndeployRemoteComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error { +func (s noopDeployer) UndeployRemoteComposeStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error { return nil } -func (s noopDeployer) StartRemoteComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry) error { +func (s noopDeployer) StartRemoteComposeStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry) error { return nil } -func (s noopDeployer) StopRemoteComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error { +func (s noopDeployer) StopRemoteComposeStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error { return nil } -func (s noopDeployer) DeployRemoteSwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, pullImage bool) error { +func (s noopDeployer) DeployRemoteSwarmStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, pullImage bool) error { return nil } -func (s noopDeployer) UndeployRemoteSwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error { +func (s noopDeployer) UndeployRemoteSwarmStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error { return nil } -func (s noopDeployer) StartRemoteSwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry) error { +func (s noopDeployer) StartRemoteSwarmStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry) error { return nil } -func (s noopDeployer) StopRemoteSwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error { +func (s noopDeployer) StopRemoteSwarmStack(_ context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error { return nil } @@ -145,7 +146,10 @@ func agentServer(t *testing.T) string { }() t.Cleanup(func() { - require.NoError(t, s.Shutdown(context.Background())) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + require.NoError(t, s.Shutdown(ctx)) require.ErrorIs(t, <-errCh, http.ErrServerClosed) }) @@ -155,7 +159,7 @@ func agentServer(t *testing.T) string { func Test_redeployWhenChanged_FailsWhenCannotFindStack(t *testing.T) { _, store := datastore.MustNewTestStore(t, true, true) - err := RedeployWhenChanged(1, nil, store, nil) + err := RedeployWhenChanged(t.Context(), 1, nil, store, nil) require.Error(t, err) assert.Truef(t, strings.HasPrefix(err.Error(), "failed to get the stack"), "it isn't an error we expected: %v", err.Error()) } @@ -170,7 +174,7 @@ func Test_redeployWhenChanged_DoesNothingWhenNotAGitBasedStack(t *testing.T) { err = store.Stack().Create(&portainer.Stack{ID: 1, CreatedBy: "admin"}) require.NoError(t, err, "failed to create a test stack") - err = RedeployWhenChanged(1, nil, store, testhelpers.NewGitService(nil, "")) + err = RedeployWhenChanged(t.Context(), 1, nil, store, testhelpers.NewGitService(nil, "")) require.NoError(t, err) } @@ -199,7 +203,7 @@ func Test_redeployWhenChanged_DoesNothingWhenNoGitChanges(t *testing.T) { }}) require.NoError(t, err, "failed to create a test stack") - err = RedeployWhenChanged(1, nil, store, testhelpers.NewGitService(nil, "oldHash")) + err = RedeployWhenChanged(t.Context(), 1, nil, store, testhelpers.NewGitService(nil, "oldHash")) require.NoError(t, err) } @@ -233,7 +237,7 @@ func Test_redeployWhenChanged_FailsWhenCannotClone(t *testing.T) { }}) require.NoError(t, err, "failed to create a test stack") - err = RedeployWhenChanged(1, nil, store, testhelpers.NewGitService(cloneErr, "newHash")) + err = RedeployWhenChanged(t.Context(), 1, nil, store, testhelpers.NewGitService(cloneErr, "newHash")) require.Error(t, err) require.ErrorIs(t, err, cloneErr, "should failed to clone but didn't, check test setup") } @@ -270,7 +274,7 @@ func Test_redeployWhenChanged(t *testing.T) { err = store.Stack().Update(stack.ID, &stack) require.NoError(t, err) - err = RedeployWhenChanged(1, noopDeployer{}, store, testhelpers.NewGitService(nil, "newHash")) + err = RedeployWhenChanged(t.Context(), 1, noopDeployer{}, store, testhelpers.NewGitService(nil, "newHash")) require.NoError(t, err) }) @@ -279,7 +283,7 @@ func Test_redeployWhenChanged(t *testing.T) { err = store.Stack().Update(stack.ID, &stack) require.NoError(t, err) - err = RedeployWhenChanged(1, noopDeployer{}, store, testhelpers.NewGitService(nil, "newHash")) + err = RedeployWhenChanged(t.Context(), 1, noopDeployer{}, store, testhelpers.NewGitService(nil, "newHash")) require.NoError(t, err) }) @@ -288,7 +292,7 @@ func Test_redeployWhenChanged(t *testing.T) { err = store.Stack().Update(stack.ID, &stack) require.NoError(t, err) - err = RedeployWhenChanged(1, noopDeployer{}, store, testhelpers.NewGitService(nil, "newHash")) + err = RedeployWhenChanged(t.Context(), 1, noopDeployer{}, store, testhelpers.NewGitService(nil, "newHash")) require.NoError(t, err) }) } diff --git a/api/stacks/deployments/deployer.go b/api/stacks/deployments/deployer.go index f4733d587..9cea23444 100644 --- a/api/stacks/deployments/deployer.go +++ b/api/stacks/deployments/deployer.go @@ -14,9 +14,9 @@ import ( ) type BaseStackDeployer interface { - DeploySwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, pullImage bool) error - DeployComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, forcePullImage, forceRecreate bool) error - DeployKubernetesStack(stack *portainer.Stack, endpoint *portainer.Endpoint, user *portainer.User) error + DeploySwarmStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, pullImage bool) error + DeployComposeStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, forcePullImage, forceRecreate bool) error + DeployKubernetesStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, user *portainer.User) error } type StackDeployer interface { @@ -45,23 +45,23 @@ func NewStackDeployer(swarmStackManager portainer.SwarmStackManager, composeStac dataStore: dataStore, } } -func (d *stackDeployer) DeploySwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, pullImage bool) error { +func (d *stackDeployer) DeploySwarmStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, pullImage bool) error { d.lock.Lock() defer d.lock.Unlock() - if err := d.swarmStackManager.Login(registries, endpoint); err != nil { + if err := d.swarmStackManager.Login(ctx, registries, endpoint); err != nil { log.Warn().Err(err).Msg("unable to login to registries for swarm stack deployment") } defer func() { - if err := d.swarmStackManager.Logout(endpoint); err != nil { + if err := d.swarmStackManager.Logout(ctx, endpoint); err != nil { log.Warn().Err(err).Msg("unable to logout from registries after swarm stack deployment") } }() - return d.swarmStackManager.Deploy(stack, prune, pullImage, endpoint) + return d.swarmStackManager.Deploy(ctx, stack, prune, pullImage, endpoint) } -func (d *stackDeployer) DeployComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, forcePullImage, forceRecreate bool) error { +func (d *stackDeployer) DeployComposeStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune, forcePullImage, forceRecreate bool) error { d.lock.Lock() defer d.lock.Unlock() @@ -69,19 +69,19 @@ func (d *stackDeployer) DeployComposeStack(stack *portainer.Stack, endpoint *por // --force-recreate doesn't pull updated images if forcePullImage { - if err := d.composeStackManager.Pull(context.TODO(), stack, endpoint, options); err != nil { + if err := d.composeStackManager.Pull(ctx, stack, endpoint, options); err != nil { return err } } - return d.composeStackManager.Up(context.TODO(), stack, endpoint, portainer.ComposeUpOptions{ + return d.composeStackManager.Up(ctx, stack, endpoint, portainer.ComposeUpOptions{ ComposeOptions: options, ForceRecreate: forceRecreate, Prune: prune, }) } -func (d *stackDeployer) DeployKubernetesStack(stack *portainer.Stack, endpoint *portainer.Endpoint, user *portainer.User) error { +func (d *stackDeployer) DeployKubernetesStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, user *portainer.User) error { d.lock.Lock() defer d.lock.Unlock() @@ -102,7 +102,7 @@ func (d *stackDeployer) DeployKubernetesStack(stack *portainer.Stack, endpoint * return errors.Wrap(err, "failed to create temp kub deployment files") } - if err := k8sDeploymentConfig.Deploy(); err != nil { + if err := k8sDeploymentConfig.Deploy(ctx); err != nil { return errors.Wrap(err, "failed to deploy kubernetes application") } diff --git a/api/stacks/deployments/deployer_remote.go b/api/stacks/deployments/deployer_remote.go index 1dd931325..7fff8f399 100644 --- a/api/stacks/deployments/deployer_remote.go +++ b/api/stacks/deployments/deployer_remote.go @@ -34,19 +34,20 @@ const ( type RemoteStackDeployer interface { // compose - DeployRemoteComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune bool, forcePullImage bool, forceRecreate bool) error - UndeployRemoteComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error - StartRemoteComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry) error - StopRemoteComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error + DeployRemoteComposeStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune bool, forcePullImage bool, forceRecreate bool) error + UndeployRemoteComposeStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error + StartRemoteComposeStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry) error + StopRemoteComposeStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error // swarm - DeployRemoteSwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune bool, pullImage bool) error - UndeployRemoteSwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error - StartRemoteSwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry) error - StopRemoteSwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error + DeployRemoteSwarmStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, prune bool, pullImage bool) error + UndeployRemoteSwarmStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error + StartRemoteSwarmStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry) error + StopRemoteSwarmStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error } // Deploy a compose stack on remote environment using a https://github.com/portainer/compose-unpacker container func (d *stackDeployer) DeployRemoteComposeStack( + ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, @@ -61,12 +62,13 @@ func (d *stackDeployer) DeployRemoteComposeStack( // --force-recreate doesn't pull updated images if forcePullImage { - if err := d.composeStackManager.Pull(context.TODO(), stack, endpoint, options); err != nil { + if err := d.composeStackManager.Pull(ctx, stack, endpoint, options); err != nil { return err } } return d.remoteStack( + ctx, stack, endpoint, OperationDeploy, @@ -79,20 +81,22 @@ func (d *stackDeployer) DeployRemoteComposeStack( } // Undeploy a compose stack on remote environment using a https://github.com/portainer/compose-unpacker container -func (d *stackDeployer) UndeployRemoteComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error { +func (d *stackDeployer) UndeployRemoteComposeStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error { d.lock.Lock() defer d.lock.Unlock() - return d.remoteStack(stack, endpoint, OperationUndeploy, unpackerCmdBuilderOptions{}) + return d.remoteStack(ctx, stack, endpoint, OperationUndeploy, unpackerCmdBuilderOptions{}) } // Start a compose stack on remote environment using a https://github.com/portainer/compose-unpacker container func (d *stackDeployer) StartRemoteComposeStack( + ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, ) error { return d.remoteStack( + ctx, stack, endpoint, OperationComposeStart, @@ -103,12 +107,13 @@ func (d *stackDeployer) StartRemoteComposeStack( } // Stop a compose stack on remote environment using a https://github.com/portainer/compose-unpacker container -func (d *stackDeployer) StopRemoteComposeStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error { - return d.remoteStack(stack, endpoint, OperationComposeStop, unpackerCmdBuilderOptions{}) +func (d *stackDeployer) StopRemoteComposeStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error { + return d.remoteStack(ctx, stack, endpoint, OperationComposeStop, unpackerCmdBuilderOptions{}) } // Deploy a swarm stack on remote environment using a https://github.com/portainer/compose-unpacker container func (d *stackDeployer) DeployRemoteSwarmStack( + ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, @@ -118,16 +123,16 @@ func (d *stackDeployer) DeployRemoteSwarmStack( d.lock.Lock() defer d.lock.Unlock() - if err := d.swarmStackManager.Login(registries, endpoint); err != nil { + if err := d.swarmStackManager.Login(ctx, registries, endpoint); err != nil { log.Warn().Err(err).Msg("unable to login to registries for swarm stack deployment") } defer func() { - if err := d.swarmStackManager.Logout(endpoint); err != nil { + if err := d.swarmStackManager.Logout(ctx, endpoint); err != nil { log.Warn().Err(err).Msg("unable to logout from registries after swarm stack deployment") } }() - return d.remoteStack(stack, endpoint, OperationSwarmDeploy, unpackerCmdBuilderOptions{ + return d.remoteStack(ctx, stack, endpoint, OperationSwarmDeploy, unpackerCmdBuilderOptions{ pullImage: pullImage, prune: prune, forceRecreate: stack.AutoUpdate != nil && stack.AutoUpdate.ForceUpdate, @@ -136,20 +141,22 @@ func (d *stackDeployer) DeployRemoteSwarmStack( } // Undeploy a swarm stack on remote environment using a https://github.com/portainer/compose-unpacker container -func (d *stackDeployer) UndeployRemoteSwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error { +func (d *stackDeployer) UndeployRemoteSwarmStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error { d.lock.Lock() defer d.lock.Unlock() - return d.remoteStack(stack, endpoint, OperationSwarmUndeploy, unpackerCmdBuilderOptions{}) + return d.remoteStack(ctx, stack, endpoint, OperationSwarmUndeploy, unpackerCmdBuilderOptions{}) } // Start a swarm stack on remote environment using a https://github.com/portainer/compose-unpacker container func (d *stackDeployer) StartRemoteSwarmStack( + ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, registries []portainer.Registry, ) error { return d.remoteStack( + ctx, stack, endpoint, OperationSwarmStart, @@ -158,8 +165,8 @@ func (d *stackDeployer) StartRemoteSwarmStack( } // Stop a swarm stack on remote environment using a https://github.com/portainer/compose-unpacker container -func (d *stackDeployer) StopRemoteSwarmStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error { - return d.remoteStack(stack, endpoint, OperationSwarmStop, unpackerCmdBuilderOptions{}) +func (d *stackDeployer) StopRemoteSwarmStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error { + return d.remoteStack(ctx, stack, endpoint, OperationSwarmStop, unpackerCmdBuilderOptions{}) } // Does all the heavy lifting: @@ -168,9 +175,7 @@ func (d *stackDeployer) StopRemoteSwarmStack(stack *portainer.Stack, endpoint *p // * deploy compose-unpacker container // * wait for deployment to end // * gather deployment logs and bubble them up -func (d *stackDeployer) remoteStack(stack *portainer.Stack, endpoint *portainer.Endpoint, operation StackRemoteOperation, opts unpackerCmdBuilderOptions) error { - ctx := context.TODO() - +func (d *stackDeployer) remoteStack(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint, operation StackRemoteOperation, opts unpackerCmdBuilderOptions) error { cli, err := d.createDockerClient(ctx, endpoint) if err != nil { return errors.WithMessage(err, "unable to create docker client") diff --git a/api/stacks/deployments/deployment_compose_config.go b/api/stacks/deployments/deployment_compose_config.go index c23a557e6..8b16fd58a 100644 --- a/api/stacks/deployments/deployment_compose_config.go +++ b/api/stacks/deployments/deployment_compose_config.go @@ -1,6 +1,7 @@ package deployments import ( + "context" "fmt" portainer "github.com/portainer/portainer/api" @@ -68,7 +69,7 @@ func (config *ComposeStackDeploymentConfig) GetUsername() string { return "" } -func (config *ComposeStackDeploymentConfig) Deploy() error { +func (config *ComposeStackDeploymentConfig) Deploy(ctx context.Context) error { if config.FileService == nil || config.StackDeployer == nil { log.Debug().Msg("file service or stack deployer is not initialized") return errors.New("file service or stack deployer cannot be nil") @@ -88,10 +89,10 @@ func (config *ComposeStackDeploymentConfig) Deploy() error { } if stackutils.IsRelativePathStack(config.stack) { - return config.StackDeployer.DeployRemoteComposeStack(config.stack, config.endpoint, config.registries, config.prune, config.forcePullImage, config.ForceCreate) + return config.StackDeployer.DeployRemoteComposeStack(ctx, config.stack, config.endpoint, config.registries, config.prune, config.forcePullImage, config.ForceCreate) } - return config.StackDeployer.DeployComposeStack(config.stack, config.endpoint, config.registries, config.prune, config.forcePullImage, config.ForceCreate) + return config.StackDeployer.DeployComposeStack(ctx, config.stack, config.endpoint, config.registries, config.prune, config.forcePullImage, config.ForceCreate) } func (config *ComposeStackDeploymentConfig) GetResponse() string { diff --git a/api/stacks/deployments/deployment_config.go b/api/stacks/deployments/deployment_config.go index e0e35d462..d8f0c66f3 100644 --- a/api/stacks/deployments/deployment_config.go +++ b/api/stacks/deployments/deployment_config.go @@ -1,7 +1,9 @@ package deployments +import "context" + type StackDeploymentConfiger interface { GetUsername() string - Deploy() error + Deploy(ctx context.Context) error GetResponse() string } diff --git a/api/stacks/deployments/deployment_kubernetes_config.go b/api/stacks/deployments/deployment_kubernetes_config.go index 48bfb5d1e..9338c4429 100644 --- a/api/stacks/deployments/deployment_kubernetes_config.go +++ b/api/stacks/deployments/deployment_kubernetes_config.go @@ -1,6 +1,7 @@ package deployments import ( + "context" "fmt" "os" @@ -36,7 +37,7 @@ func (config *KubernetesStackDeploymentConfig) GetUsername() string { return config.user.Username } -func (config *KubernetesStackDeploymentConfig) Deploy() error { +func (config *KubernetesStackDeploymentConfig) Deploy(ctx context.Context) error { fileNames := stackutils.GetStackFilePaths(config.stack, false) manifestFilePaths := make([]string, 0, len(fileNames)) @@ -71,7 +72,7 @@ func (config *KubernetesStackDeploymentConfig) Deploy() error { manifestFilePaths = append(manifestFilePaths, manifestFilePath) } - output, err := config.kubernetesDeployer.Deploy(config.user.ID, config.endpoint, manifestFilePaths, config.stack.Namespace) + output, err := config.kubernetesDeployer.Deploy(ctx, config.user.ID, config.endpoint, manifestFilePaths, config.stack.Namespace) if err != nil { return fmt.Errorf("failed to deploy kubernete stack: %w", err) } diff --git a/api/stacks/deployments/deployment_swarm_config.go b/api/stacks/deployments/deployment_swarm_config.go index f178c8fcf..c85e2920a 100644 --- a/api/stacks/deployments/deployment_swarm_config.go +++ b/api/stacks/deployments/deployment_swarm_config.go @@ -1,6 +1,7 @@ package deployments import ( + "context" "fmt" "log" @@ -65,7 +66,7 @@ func (config *SwarmStackDeploymentConfig) GetUsername() string { return "" } -func (config *SwarmStackDeploymentConfig) Deploy() error { +func (config *SwarmStackDeploymentConfig) Deploy(ctx context.Context) error { if config.FileService == nil || config.StackDeployer == nil { log.Println("[deployment, swarm] file service or stack deployer is not initialised") return errors.New("file service or stack deployer cannot be nil") @@ -85,10 +86,10 @@ func (config *SwarmStackDeploymentConfig) Deploy() error { } if stackutils.IsRelativePathStack(config.stack) { - return config.StackDeployer.DeployRemoteSwarmStack(config.stack, config.endpoint, config.registries, config.prune, config.pullImage) + return config.StackDeployer.DeployRemoteSwarmStack(ctx, config.stack, config.endpoint, config.registries, config.prune, config.pullImage) } - return config.StackDeployer.DeploySwarmStack(config.stack, config.endpoint, config.registries, config.prune, config.pullImage) + return config.StackDeployer.DeploySwarmStack(ctx, config.stack, config.endpoint, config.registries, config.prune, config.pullImage) } func (config *SwarmStackDeploymentConfig) GetResponse() string { diff --git a/api/stacks/deployments/scheduled.go b/api/stacks/deployments/scheduled.go index c47ff62b4..44ee81cd4 100644 --- a/api/stacks/deployments/scheduled.go +++ b/api/stacks/deployments/scheduled.go @@ -1,6 +1,7 @@ package deployments import ( + "context" "time" "github.com/pkg/errors" @@ -22,7 +23,7 @@ func StartStackSchedules(scheduler *scheduler.Scheduler, stackdeployer StackDepl } stackID := stack.ID // to be captured by the scheduled function jobID := scheduler.StartJobEvery(d, func() error { - return RedeployWhenChanged(stackID, stackdeployer, datastore, gitService) + return RedeployWhenChanged(context.Background(), stackID, stackdeployer, datastore, gitService) }) stack.AutoUpdate.JobID = jobID diff --git a/api/stacks/stackbuilders/compose_file_content_builder.go b/api/stacks/stackbuilders/compose_file_content_builder.go index 980ec9057..5ca9bbf2e 100644 --- a/api/stacks/stackbuilders/compose_file_content_builder.go +++ b/api/stacks/stackbuilders/compose_file_content_builder.go @@ -1,6 +1,7 @@ package stackbuilders import ( + "context" "fmt" "strconv" @@ -63,7 +64,7 @@ func (b *ComposeStackFileContentBuilder) SetFileContent(payload *StackPayload) F return b } -func (b *ComposeStackFileContentBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) FileContentMethodStackBuildProcess { +func (b *ComposeStackFileContentBuilder) Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) FileContentMethodStackBuildProcess { if b.hasError() { return b } @@ -77,5 +78,5 @@ func (b *ComposeStackFileContentBuilder) Deploy(payload *StackPayload, endpoint b.deploymentConfiger = composeDeploymentConfig b.stack.CreatedBy = b.deploymentConfiger.GetUsername() - return b.FileContentMethodStackBuilder.Deploy(payload, endpoint) + return b.FileContentMethodStackBuilder.Deploy(ctx, payload, endpoint) } diff --git a/api/stacks/stackbuilders/compose_file_upload_builder.go b/api/stacks/stackbuilders/compose_file_upload_builder.go index 9921d0a80..4e99b4c39 100644 --- a/api/stacks/stackbuilders/compose_file_upload_builder.go +++ b/api/stacks/stackbuilders/compose_file_upload_builder.go @@ -1,6 +1,8 @@ package stackbuilders import ( + "context" + portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/filesystem" @@ -53,7 +55,7 @@ func (b *ComposeStackFileUploadBuilder) SetUploadedFile(payload *StackPayload) F return b } -func (b *ComposeStackFileUploadBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) FileUploadMethodStackBuildProcess { +func (b *ComposeStackFileUploadBuilder) Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) FileUploadMethodStackBuildProcess { if b.hasError() { return b } @@ -67,5 +69,5 @@ func (b *ComposeStackFileUploadBuilder) Deploy(payload *StackPayload, endpoint * b.deploymentConfiger = composeDeploymentConfig b.stack.CreatedBy = b.deploymentConfiger.GetUsername() - return b.FileUploadMethodStackBuilder.Deploy(payload, endpoint) + return b.FileUploadMethodStackBuilder.Deploy(ctx, payload, endpoint) } diff --git a/api/stacks/stackbuilders/compose_git_builder.go b/api/stacks/stackbuilders/compose_git_builder.go index 5088b6d29..d93681c64 100644 --- a/api/stacks/stackbuilders/compose_git_builder.go +++ b/api/stacks/stackbuilders/compose_git_builder.go @@ -1,6 +1,8 @@ package stackbuilders import ( + "context" + portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/http/security" @@ -48,12 +50,12 @@ func (b *ComposeStackGitBuilder) SetUniqueInfo(payload *StackPayload) GitMethodS return b } -func (b *ComposeStackGitBuilder) SetGitRepository(payload *StackPayload) GitMethodStackBuildProcess { - b.GitMethodStackBuilder.SetGitRepository(payload) +func (b *ComposeStackGitBuilder) SetGitRepository(ctx context.Context, payload *StackPayload) GitMethodStackBuildProcess { + b.GitMethodStackBuilder.SetGitRepository(ctx, payload) return b } -func (b *ComposeStackGitBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) GitMethodStackBuildProcess { +func (b *ComposeStackGitBuilder) Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) GitMethodStackBuildProcess { if b.hasError() { return b } @@ -67,7 +69,7 @@ func (b *ComposeStackGitBuilder) Deploy(payload *StackPayload, endpoint *portain b.deploymentConfiger = composeDeploymentConfig b.stack.CreatedBy = b.deploymentConfiger.GetUsername() - return b.GitMethodStackBuilder.Deploy(payload, endpoint) + return b.GitMethodStackBuilder.Deploy(ctx, payload, endpoint) } func (b *ComposeStackGitBuilder) SetAutoUpdate(payload *StackPayload) GitMethodStackBuildProcess { diff --git a/api/stacks/stackbuilders/director.go b/api/stacks/stackbuilders/director.go index 0f671433a..7703e2d82 100644 --- a/api/stacks/stackbuilders/director.go +++ b/api/stacks/stackbuilders/director.go @@ -1,6 +1,7 @@ package stackbuilders import ( + "context" "errors" portainer "github.com/portainer/portainer/api" @@ -22,7 +23,7 @@ func NewStackBuilderDirector(b any) *StackBuilderDirector { // created stack and any error encountered during the process. // The returned error is of type *httperror.HandlerError, which could be a BadRequest // or InternalServerError depending on the error encountered during the stack build process. -func (d *StackBuilderDirector) Build(payload *StackPayload, endpoint *portainer.Endpoint) (*portainer.Stack, *httperror.HandlerError) { +func (d *StackBuilderDirector) Build(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) (*portainer.Stack, *httperror.HandlerError) { var ( stack *portainer.Stack err error @@ -34,7 +35,7 @@ func (d *StackBuilderDirector) Build(payload *StackPayload, endpoint *portainer. case GitMethodStackBuildProcess: stack, err = builder.SetGeneralInfo(payload, endpoint). SetUniqueInfo(payload). - SetGitRepository(payload). + SetGitRepository(ctx, payload). SaveStack() if err != nil { return nil, httperror.InternalServerError("Failed to save stack via Git repository method", err) @@ -42,7 +43,7 @@ func (d *StackBuilderDirector) Build(payload *StackPayload, endpoint *portainer. // Since AutoUpdate job for stack is created after a successful // deployment, we need to update the stack with the new generated job ID - stack, err = builder.Deploy(payload, endpoint). + stack, err = builder.Deploy(ctx, payload, endpoint). SetAutoUpdate(payload). UpdateStack(stack) @@ -55,7 +56,7 @@ func (d *StackBuilderDirector) Build(payload *StackPayload, endpoint *portainer. return nil, httperror.InternalServerError("Failed to save stack via File Upload method", err) } - builder.Deploy(payload, endpoint) + builder.Deploy(ctx, payload, endpoint) err = builder.Error() case FileContentMethodStackBuildProcess: @@ -67,7 +68,7 @@ func (d *StackBuilderDirector) Build(payload *StackPayload, endpoint *portainer. return nil, httperror.InternalServerError("Failed to save stack via File Content method", err) } - builder.Deploy(payload, endpoint) + builder.Deploy(ctx, payload, endpoint) err = builder.Error() case UrlMethodStackBuildProcess: @@ -79,7 +80,7 @@ func (d *StackBuilderDirector) Build(payload *StackPayload, endpoint *portainer. return nil, httperror.InternalServerError("Failed to save stack via URL method", err) } - builder.Deploy(payload, endpoint) + builder.Deploy(ctx, payload, endpoint) err = builder.Error() default: diff --git a/api/stacks/stackbuilders/k8s_file_content_builder.go b/api/stacks/stackbuilders/k8s_file_content_builder.go index fae2ceec5..421f72b56 100644 --- a/api/stacks/stackbuilders/k8s_file_content_builder.go +++ b/api/stacks/stackbuilders/k8s_file_content_builder.go @@ -1,6 +1,7 @@ package stackbuilders import ( + "context" "fmt" "strconv" "sync" @@ -76,7 +77,7 @@ func (b *K8sStackFileContentBuilder) SetFileContent(payload *StackPayload) FileC return b } -func (b *K8sStackFileContentBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) FileContentMethodStackBuildProcess { +func (b *K8sStackFileContentBuilder) Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) FileContentMethodStackBuildProcess { if b.hasError() { return b } @@ -100,7 +101,7 @@ func (b *K8sStackFileContentBuilder) Deploy(payload *StackPayload, endpoint *por b.deploymentConfiger = k8sDeploymentConfig - return b.FileContentMethodStackBuilder.Deploy(payload, endpoint) + return b.FileContentMethodStackBuilder.Deploy(ctx, payload, endpoint) } func (b *K8sStackFileContentBuilder) GetResponse() string { diff --git a/api/stacks/stackbuilders/k8s_git_builder.go b/api/stacks/stackbuilders/k8s_git_builder.go index 8fcbe3e3f..4b491ff4c 100644 --- a/api/stacks/stackbuilders/k8s_git_builder.go +++ b/api/stacks/stackbuilders/k8s_git_builder.go @@ -1,6 +1,7 @@ package stackbuilders import ( + "context" "fmt" "sync" @@ -60,13 +61,13 @@ func (b *KubernetesStackGitBuilder) SetUniqueInfo(payload *StackPayload) GitMeth return b } -func (b *KubernetesStackGitBuilder) SetGitRepository(payload *StackPayload) GitMethodStackBuildProcess { - b.GitMethodStackBuilder.SetGitRepository(payload) +func (b *KubernetesStackGitBuilder) SetGitRepository(ctx context.Context, payload *StackPayload) GitMethodStackBuildProcess { + b.GitMethodStackBuilder.SetGitRepository(ctx, payload) return b } -func (b *KubernetesStackGitBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) GitMethodStackBuildProcess { +func (b *KubernetesStackGitBuilder) Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) GitMethodStackBuildProcess { if b.hasError() { return b } @@ -89,7 +90,7 @@ func (b *KubernetesStackGitBuilder) Deploy(payload *StackPayload, endpoint *port b.deploymentConfiger = k8sDeploymentConfig - return b.GitMethodStackBuilder.Deploy(payload, endpoint) + return b.GitMethodStackBuilder.Deploy(ctx, payload, endpoint) } func (b *KubernetesStackGitBuilder) SetAutoUpdate(payload *StackPayload) GitMethodStackBuildProcess { diff --git a/api/stacks/stackbuilders/k8s_url_builder.go b/api/stacks/stackbuilders/k8s_url_builder.go index 312bea01c..13f6ca286 100644 --- a/api/stacks/stackbuilders/k8s_url_builder.go +++ b/api/stacks/stackbuilders/k8s_url_builder.go @@ -1,6 +1,7 @@ package stackbuilders import ( + "context" "fmt" "strconv" "sync" @@ -82,7 +83,7 @@ func (b *KubernetesStackUrlBuilder) SetURL(payload *StackPayload) UrlMethodStack return b } -func (b *KubernetesStackUrlBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) UrlMethodStackBuildProcess { +func (b *KubernetesStackUrlBuilder) Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) UrlMethodStackBuildProcess { if b.hasError() { return b } @@ -106,7 +107,7 @@ func (b *KubernetesStackUrlBuilder) Deploy(payload *StackPayload, endpoint *port b.deploymentConfiger = k8sDeploymentConfig - return b.UrlMethodStackBuilder.Deploy(payload, endpoint) + return b.UrlMethodStackBuilder.Deploy(ctx, payload, endpoint) } func (b *KubernetesStackUrlBuilder) GetResponse() string { diff --git a/api/stacks/stackbuilders/stack_file_content_builder.go b/api/stacks/stackbuilders/stack_file_content_builder.go index 4f08d7e0b..5f058393d 100644 --- a/api/stacks/stackbuilders/stack_file_content_builder.go +++ b/api/stacks/stackbuilders/stack_file_content_builder.go @@ -1,6 +1,7 @@ package stackbuilders import ( + "context" "time" portainer "github.com/portainer/portainer/api" @@ -12,7 +13,7 @@ type FileContentMethodStackBuildProcess interface { // Set unique stack information, e.g. swarm stack has swarmID, kubernetes stack has namespace SetUniqueInfo(payload *StackPayload) FileContentMethodStackBuildProcess // Deploy stack based on the configuration - Deploy(payload *StackPayload, endpoint *portainer.Endpoint) FileContentMethodStackBuildProcess + Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) FileContentMethodStackBuildProcess // Save the stack information to database SaveStack() (*portainer.Stack, error) // Get response from HTTP request. Use if it is needed @@ -44,13 +45,13 @@ func (b *FileContentMethodStackBuilder) SetFileContent(payload *StackPayload) Fi return b } -func (b *FileContentMethodStackBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) FileContentMethodStackBuildProcess { +func (b *FileContentMethodStackBuilder) Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) FileContentMethodStackBuildProcess { if b.hasError() { return b } // Deploy the stack - b.err = b.deploymentConfiger.Deploy() + b.err = b.deploymentConfiger.Deploy(ctx) return b } diff --git a/api/stacks/stackbuilders/stack_file_upload_builder.go b/api/stacks/stackbuilders/stack_file_upload_builder.go index 7a92f66d4..cb3fbf54a 100644 --- a/api/stacks/stackbuilders/stack_file_upload_builder.go +++ b/api/stacks/stackbuilders/stack_file_upload_builder.go @@ -1,6 +1,7 @@ package stackbuilders import ( + "context" "fmt" "strconv" "time" @@ -14,7 +15,7 @@ type FileUploadMethodStackBuildProcess interface { // Set unique stack information, e.g. swarm stack has swarmID, kubernetes stack has namespace SetUniqueInfo(payload *StackPayload) FileUploadMethodStackBuildProcess // Deploy stack based on the configuration - Deploy(payload *StackPayload, endpoint *portainer.Endpoint) FileUploadMethodStackBuildProcess + Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) FileUploadMethodStackBuildProcess // Save the stack information to database SaveStack() (*portainer.Stack, error) // Get response from HTTP request. Use if it is needed @@ -59,13 +60,13 @@ func (b *FileUploadMethodStackBuilder) SetUploadedFile(payload *StackPayload) Fi return b } -func (b *FileUploadMethodStackBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) FileUploadMethodStackBuildProcess { +func (b *FileUploadMethodStackBuilder) Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) FileUploadMethodStackBuildProcess { if b.hasError() { return b } // Deploy the stack - if err := b.deploymentConfiger.Deploy(); err != nil { + if err := b.deploymentConfiger.Deploy(ctx); err != nil { b.err = err return b diff --git a/api/stacks/stackbuilders/stack_git_builder.go b/api/stacks/stackbuilders/stack_git_builder.go index d10f7be4a..eca183c88 100644 --- a/api/stacks/stackbuilders/stack_git_builder.go +++ b/api/stacks/stackbuilders/stack_git_builder.go @@ -1,6 +1,7 @@ package stackbuilders import ( + "context" "fmt" "strconv" "time" @@ -20,13 +21,13 @@ type GitMethodStackBuildProcess interface { // Set unique stack information, e.g. swarm stack has swarmID, kubernetes stack has namespace SetUniqueInfo(payload *StackPayload) GitMethodStackBuildProcess // Deploy stack based on the configuration - Deploy(payload *StackPayload, endpoint *portainer.Endpoint) GitMethodStackBuildProcess + Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) GitMethodStackBuildProcess // Save the stack information to database SaveStack() (*portainer.Stack, error) // Get response from HTTP request. Use if it is needed GetResponse() string // Set git repository configuration - SetGitRepository(payload *StackPayload) GitMethodStackBuildProcess + SetGitRepository(ctx context.Context, payload *StackPayload) GitMethodStackBuildProcess // Set auto update setting SetAutoUpdate(payload *StackPayload) GitMethodStackBuildProcess UpdateStack(stack *portainer.Stack) (*portainer.Stack, error) @@ -55,7 +56,7 @@ func (b *GitMethodStackBuilder) SetUniqueInfo(payload *StackPayload) GitMethodSt return b } -func (b *GitMethodStackBuilder) SetGitRepository(payload *StackPayload) GitMethodStackBuildProcess { +func (b *GitMethodStackBuilder) SetGitRepository(ctx context.Context, payload *StackPayload) GitMethodStackBuildProcess { if b.hasError() { return b } @@ -91,7 +92,7 @@ func (b *GitMethodStackBuilder) SetGitRepository(payload *StackPayload) GitMetho return b.fileService.GetStackProjectPath(stackFolder) } - commitHash, err := stackutils.DownloadGitRepository(repoConfig, b.gitService, getProjectPath) + commitHash, err := stackutils.DownloadGitRepository(ctx, repoConfig, b.gitService, getProjectPath) if err != nil { b.err = fmt.Errorf("failed to download git repository: %w", err) return b @@ -104,13 +105,13 @@ func (b *GitMethodStackBuilder) SetGitRepository(payload *StackPayload) GitMetho return b } -func (b *GitMethodStackBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) GitMethodStackBuildProcess { +func (b *GitMethodStackBuilder) Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) GitMethodStackBuildProcess { if b.hasError() { return b } // Deploy the stack - b.err = b.deploymentConfiger.Deploy() + b.err = b.deploymentConfiger.Deploy(ctx) return b } @@ -144,7 +145,7 @@ func (b *GitMethodStackBuilder) SetAutoUpdate(payload *StackPayload) GitMethodSt } if payload.AutoUpdate != nil && payload.AutoUpdate.Interval != "" { - jobID, err := deployments.StartAutoupdate(b.stack.ID, + jobID, err := deployments.StartAutoupdate(context.TODO(), b.stack.ID, b.stack.AutoUpdate.Interval, b.scheduler, b.stackDeployer, diff --git a/api/stacks/stackbuilders/stack_url_builder.go b/api/stacks/stackbuilders/stack_url_builder.go index c91c90392..8fe6f8893 100644 --- a/api/stacks/stackbuilders/stack_url_builder.go +++ b/api/stacks/stackbuilders/stack_url_builder.go @@ -1,6 +1,7 @@ package stackbuilders import ( + "context" "time" portainer "github.com/portainer/portainer/api" @@ -12,7 +13,7 @@ type UrlMethodStackBuildProcess interface { // Set unique stack information, e.g. swarm stack has swarmID, kubernetes stack has namespace SetUniqueInfo(payload *StackPayload) UrlMethodStackBuildProcess // Deploy stack based on the configuration - Deploy(payload *StackPayload, endpoint *portainer.Endpoint) UrlMethodStackBuildProcess + Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) UrlMethodStackBuildProcess // Save the stack information to database SaveStack() (*portainer.Stack, error) // Get reponse from http request. Use if it is needed @@ -40,20 +41,16 @@ func (b *UrlMethodStackBuilder) SetUniqueInfo(payload *StackPayload) UrlMethodSt } func (b *UrlMethodStackBuilder) SetURL(payload *StackPayload) UrlMethodStackBuildProcess { - if b.hasError() { - return b - } - return b } -func (b *UrlMethodStackBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) UrlMethodStackBuildProcess { +func (b *UrlMethodStackBuilder) Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) UrlMethodStackBuildProcess { if b.hasError() { return b } // Deploy the stack - err := b.deploymentConfiger.Deploy() + err := b.deploymentConfiger.Deploy(ctx) if err != nil { b.err = err return b diff --git a/api/stacks/stackbuilders/swarm_file_content_builder.go b/api/stacks/stackbuilders/swarm_file_content_builder.go index c8fa5d82b..fca780742 100644 --- a/api/stacks/stackbuilders/swarm_file_content_builder.go +++ b/api/stacks/stackbuilders/swarm_file_content_builder.go @@ -1,6 +1,7 @@ package stackbuilders import ( + "context" "fmt" "strconv" @@ -64,7 +65,7 @@ func (b *SwarmStackFileContentBuilder) SetFileContent(payload *StackPayload) Fil return b } -func (b *SwarmStackFileContentBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) FileContentMethodStackBuildProcess { +func (b *SwarmStackFileContentBuilder) Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) FileContentMethodStackBuildProcess { if b.hasError() { return b } @@ -78,5 +79,5 @@ func (b *SwarmStackFileContentBuilder) Deploy(payload *StackPayload, endpoint *p b.deploymentConfiger = swarmDeploymentConfig b.stack.CreatedBy = b.deploymentConfiger.GetUsername() - return b.FileContentMethodStackBuilder.Deploy(payload, endpoint) + return b.FileContentMethodStackBuilder.Deploy(ctx, payload, endpoint) } diff --git a/api/stacks/stackbuilders/swarm_file_upload_builder.go b/api/stacks/stackbuilders/swarm_file_upload_builder.go index c4ab1f837..60d82f2bc 100644 --- a/api/stacks/stackbuilders/swarm_file_upload_builder.go +++ b/api/stacks/stackbuilders/swarm_file_upload_builder.go @@ -1,6 +1,8 @@ package stackbuilders import ( + "context" + portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/filesystem" @@ -57,7 +59,7 @@ func (b *SwarmStackFileUploadBuilder) SetUploadedFile(payload *StackPayload) Fil return b } -func (b *SwarmStackFileUploadBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) FileUploadMethodStackBuildProcess { +func (b *SwarmStackFileUploadBuilder) Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) FileUploadMethodStackBuildProcess { if b.hasError() { return b } @@ -71,5 +73,5 @@ func (b *SwarmStackFileUploadBuilder) Deploy(payload *StackPayload, endpoint *po b.deploymentConfiger = swarmDeploymentConfig b.stack.CreatedBy = b.deploymentConfiger.GetUsername() - return b.FileUploadMethodStackBuilder.Deploy(payload, endpoint) + return b.FileUploadMethodStackBuilder.Deploy(ctx, payload, endpoint) } diff --git a/api/stacks/stackbuilders/swarm_git_builder.go b/api/stacks/stackbuilders/swarm_git_builder.go index 25167fb21..f8dd2e6e6 100644 --- a/api/stacks/stackbuilders/swarm_git_builder.go +++ b/api/stacks/stackbuilders/swarm_git_builder.go @@ -1,6 +1,8 @@ package stackbuilders import ( + "context" + portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/http/security" @@ -49,13 +51,13 @@ func (b *SwarmStackGitBuilder) SetUniqueInfo(payload *StackPayload) GitMethodSta return b } -func (b *SwarmStackGitBuilder) SetGitRepository(payload *StackPayload) GitMethodStackBuildProcess { - b.GitMethodStackBuilder.SetGitRepository(payload) +func (b *SwarmStackGitBuilder) SetGitRepository(ctx context.Context, payload *StackPayload) GitMethodStackBuildProcess { + b.GitMethodStackBuilder.SetGitRepository(ctx, payload) return b } // Deploy creates deployment configuration for swarm stack -func (b *SwarmStackGitBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) GitMethodStackBuildProcess { +func (b *SwarmStackGitBuilder) Deploy(ctx context.Context, payload *StackPayload, endpoint *portainer.Endpoint) GitMethodStackBuildProcess { if b.hasError() { return b } @@ -69,7 +71,7 @@ func (b *SwarmStackGitBuilder) Deploy(payload *StackPayload, endpoint *portainer b.deploymentConfiger = swarmDeploymentConfig b.stack.CreatedBy = b.deploymentConfiger.GetUsername() - return b.GitMethodStackBuilder.Deploy(payload, endpoint) + return b.GitMethodStackBuilder.Deploy(ctx, payload, endpoint) } func (b *SwarmStackGitBuilder) SetAutoUpdate(payload *StackPayload) GitMethodStackBuildProcess { diff --git a/api/stacks/stackutils/gitops.go b/api/stacks/stackutils/gitops.go index 30011c62d..3cff71f3a 100644 --- a/api/stacks/stackutils/gitops.go +++ b/api/stacks/stackutils/gitops.go @@ -1,6 +1,7 @@ package stackutils import ( + "context" "fmt" "github.com/pkg/errors" @@ -16,7 +17,7 @@ var ( // DownloadGitRepository downloads the target git repository on the disk // The first return value represents the commit hash of the downloaded git repository -func DownloadGitRepository(config gittypes.RepoConfig, gitService portainer.GitService, getProjectPath func() string) (string, error) { +func DownloadGitRepository(ctx context.Context, config gittypes.RepoConfig, gitService portainer.GitService, getProjectPath func() string) (string, error) { username := "" password := "" if config.Authentication != nil { @@ -26,6 +27,7 @@ func DownloadGitRepository(config gittypes.RepoConfig, gitService portainer.GitS projectPath := getProjectPath() err := gitService.CloneRepository( + ctx, projectPath, config.URL, config.ReferenceName, @@ -44,6 +46,7 @@ func DownloadGitRepository(config gittypes.RepoConfig, gitService portainer.GitS } commitID, err := gitService.LatestCommitID( + ctx, config.URL, config.ReferenceName, username, diff --git a/pkg/libkubectl/apply_dynamic_test.go b/pkg/libkubectl/apply_dynamic_test.go index 00d54be5e..2b173b20a 100644 --- a/pkg/libkubectl/apply_dynamic_test.go +++ b/pkg/libkubectl/apply_dynamic_test.go @@ -1,7 +1,6 @@ package libkubectl import ( - "context" "os" "strings" "testing" @@ -546,14 +545,14 @@ data: // This runs even if the test fails, ensuring cluster stays clean if !tt.wantErr && len(tt.manifests) > 0 { t.Cleanup(func() { - _, err := client.DeleteDynamic(context.Background(), tt.manifests) + _, err := client.DeleteDynamic(t.Context(), tt.manifests) if err != nil { t.Logf("Warning: failed to cleanup resources: %v", err) } }) } - output, err := client.ApplyDynamic(context.Background(), tt.manifests) + output, err := client.ApplyDynamic(t.Context(), tt.manifests) if tt.wantErr { if err == nil { @@ -683,7 +682,7 @@ metadata: for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := client.applyResource(context.Background(), dynamicClient, mapper, []byte(tt.yaml), tt.configuredNS) + result, err := client.applyResource(t.Context(), dynamicClient, mapper, []byte(tt.yaml), tt.configuredNS) if tt.wantErr { if err == nil { diff --git a/pkg/libkubectl/apply_test.go b/pkg/libkubectl/apply_test.go index 2afad6c6a..44b4a0a5e 100644 --- a/pkg/libkubectl/apply_test.go +++ b/pkg/libkubectl/apply_test.go @@ -1,7 +1,6 @@ package libkubectl import ( - "context" "os" "path/filepath" "runtime" @@ -34,7 +33,6 @@ data: } manifests := []string{manifestFile} - ctx := context.Background() // Force GC and measure memory before benchmark runtime.GC() @@ -42,7 +40,7 @@ data: runtime.ReadMemStats(&memBefore) for b.Loop() { - _, err := client.Apply(ctx, manifests) + _, err := client.Apply(b.Context(), manifests) if err != nil { b.Errorf("Failed to apply manifests: %v", err) } @@ -79,7 +77,7 @@ data: key: value`, } - ctx := context.Background() + ctx := b.Context() // Force GC and measure memory before benchmark runtime.GC() diff --git a/pkg/libkubectl/delete_dynamic_test.go b/pkg/libkubectl/delete_dynamic_test.go index 47c8819a6..93deeae16 100644 --- a/pkg/libkubectl/delete_dynamic_test.go +++ b/pkg/libkubectl/delete_dynamic_test.go @@ -1,7 +1,6 @@ package libkubectl import ( - "context" "testing" "github.com/stretchr/testify/require" @@ -141,12 +140,12 @@ data: if shouldCreate { // Create the resources first - _, err := client.ApplyDynamic(context.Background(), tt.manifests) + _, err := client.ApplyDynamic(t.Context(), tt.manifests) require.NoError(t, err, "Failed to create resources for deletion test") } // Test DeleteDynamic - output, err := client.DeleteDynamic(context.Background(), tt.manifests) + output, err := client.DeleteDynamic(t.Context(), tt.manifests) if tt.wantErr { require.Error(t, err, "DeleteDynamic() expected error but got none") @@ -177,15 +176,15 @@ data: require.NoError(t, err, "Failed to create client") // Create the resource - _, err = client.ApplyDynamic(context.Background(), manifest) + _, err = client.ApplyDynamic(t.Context(), manifest) require.NoError(t, err, "Failed to create resource") // Delete it once - _, err = client.DeleteDynamic(context.Background(), manifest) + _, err = client.DeleteDynamic(t.Context(), manifest) require.NoError(t, err, "First DeleteDynamic() failed") // Delete it again (should succeed since DeleteDynamic ignores not found errors) - _, err = client.DeleteDynamic(context.Background(), manifest) + _, err = client.DeleteDynamic(t.Context(), manifest) require.NoError(t, err, "Second DeleteDynamic() should not return error for already deleted resource") } @@ -207,11 +206,11 @@ data: key: value`, } - _, err = client.ApplyDynamic(context.Background(), validManifest) + _, err = client.ApplyDynamic(t.Context(), validManifest) require.NoError(t, err, "Failed to create resource") t.Cleanup(func() { - _, err = client.DeleteDynamic(context.Background(), validManifest) + _, err = client.DeleteDynamic(t.Context(), validManifest) require.NoError(t, err, "Cleanup DeleteDynamic() failed") }) @@ -230,6 +229,6 @@ metadata: namespace: default`, } - _, err = client.DeleteDynamic(context.Background(), mixedManifests) + _, err = client.DeleteDynamic(t.Context(), mixedManifests) require.NoError(t, err, "DeleteDynamic() should handle non-existent resources gracefully") } diff --git a/pkg/libkubectl/delete_test.go b/pkg/libkubectl/delete_test.go index f90413431..81a550992 100644 --- a/pkg/libkubectl/delete_test.go +++ b/pkg/libkubectl/delete_test.go @@ -1,7 +1,6 @@ package libkubectl import ( - "context" "os" "path/filepath" "runtime" @@ -29,10 +28,8 @@ data: key: value`, } - ctx := context.Background() - // Apply the manifest once BEFORE the benchmark loop - _, err = client.ApplyDynamic(ctx, manifests) + _, err = client.ApplyDynamic(b.Context(), manifests) if err != nil { b.Fatalf("Failed to apply initial manifest: %v", err) } @@ -45,14 +42,14 @@ data: for b.Loop() { // Recreate the resource before each delete to ensure it always exists b.StopTimer() - _, err = client.ApplyDynamic(ctx, manifests) + _, err = client.ApplyDynamic(b.Context(), manifests) if err != nil { b.Fatalf("Failed to apply manifest: %v", err) } b.StartTimer() // Delete the resource (always exists) - _, err = client.DeleteDynamic(ctx, manifests) + _, err = client.DeleteDynamic(b.Context(), manifests) if err != nil { b.Errorf("Failed to delete dynamic manifests: %v", err) } @@ -99,10 +96,9 @@ data: manifests := []string{manifestFile} manifestsInline := []string{manifestContent} - ctx := context.Background() // Apply the manifest once BEFORE the benchmark loop using ApplyDynamic - _, err = client.ApplyDynamic(ctx, manifestsInline) + _, err = client.ApplyDynamic(b.Context(), manifestsInline) if err != nil { b.Fatalf("Failed to apply initial manifest: %v", err) } @@ -115,14 +111,14 @@ data: for b.Loop() { // Recreate the resource before each delete to ensure it always exists b.StopTimer() - _, err = client.ApplyDynamic(ctx, manifestsInline) + _, err = client.ApplyDynamic(b.Context(), manifestsInline) if err != nil { b.Fatalf("Failed to apply manifest: %v", err) } b.StartTimer() // Delete the resource using Delete (file-based, always exists) - _, err = client.Delete(ctx, manifests) + _, err = client.Delete(b.Context(), manifests) if err != nil { b.Errorf("Failed to delete manifests: %v", err) } diff --git a/pkg/libstack/compose/compose_test.go b/pkg/libstack/compose/compose_test.go index d5159f39b..bab682279 100644 --- a/pkg/libstack/compose/compose_test.go +++ b/pkg/libstack/compose/compose_test.go @@ -1,7 +1,6 @@ package compose_test import ( - "context" "log" "os" "os/exec" @@ -53,10 +52,9 @@ func Test_UpAndDown(t *testing.T) { t.Fatal(err) } - ctx := context.Background() projectName := "binarytest" - err = deployer.Deploy(ctx, []string{filePathOriginal, filePathOverride}, libstack.DeployOptions{ + err = deployer.Deploy(t.Context(), []string{filePathOriginal, filePathOverride}, libstack.DeployOptions{ Options: libstack.Options{ ProjectName: projectName, }, @@ -69,7 +67,7 @@ func Test_UpAndDown(t *testing.T) { t.Fatal("container should exist") } - err = deployer.Remove(ctx, projectName, []string{filePathOriginal, filePathOverride}, libstack.RemoveOptions{}) + err = deployer.Remove(t.Context(), projectName, []string{filePathOriginal, filePathOverride}, libstack.RemoveOptions{}) if err != nil { t.Fatal(err) } diff --git a/pkg/libstack/compose/composeplugin_test.go b/pkg/libstack/compose/composeplugin_test.go index 8cc289393..70759419f 100644 --- a/pkg/libstack/compose/composeplugin_test.go +++ b/pkg/libstack/compose/composeplugin_test.go @@ -53,17 +53,15 @@ services: filePaths := []string{filePathOriginal, filePathOverride} - ctx := context.Background() - - err := w.Validate(ctx, filePaths, libstack.Options{ProjectName: projectName}) + err := w.Validate(t.Context(), filePaths, libstack.Options{ProjectName: projectName}) require.NoError(t, err) - err = w.Pull(ctx, filePaths, libstack.Options{ProjectName: projectName}) + err = w.Pull(t.Context(), filePaths, libstack.Options{ProjectName: projectName}) require.NoError(t, err) require.False(t, containerExists(composeContainerName)) - err = w.Deploy(ctx, filePaths, libstack.DeployOptions{ + err = w.Deploy(t.Context(), filePaths, libstack.DeployOptions{ Options: libstack.Options{ ProjectName: projectName, }, @@ -72,12 +70,12 @@ services: require.True(t, containerExists(composeContainerName)) - waitResult := w.WaitForStatus(ctx, projectName, libstack.StatusCompleted) + waitResult := w.WaitForStatus(t.Context(), projectName, libstack.StatusCompleted) require.Empty(t, waitResult.ErrorMsg) require.Equal(t, libstack.StatusCompleted, waitResult.Status) - err = w.Remove(ctx, projectName, filePaths, libstack.RemoveOptions{}) + err = w.Remove(t.Context(), projectName, filePaths, libstack.RemoveOptions{}) require.NoError(t, err) require.False(t, containerExists(composeContainerName)) @@ -108,20 +106,19 @@ configs: w := NewComposeDeployer() dir := t.TempDir() - ctx := context.Background() filePath := createFile(t, dir, "docker-compose.yml", content) filePaths := []string{filePath} - err := w.Validate(ctx, filePaths, libstack.Options{ProjectName: projectName}) + err := w.Validate(t.Context(), filePaths, libstack.Options{ProjectName: projectName}) require.NoError(t, err) - err = w.Pull(ctx, filePaths, libstack.Options{ProjectName: projectName}) + err = w.Pull(t.Context(), filePaths, libstack.Options{ProjectName: projectName}) require.NoError(t, err) require.False(t, containerExists(containerName)) - err = w.Deploy(ctx, filePaths, libstack.DeployOptions{ + err = w.Deploy(t.Context(), filePaths, libstack.DeployOptions{ Options: libstack.Options{ ProjectName: projectName, }, @@ -130,12 +127,12 @@ configs: require.True(t, containerExists(containerName)) - waitResult := w.WaitForStatus(ctx, projectName, libstack.StatusCompleted) + waitResult := w.WaitForStatus(t.Context(), projectName, libstack.StatusCompleted) require.Empty(t, waitResult.ErrorMsg) require.Equal(t, libstack.StatusCompleted, waitResult.Status) - err = w.Remove(ctx, projectName, filePaths, libstack.RemoveOptions{}) + err = w.Remove(t.Context(), projectName, filePaths, libstack.RemoveOptions{}) require.NoError(t, err) require.False(t, containerExists(containerName)) @@ -154,7 +151,7 @@ services: filePaths := []string{filePath} serviceName := "updater" - err := w.Run(context.Background(), filePaths, serviceName, libstack.RunOptions{ + err := w.Run(t.Context(), filePaths, serviceName, libstack.RunOptions{ Remove: true, Options: libstack.Options{ ProjectName: "project_name", @@ -196,14 +193,11 @@ func Test_Validate(t *testing.T) { projectName := "plugintest" - ctx := context.Background() - - err := w.Validate(ctx, filePaths, libstack.Options{ProjectName: projectName}) + err := w.Validate(t.Context(), filePaths, libstack.Options{ProjectName: projectName}) require.Error(t, err) } func Test_Config(t *testing.T) { - ctx := context.Background() dir := t.TempDir() projectName := "configtest" @@ -411,7 +405,7 @@ networks: } w := NewComposeDeployer() - actual, err := w.Config(ctx, []string{composeFilePath}, libstack.Options{ + actual, err := w.Config(t.Context(), []string{composeFilePath}, libstack.Options{ WorkingDir: dir, ProjectName: projectName, EnvFilePath: envFilePath, @@ -451,8 +445,6 @@ func Test_DeployWithRemoveOrphans(t *testing.T) { filepaths := []string{composeFilepath} modifiedFilepaths := []string{modifiedComposeFilepath} - ctx := context.Background() - testCases := []struct { name string options libstack.DeployOptions @@ -491,20 +483,20 @@ func Test_DeployWithRemoveOrphans(t *testing.T) { t.Run(tc.name, func(t *testing.T) { options := tc.options.Options - err := w.Validate(ctx, filepaths, options) + err := w.Validate(t.Context(), filepaths, options) require.NoError(t, err) - err = w.Pull(ctx, filepaths, options) + err = w.Pull(t.Context(), filepaths, options) require.NoError(t, err) require.False(t, containerExists(service1ContainerName)) require.False(t, containerExists(service2ContainerName)) - err = w.Deploy(ctx, filepaths, tc.options) + err = w.Deploy(t.Context(), filepaths, tc.options) require.NoError(t, err) defer func() { - err = w.Remove(ctx, projectName, filepaths, libstack.RemoveOptions{}) + err = w.Remove(t.Context(), projectName, filepaths, libstack.RemoveOptions{}) require.NoError(t, err) require.False(t, containerExists(service1ContainerName)) @@ -514,27 +506,27 @@ func Test_DeployWithRemoveOrphans(t *testing.T) { require.True(t, containerExists(service1ContainerName)) require.True(t, containerExists(service2ContainerName)) - waitResult := w.WaitForStatus(ctx, projectName, libstack.StatusCompleted) + waitResult := w.WaitForStatus(t.Context(), projectName, libstack.StatusCompleted) require.Empty(t, waitResult.ErrorMsg) require.Equal(t, libstack.StatusCompleted, waitResult.Status) - err = w.Validate(ctx, modifiedFilepaths, options) + err = w.Validate(t.Context(), modifiedFilepaths, options) require.NoError(t, err) - err = w.Pull(ctx, modifiedFilepaths, options) + err = w.Pull(t.Context(), modifiedFilepaths, options) require.NoError(t, err) require.True(t, containerExists(service1ContainerName)) require.True(t, containerExists(service2ContainerName)) - err = w.Deploy(ctx, modifiedFilepaths, tc.options) + err = w.Deploy(t.Context(), modifiedFilepaths, tc.options) require.NoError(t, err) require.False(t, containerExists(service1ContainerName)) require.True(t, containerExists(service2ContainerName)) - waitResult = w.WaitForStatus(ctx, projectName, libstack.StatusCompleted) + waitResult = w.WaitForStatus(t.Context(), projectName, libstack.StatusCompleted) require.Empty(t, waitResult.ErrorMsg) require.Equal(t, libstack.StatusCompleted, waitResult.Status) @@ -591,22 +583,20 @@ func Test_DeployWithIgnoreOrphans(t *testing.T) { Env: []string{cmdcompose.ComposeIgnoreOrphans + "=true"}, } - ctx := context.Background() - - err := w.Validate(ctx, filepaths, options) + err := w.Validate(t.Context(), filepaths, options) require.NoError(t, err) - err = w.Pull(ctx, filepaths, options) + err = w.Pull(t.Context(), filepaths, options) require.NoError(t, err) require.False(t, containerExists(service1ContainerName)) require.False(t, containerExists(service2ContainerName)) - err = w.Deploy(ctx, filepaths, libstack.DeployOptions{Options: options}) + err = w.Deploy(t.Context(), filepaths, libstack.DeployOptions{Options: options}) require.NoError(t, err) defer func() { - err = w.Remove(ctx, projectName, filepaths, libstack.RemoveOptions{}) + err = w.Remove(t.Context(), projectName, filepaths, libstack.RemoveOptions{}) require.NoError(t, err) require.False(t, containerExists(service1ContainerName)) @@ -616,27 +606,27 @@ func Test_DeployWithIgnoreOrphans(t *testing.T) { require.True(t, containerExists(service1ContainerName)) require.True(t, containerExists(service2ContainerName)) - waitResult := w.WaitForStatus(ctx, projectName, libstack.StatusCompleted) + waitResult := w.WaitForStatus(t.Context(), projectName, libstack.StatusCompleted) require.Empty(t, waitResult.ErrorMsg) require.Equal(t, libstack.StatusCompleted, waitResult.Status) - err = w.Validate(ctx, modifiedFilepaths, options) + err = w.Validate(t.Context(), modifiedFilepaths, options) require.NoError(t, err) - err = w.Pull(ctx, modifiedFilepaths, options) + err = w.Pull(t.Context(), modifiedFilepaths, options) require.NoError(t, err) require.True(t, containerExists(service1ContainerName)) require.True(t, containerExists(service2ContainerName)) - err = w.Deploy(ctx, modifiedFilepaths, libstack.DeployOptions{Options: options}) + err = w.Deploy(t.Context(), modifiedFilepaths, libstack.DeployOptions{Options: options}) require.NoError(t, err) require.True(t, containerExists(service1ContainerName)) require.True(t, containerExists(service2ContainerName)) - waitResult = w.WaitForStatus(ctx, projectName, libstack.StatusCompleted) + waitResult = w.WaitForStatus(t.Context(), projectName, libstack.StatusCompleted) require.Empty(t, waitResult.ErrorMsg) require.Equal(t, libstack.StatusCompleted, waitResult.Status) @@ -668,12 +658,10 @@ func Test_MaxConcurrency(t *testing.T) { Env: []string{cmdcompose.ComposeParallelLimit + "=" + strconv.Itoa(expectedMaxConcurrency)}, } - ctx := context.Background() - - err := w.Validate(ctx, filepaths, options) + err := w.Validate(t.Context(), filepaths, options) require.NoError(t, err) - err = w.withComposeService(ctx, filepaths, options, func(service api.Compose, _ *types.Project) error { + err = w.withComposeService(t.Context(), filepaths, options, func(service api.Compose, _ *types.Project) error { if mockS, ok := service.(*mockComposeService); ok { require.Equal(t, expectedMaxConcurrency, mockS.maxConcurrency) } else { @@ -685,7 +673,6 @@ func Test_MaxConcurrency(t *testing.T) { } func Test_createProject(t *testing.T) { - ctx := context.Background() dir := t.TempDir() projectName := "create-project-test" @@ -1364,7 +1351,7 @@ func Test_createProject(t *testing.T) { t.Setenv(k, v) } - gotProject, err := createProject(ctx, tc.configFilepaths, tc.options) + gotProject, err := createProject(t.Context(), tc.configFilepaths, tc.options) if err != nil { t.Fatalf("Failed to create new project: %v", err) } @@ -1377,7 +1364,6 @@ func Test_createProject(t *testing.T) { } func Test_CredentialsStore_Behavior(t *testing.T) { - ctx := context.Background() // Create a temporary Docker config with a credsStore set (simulating Docker Desktop) tmpDir := t.TempDir() @@ -1397,7 +1383,7 @@ func Test_CredentialsStore_Behavior(t *testing.T) { var capturedCredsStore string var capturedAuthConfigs map[string]configtypes.AuthConfig - err = withCli(ctx, libstack.Options{}, func(ctx context.Context, cli *command.DockerCli) error { + err = withCli(t.Context(), libstack.Options{}, func(ctx context.Context, cli *command.DockerCli) error { // Capture the state after withCli sets up credentials capturedCredsStore = cli.ConfigFile().CredentialsStore capturedAuthConfigs = cli.ConfigFile().AuthConfigs @@ -1429,7 +1415,7 @@ func Test_CredentialsStore_Behavior(t *testing.T) { var capturedCredsStore string var capturedAuthConfigs map[string]configtypes.AuthConfig - err = withCli(ctx, libstack.Options{Registries: registries}, func(ctx context.Context, cli *command.DockerCli) error { + err = withCli(t.Context(), libstack.Options{Registries: registries}, func(ctx context.Context, cli *command.DockerCli) error { // Capture the state after withCli sets up credentials capturedCredsStore = cli.ConfigFile().CredentialsStore capturedAuthConfigs = cli.ConfigFile().AuthConfigs diff --git a/pkg/libstack/compose/composeplugin_windows_test.go b/pkg/libstack/compose/composeplugin_windows_test.go index c580d6035..58cad3301 100644 --- a/pkg/libstack/compose/composeplugin_windows_test.go +++ b/pkg/libstack/compose/composeplugin_windows_test.go @@ -1,7 +1,6 @@ package compose import ( - "context" "errors" "os" "testing" @@ -12,7 +11,6 @@ import ( ) func Test_createProject_win(t *testing.T) { - ctx := context.Background() dir := t.TempDir() projectName := "create-project-test" @@ -95,7 +93,7 @@ func Test_createProject_win(t *testing.T) { } }() - gotProject, err := createProject(ctx, tc.configFilepaths, tc.options) + gotProject, err := createProject(t.Context(), tc.configFilepaths, tc.options) if err != nil { t.Fatalf("Failed to create new project: %v", err) } diff --git a/pkg/libstack/compose/status_integration_test.go b/pkg/libstack/compose/status_integration_test.go index ad38914d2..5d99fe0e9 100644 --- a/pkg/libstack/compose/status_integration_test.go +++ b/pkg/libstack/compose/status_integration_test.go @@ -50,12 +50,11 @@ func TestComposeProjectStatus(t *testing.T) { } w := NewComposeDeployer() - ctx := context.Background() for _, testCase := range testCases { t.Run(testCase.TestName, func(t *testing.T) { projectName := testCase.TestName - err := w.Deploy(ctx, []string{testCase.ComposeFile}, libstack.DeployOptions{ + err := w.Deploy(t.Context(), []string{testCase.ComposeFile}, libstack.DeployOptions{ Options: libstack.Options{ ProjectName: projectName, }, @@ -66,7 +65,7 @@ func TestComposeProjectStatus(t *testing.T) { time.Sleep(5 * time.Second) - status, statusMessage, err := waitForStatus(w, ctx, projectName, libstack.StatusRunning) + status, statusMessage, err := waitForStatus(w, t.Context(), projectName, libstack.StatusRunning) if err != nil { t.Fatalf("[test: %s] Failed to get compose project status: %v", testCase.TestName, err) } @@ -79,14 +78,14 @@ func TestComposeProjectStatus(t *testing.T) { t.Fatalf("[test: %s] Expected status message but got empty", testCase.TestName) } - err = w.Remove(ctx, projectName, nil, libstack.RemoveOptions{}) + err = w.Remove(t.Context(), projectName, nil, libstack.RemoveOptions{}) if err != nil { t.Fatalf("[test: %s] Failed to remove compose project: %v", testCase.TestName, err) } time.Sleep(20 * time.Second) - status, statusMessage, err = waitForStatus(w, ctx, projectName, libstack.StatusRemoved) + status, statusMessage, err = waitForStatus(w, t.Context(), projectName, libstack.StatusRemoved) if err != nil { t.Fatalf("[test: %s] Failed to get compose project status: %v", testCase.TestName, err) } diff --git a/pkg/snapshot/kubernetes_test.go b/pkg/snapshot/kubernetes_test.go index 6cd39169e..3f6e46f53 100644 --- a/pkg/snapshot/kubernetes_test.go +++ b/pkg/snapshot/kubernetes_test.go @@ -1,7 +1,6 @@ package snapshot import ( - "context" "errors" "testing" @@ -59,11 +58,11 @@ func TestKubernetesSnapshotNodes(t *testing.T) { } // Add nodes to fake client - _, err := fakeClient.CoreV1().Nodes().Create(context.TODO(), node1, metav1.CreateOptions{}) + _, err := fakeClient.CoreV1().Nodes().Create(t.Context(), node1, metav1.CreateOptions{}) require.NoError(t, err) - _, err = fakeClient.CoreV1().Nodes().Create(context.TODO(), node2, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Nodes().Create(t.Context(), node2, metav1.CreateOptions{}) require.NoError(t, err) - _, err = fakeClient.CoreV1().Nodes().Create(context.TODO(), node3, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Nodes().Create(t.Context(), node3, metav1.CreateOptions{}) require.NoError(t, err) snapshot := &portainer.KubernetesSnapshot{} @@ -129,9 +128,9 @@ func TestCreateKubernetesSnapshotIntegration(t *testing.T) { } // Add nodes to fake client - _, err := fakeClient.CoreV1().Nodes().Create(context.TODO(), node1, metav1.CreateOptions{}) + _, err := fakeClient.CoreV1().Nodes().Create(t.Context(), node1, metav1.CreateOptions{}) require.NoError(t, err) - _, err = fakeClient.CoreV1().Nodes().Create(context.TODO(), node2, metav1.CreateOptions{}) + _, err = fakeClient.CoreV1().Nodes().Create(t.Context(), node2, metav1.CreateOptions{}) require.NoError(t, err) // Test that kubernetesSnapshotVersion would work @@ -199,7 +198,7 @@ func TestKubernetesSnapshotNodesSingleNode(t *testing.T) { }, } - _, err := fakeClient.CoreV1().Nodes().Create(context.TODO(), node, metav1.CreateOptions{}) + _, err := fakeClient.CoreV1().Nodes().Create(t.Context(), node, metav1.CreateOptions{}) require.NoError(t, err) snapshot := &portainer.KubernetesSnapshot{} @@ -231,7 +230,7 @@ func TestKubernetesSnapshotNodesZeroResources(t *testing.T) { }, } - _, err := fakeClient.CoreV1().Nodes().Create(context.TODO(), node, metav1.CreateOptions{}) + _, err := fakeClient.CoreV1().Nodes().Create(t.Context(), node, metav1.CreateOptions{}) require.NoError(t, err) snapshot := &portainer.KubernetesSnapshot{}