Compare commits

..

1 Commits

Author SHA1 Message Date
Steven Kang cb84cb73ab chore: version bump 2.33.3 (#1351) 2025-10-30 11:56:33 +13:00
96 changed files with 159 additions and 408 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ plugins:
- import
parserOptions:
ecmaVersion: latest
ecmaVersion: 2018
sourceType: module
project: './tsconfig.json'
ecmaFeatures:
@@ -615,7 +615,7 @@
"RequiredPasswordLength": 12
},
"KubeconfigExpiry": "0",
"KubectlShellImage": "portainer/kubectl-shell:2.33.6",
"KubectlShellImage": "portainer/kubectl-shell:2.33.3",
"LDAPSettings": {
"AnonymousMode": true,
"AutoCreateUsers": true,
@@ -944,7 +944,7 @@
}
],
"version": {
"VERSION": "{\"SchemaVersion\":\"2.33.6\",\"MigratorCount\":0,\"Edition\":1,\"InstanceID\":\"463d5c47-0ea5-4aca-85b1-405ceefee254\"}"
"VERSION": "{\"SchemaVersion\":\"2.33.3\",\"MigratorCount\":0,\"Edition\":1,\"InstanceID\":\"463d5c47-0ea5-4aca-85b1-405ceefee254\"}"
},
"webhooks": null
}
@@ -74,7 +74,7 @@ func (handler *Handler) edgeStackStatusUpdate(w http.ResponseWriter, r *http.Req
}
if err := handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint); err != nil {
return httperror.Forbidden("Permission denied to access environment", fmt.Errorf("unauthorized edge endpoint operation: %w. Environment ID: %d", err, payload.EndpointID))
return httperror.Forbidden("Permission denied to access environment", fmt.Errorf("unauthorized edge endpoint operation: %w. Environment name: %s", err, endpoint.Name))
}
var stack *portainer.EdgeStack
@@ -42,17 +42,17 @@ func (handler *Handler) endpointEdgeJobsLogs(w http.ResponseWriter, r *http.Requ
}
if err := handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint); err != nil {
return httperror.Forbidden("Permission denied to access environment", fmt.Errorf("unauthorized edge endpoint operation: %w. Environment ID: %d", err, endpoint.ID))
return httperror.Forbidden("Permission denied to access environment", fmt.Errorf("unauthorized edge endpoint operation: %w. Environment name: %s", err, endpoint.Name))
}
edgeJobID, err := request.RetrieveNumericRouteVariableValue(r, "jobID")
if err != nil {
return httperror.BadRequest("Invalid edge job identifier route variable", fmt.Errorf("invalid Edge job route variable: %w. Environment ID: %d", err, endpoint.ID))
return httperror.BadRequest("Invalid edge job identifier route variable", fmt.Errorf("invalid Edge job route variable: %w. Environment name: %s", err, endpoint.Name))
}
var payload logsPayload
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
return httperror.BadRequest("Invalid request payload", fmt.Errorf("invalid Edge job request payload: %w. Environment ID: %d", err, endpoint.ID))
return httperror.BadRequest("Invalid request payload", fmt.Errorf("invalid Edge job request payload: %w. Environment name: %s", err, endpoint.Name))
}
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
@@ -60,11 +60,11 @@ func (handler *Handler) endpointEdgeJobsLogs(w http.ResponseWriter, r *http.Requ
}); err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
httpErr.Err = fmt.Errorf("edge polling error: %w. Environment ID: %d", httpErr.Err, endpoint.ID)
httpErr.Err = fmt.Errorf("edge polling error: %w. Environment name: %s", httpErr.Err, endpoint.Name)
return httpErr
}
return httperror.InternalServerError("Unexpected error", fmt.Errorf("edge polling error: %w. Environment ID: %d", err, endpoint.ID))
return httperror.InternalServerError("Unexpected error", fmt.Errorf("edge polling error: %w. Environment name: %s", err, endpoint.Name))
}
return response.JSON(w, nil)
@@ -40,18 +40,18 @@ func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http.
}
if err := handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint); err != nil {
return httperror.Forbidden("Permission denied to access environment", fmt.Errorf("unauthorized edge endpoint operation: %w. Environment ID: %d", err, endpoint.ID))
return httperror.Forbidden("Permission denied to access environment", fmt.Errorf("unauthorized edge endpoint operation: %w. Environment name: %s", err, endpoint.Name))
}
edgeStackID, err := request.RetrieveNumericRouteVariableValue(r, "stackId")
if err != nil {
return httperror.BadRequest("Invalid edge stack identifier route variable", fmt.Errorf("invalid Edge stack route variable: %w. Environment ID: %d", err, endpoint.ID))
return httperror.BadRequest("Invalid edge stack identifier route variable", fmt.Errorf("invalid Edge stack route variable: %w. Environment name: %s", err, endpoint.Name))
}
s, err, _ := edgeStackSingleFlightGroup.Do(strconv.Itoa(edgeStackID), func() (any, error) {
edgeStack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(edgeStackID))
if handler.DataStore.IsErrObjectNotFound(err) {
return nil, httperror.NotFound("Unable to find an edge stack with the specified identifier inside the database", fmt.Errorf("unable to find the Edge stack from database: %w. Environment ID: %d", err, endpoint.ID))
return nil, httperror.NotFound("Unable to find an edge stack with the specified identifier inside the database", fmt.Errorf("unable to find the Edge stack from database: %w. Environment name: %s", err, endpoint.Name))
}
return edgeStack, err
@@ -62,7 +62,7 @@ func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http.
return httpErr
}
return httperror.InternalServerError("Unable to find an edge stack with the specified identifier inside the database", fmt.Errorf("failed to find Edge stack from the database: %w. Environment ID: %d", err, endpoint.ID))
return httperror.InternalServerError("Unable to find an edge stack with the specified identifier inside the database", fmt.Errorf("failed to find Edge stack from the database: %w. Environment name: %s", err, endpoint.Name))
}
// WARNING: this variable must not be mutated
@@ -71,7 +71,7 @@ func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http.
fileName := edgeStack.EntryPoint
if endpointutils.IsDockerEndpoint(endpoint) {
if fileName == "" {
return httperror.BadRequest("Docker is not supported by this stack", fmt.Errorf("no filename is provided for the Docker endpoint. Environment ID: %d", endpoint.ID))
return httperror.BadRequest("Docker is not supported by this stack", fmt.Errorf("no filename is provided for the Docker endpoint. Environment name: %s", endpoint.Name))
}
}
@@ -84,18 +84,18 @@ func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http.
fileName = edgeStack.ManifestPath
if fileName == "" {
return httperror.BadRequest("Kubernetes is not supported by this stack", fmt.Errorf("no filename is provided for the Kubernetes endpoint. Environment ID: %d", endpoint.ID))
return httperror.BadRequest("Kubernetes is not supported by this stack", fmt.Errorf("no filename is provided for the Kubernetes endpoint. Environment name: %s", endpoint.Name))
}
}
dirEntries, err := filesystem.LoadDir(edgeStack.ProjectPath)
if err != nil {
return httperror.InternalServerError("Unable to load repository", fmt.Errorf("failed to load project directory: %w. Environment ID: %d", err, endpoint.ID))
return httperror.InternalServerError("Unable to load repository", fmt.Errorf("failed to load project directory: %w. Environment name: %s", err, endpoint.Name))
}
fileContent, err := filesystem.FilterDirForCompatibility(dirEntries, fileName, endpoint.Agent.Version)
if err != nil {
return httperror.InternalServerError("File not found", fmt.Errorf("unable to find file: %w. Environment ID: %d", err, endpoint.ID))
return httperror.InternalServerError("File not found", fmt.Errorf("unable to find file: %w. Environment name: %s", err, endpoint.Name))
}
dirEntries = filesystem.FilterDirForEntryFile(dirEntries, fileName)
@@ -97,13 +97,13 @@ func (handler *Handler) endpointEdgeStatusInspect(w http.ResponseWriter, r *http
firstConn := endpoint.LastCheckInDate == 0
if err := handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint); err != nil {
return httperror.Forbidden("Permission denied to access environment. The device has not been trusted yet", fmt.Errorf("unauthorized Edge endpoint operation: %w. Environment ID: %d", err, endpoint.ID))
return httperror.Forbidden("Permission denied to access environment. The device has not been trusted yet", fmt.Errorf("unauthorized Edge endpoint operation: %w. Environment name: %s", err, endpoint.Name))
}
handler.DataStore.Endpoint().UpdateHeartbeat(endpoint.ID)
if err := handler.requestBouncer.TrustedEdgeEnvironmentAccess(handler.DataStore, endpoint); err != nil {
return httperror.Forbidden("Permission denied to access environment. The device has not been trusted yet", fmt.Errorf("untrusted Edge environment access: %w. Environment ID: %d", err, endpoint.ID))
return httperror.Forbidden("Permission denied to access environment. The device has not been trusted yet", fmt.Errorf("untrusted Edge environment access: %w. Environment name: %s", err, endpoint.Name))
}
var statusResponse *endpointEdgeStatusInspectResponse
@@ -113,11 +113,11 @@ func (handler *Handler) endpointEdgeStatusInspect(w http.ResponseWriter, r *http
}); err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
httpErr.Err = fmt.Errorf("edge polling error: %w. Environment ID: %d", httpErr.Err, endpoint.ID)
httpErr.Err = fmt.Errorf("edge polling error: %w. Environment name: %s", httpErr.Err, endpoint.Name)
return httpErr
}
return httperror.InternalServerError("Unexpected error", fmt.Errorf("edge polling error: %w. Environment ID: %d", err, endpoint.ID))
return httperror.InternalServerError("Unexpected error", fmt.Errorf("edge polling error: %w. Environment name: %s", err, endpoint.Name))
}
return cacheResponse(w, endpoint.ID, *statusResponse)
@@ -20,6 +20,7 @@ import (
// @produce json
// @param id path int true "Environment(Endpoint) identifier"
// @param excludeSnapshot query bool false "if true, the snapshot data won't be retrieved"
// @param excludeSnapshotRaw query bool false "if true, the SnapshotRaw field won't be retrieved"
// @success 200 {object} portainer.Endpoint "Success"
// @failure 400 "Invalid request"
// @failure 404 "Environment(Endpoint) not found"
@@ -52,9 +53,10 @@ func (handler *Handler) endpointInspect(w http.ResponseWriter, r *http.Request)
endpoint.ComposeSyntaxMaxVersion = handler.ComposeStackManager.ComposeSyntaxMaxVersion()
excludeSnapshot, _ := request.RetrieveBooleanQueryParameter(r, "excludeSnapshot", true)
excludeRaw, _ := request.RetrieveBooleanQueryParameter(r, "excludeSnapshotRaw", true)
if !excludeSnapshot {
if err := handler.SnapshotService.FillSnapshotData(endpoint, false); err != nil {
if err := handler.SnapshotService.FillSnapshotData(endpoint, !excludeRaw); err != nil {
return httperror.InternalServerError("Unable to add snapshot data", err)
}
}
+3 -1
View File
@@ -45,6 +45,7 @@ const (
// @param edgeDeviceUntrusted query bool false "if true, show only untrusted edge agents, if false show only trusted edge agents (relevant only for edge agents)"
// @param edgeCheckInPassedSeconds query number false "if bigger then zero, show only edge agents that checked-in in the last provided seconds (relevant only for edge agents)"
// @param excludeSnapshots query bool false "if true, the snapshot data won't be retrieved"
// @param excludeSnapshotRaw query bool false "if true, the SnapshotRaw field won't be retrieved"
// @param name query string false "will return only environments(endpoints) with this name"
// @param edgeStackId query portainer.EdgeStackID false "will return the environements of the specified edge stack"
// @param edgeStackStatus query string false "only applied when edgeStackId exists. Filter the returned environments based on their deployment status in the stack (not the environment status!)" Enum("Pending", "Ok", "Error", "Acknowledged", "Remove", "RemoteUpdateSuccess", "ImagesPulled")
@@ -62,6 +63,7 @@ func (handler *Handler) endpointList(w http.ResponseWriter, r *http.Request) *ht
limit, _ := request.RetrieveNumericQueryParameter(r, "limit", true)
sortField, _ := request.RetrieveQueryParameter(r, "sort", true)
sortOrder, _ := request.RetrieveQueryParameter(r, "order", true)
excludeRaw, _ := request.RetrieveBooleanQueryParameter(r, "excludeSnapshotRaw", true)
endpointGroups, err := handler.DataStore.EndpointGroup().ReadAll()
if err != nil {
@@ -116,7 +118,7 @@ func (handler *Handler) endpointList(w http.ResponseWriter, r *http.Request) *ht
endpointutils.UpdateEdgeEndpointHeartbeat(&paginatedEndpoints[idx], settings)
if !query.excludeSnapshots {
if err := handler.SnapshotService.FillSnapshotData(&paginatedEndpoints[idx], false); err != nil {
if err := handler.SnapshotService.FillSnapshotData(&paginatedEndpoints[idx], !excludeRaw); err != nil {
return httperror.InternalServerError("Unable to add snapshot data", err)
}
}
+1 -1
View File
@@ -81,7 +81,7 @@ type Handler struct {
}
// @title PortainerCE API
// @version 2.33.6
// @version 2.33.3
// @description.markdown api-description.md
// @termsOfService
+1 -7
View File
@@ -161,13 +161,7 @@ func (handler *Handler) startStack(
return handler.StackDeployer.StartRemoteComposeStack(stack, endpoint, filteredRegistries)
}
options := portainer.ComposeUpOptions{
ComposeOptions: portainer.ComposeOptions{
Registries: filteredRegistries,
},
}
return handler.ComposeStackManager.Up(context.TODO(), stack, endpoint, options)
return handler.ComposeStackManager.Up(context.TODO(), stack, endpoint, portainer.ComposeUpOptions{})
case portainer.DockerSwarmStack:
stack.Name = handler.SwarmStackManager.NormalizeStackName(stack.Name)
+1 -1
View File
@@ -111,7 +111,7 @@ var prefixProxyFuncMap = map[string]func(*Transport, *http.Request, string) (*ht
// ProxyDockerRequest intercepts a Docker API request and apply logic based
// on the requested operation.
func (transport *Transport) ProxyDockerRequest(request *http.Request) (*http.Response, error) {
// from : /v1.44/containers/{id}/json
// from : /v1.41/containers/{id}/json
// or : /containers/{id}/json
// to : /containers/{id}/json
unversionedPath := apiVersionRe.ReplaceAllString(request.URL.Path, "")
+1 -1
View File
@@ -1782,7 +1782,7 @@ type (
const (
// APIVersion is the version number of the Portainer API
APIVersion = "2.33.6"
APIVersion = "2.33.3"
// Support annotation for the API version ("STS" for Short-Term Support or "LTS" for Long-Term Support)
APIVersionSupport = "LTS"
// Edition is what this edition of Portainer is called
+3 -2
View File
@@ -55,11 +55,12 @@ func (d *stackDeployer) DeployRemoteComposeStack(
d.lock.Lock()
defer d.lock.Unlock()
options := portainer.ComposeOptions{Registries: registries}
d.swarmStackManager.Login(registries, endpoint)
defer d.swarmStackManager.Logout(endpoint)
// --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(context.TODO(), stack, endpoint, portainer.ComposeOptions{}); err != nil {
return err
}
}
+3 -1
View File
@@ -117,7 +117,9 @@ div.input-mask {
.widget .widget-body .error {
color: #ff0000;
}
.widget .widget-body button {
margin-left: 5px;
}
.widget .widget-body div.alert {
margin-bottom: 10px;
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { NodeStatus, TaskState } from 'docker-types/generated/1.44';
import { NodeStatus, TaskState } from 'docker-types/generated/1.41';
import _ from 'lodash';
export function trimVersionTag(fullName: string) {
+1 -1
View File
@@ -1,4 +1,4 @@
import { Config } from 'docker-types/generated/1.44';
import { Config } from 'docker-types/generated/1.41';
import { IResource } from '@/react/docker/components/datatable/createOwnershipColumn';
import { PortainerResponse } from '@/react/docker/types';
+1 -1
View File
@@ -1,4 +1,4 @@
import { ImageSummary } from 'docker-types/generated/1.44';
import { ImageSummary } from 'docker-types/generated/1.41';
import { PortainerResponse } from '@/react/docker/types';
+1 -1
View File
@@ -1,4 +1,4 @@
import { ImageInspect } from 'docker-types/generated/1.44';
import { ImageInspect } from 'docker-types/generated/1.41';
type ImageInspectConfig = NonNullable<ImageInspect['Config']>;
+1 -1
View File
@@ -1,4 +1,4 @@
import { IPAM, Network, NetworkContainer } from 'docker-types/generated/1.44';
import { IPAM, Network, NetworkContainer } from 'docker-types/generated/1.41';
import { ResourceControlViewModel } from '@/react/portainer/access-control/models/ResourceControlViewModel';
import { IResource } from '@/react/docker/components/datatable/createOwnershipColumn';
+1 -1
View File
@@ -8,7 +8,7 @@ import {
ObjectVersion,
Platform,
ResourceObject,
} from 'docker-types/generated/1.44';
} from 'docker-types/generated/1.41';
export class NodeViewModel {
Model: Node;
+1 -1
View File
@@ -1,4 +1,4 @@
import { Secret } from 'docker-types/generated/1.44';
import { Secret } from 'docker-types/generated/1.41';
import { ResourceControlViewModel } from '@/react/portainer/access-control/models/ResourceControlViewModel';
import { PortainerResponse } from '@/react/docker/types';
+1 -1
View File
@@ -6,7 +6,7 @@ import {
Service,
ServiceSpec,
TaskSpec,
} from 'docker-types/generated/1.44';
} from 'docker-types/generated/1.41';
import { ResourceControlViewModel } from '@/react/portainer/access-control/models/ResourceControlViewModel';
import { PortainerResponse } from '@/react/docker/types';
+1 -1
View File
@@ -1,4 +1,4 @@
import { Task } from 'docker-types/generated/1.44';
import { Task } from 'docker-types/generated/1.41';
import { DeepPick } from '@/types/deepPick';
+1 -1
View File
@@ -1,4 +1,4 @@
import { Volume } from 'docker-types/generated/1.44';
import { Volume } from 'docker-types/generated/1.41';
import { ResourceControlViewModel } from '@/react/portainer/access-control/models/ResourceControlViewModel';
import { IResource } from '@/react/docker/components/datatable/createOwnershipColumn';
@@ -720,7 +720,7 @@ angular.module('portainer.docker').controller('ServiceController', [
$scope.onResetPorts = function (all = false) {
$scope.$evalAsync(() => {
$scope.formValues.ports = portsMappingUtils.toViewModel($scope.service.Model.Spec.EndpointSpec?.Ports);
$scope.formValues.ports = portsMappingUtils.toViewModel($scope.service.Model.Spec.EndpointSpec.Ports);
$scope.cancelChanges($scope.service, all ? undefined : ['Ports']);
});
@@ -744,7 +744,7 @@ angular.module('portainer.docker').controller('ServiceController', [
$scope.lastVersion = service.Version;
}
$scope.formValues.ports = portsMappingUtils.toViewModel(service.Model.Spec.EndpointSpec?.Ports);
$scope.formValues.ports = portsMappingUtils.toViewModel(service.Model.Spec.EndpointSpec.Ports);
transformResources(service);
translateServiceArrays(service);
@@ -4,4 +4,8 @@ export const MaxDockerAPIVersionKey = 'maxDockerAPIVersion' as const;
export type DockerAPIVersionType = number;
// this is the version we are using with the generated API types
export const MAX_DOCKER_API_VERSION: DockerAPIVersionType = 1.44;
export const MAX_DOCKER_API_VERSION: DockerAPIVersionType = 1.41;
// https://docs.docker.com/engine/api/#api-version-matrix
// Docker 26 = API 1.45
export const LATEST_DOCKER_API_VERSION: DockerAPIVersionType = 1.45;
@@ -1,4 +1,4 @@
import { SystemVersion } from 'docker-types/generated/1.44';
import { SystemVersion } from 'docker-types/generated/1.41';
import Axios, { InternalAxiosRequestConfig } from 'axios';
import { setupCache, buildMemoryStorage } from 'axios-cache-interceptor';
@@ -1,4 +1,4 @@
import { Config } from 'docker-types/generated/1.44';
import { Config } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,4 +1,4 @@
import { Config } from 'docker-types/generated/1.44';
import { Config } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,4 +1,4 @@
import { ConfigSpec } from 'docker-types/generated/1.44';
import { ConfigSpec } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,4 +1,4 @@
import { PortMap } from 'docker-types/generated/1.44';
import { PortMap } from 'docker-types/generated/1.41';
import _ from 'lodash';
import { PortMapping, Protocol, Values } from './PortsMappingField';
@@ -1,4 +1,4 @@
import { PortMap } from 'docker-types/generated/1.44';
import { PortMap } from 'docker-types/generated/1.41';
import _ from 'lodash';
import { Protocol, Values } from './PortsMappingField';
@@ -1,6 +1,6 @@
import { ReactNode } from 'react';
import { mixed } from 'yup';
import { ContainerConfig } from 'docker-types/generated/1.44';
import { ContainerConfig } from 'docker-types/generated/1.41';
import { AutomationTestingProps } from '@/types';
@@ -1,4 +1,4 @@
import { HostConfig } from 'docker-types/generated/1.44';
import { HostConfig } from 'docker-types/generated/1.41';
import { commandArrayToString } from '@/docker/helpers/containers';
@@ -1,82 +0,0 @@
import { describe, it, expect } from 'vitest';
import { CreateContainerRequest } from '../types';
import { toRequest } from './toRequest';
import { Values } from './types';
describe('toRequest', () => {
const mockOldConfig: CreateContainerRequest = {
Hostname: 'old-hostname',
Domainname: 'old-domain',
MacAddress: '02:42:ac:11:00:99',
HostConfig: {
NetworkMode: 'bridge',
Dns: ['1.1.1.1'],
ExtraHosts: [],
},
NetworkingConfig: {
EndpointsConfig: {
bridge: {
Aliases: [],
},
},
},
};
const mockValues: Values = {
networkMode: 'bridge',
hostname: 'new-hostname',
domain: 'new-domain',
macAddress: '02:42:ac:11:00:88',
ipv4Address: '172.17.0.5',
ipv6Address: 'fe80::42:acff:fe11:5',
primaryDns: '8.8.8.8',
secondaryDns: '8.8.4.4',
hostsFileEntries: ['host1:127.0.0.1'],
container: '',
};
it('should use MAC address from values, not from oldConfig', () => {
const oldMacAddress = '02:42:ac:11:00:99';
const macAddress = '02:42:ac:11:00:88';
const result = toRequest(
{ ...mockOldConfig, MacAddress: oldMacAddress },
{ ...mockValues, macAddress },
'container-123'
);
expect(result.MacAddress).toBe(macAddress);
expect(result.MacAddress).not.toBe(oldMacAddress);
});
it('should allow empty MAC address when duplicating containers', () => {
const valuesWithEmptyMac: Values = {
...mockValues,
macAddress: '', // Empty MAC from toViewModel
};
const result = toRequest(
mockOldConfig,
valuesWithEmptyMac,
'container-123'
);
expect(result.MacAddress).toBe('');
expect(result.MacAddress).not.toBe(mockOldConfig.MacAddress);
});
it('should set other network properties from values', () => {
const result = toRequest(mockOldConfig, mockValues, 'container-123');
expect(result.Hostname).toBe('new-hostname');
expect(result.Domainname).toBe('new-domain');
expect(result.HostConfig.NetworkMode).toBe('bridge');
expect(result.HostConfig.Dns).toEqual(['8.8.8.8', '8.8.4.4']);
expect(result.HostConfig.ExtraHosts).toEqual(['host1:127.0.0.1']);
expect(result.NetworkingConfig.EndpointsConfig?.bridge.IPAMConfig).toEqual({
IPv4Address: '172.17.0.5',
IPv6Address: 'fe80::42:acff:fe11:5',
});
});
});
@@ -5,11 +5,7 @@ import { DockerNetwork } from '@/react/docker/networks/types';
import { ContainerListViewModel } from '../../types';
import { ContainerDetailsJSON } from '../../queries/useContainer';
import {
getDefaultViewModel,
getNetworkMode,
toViewModel,
} from './toViewModel';
import { getDefaultViewModel, getNetworkMode } from './toViewModel';
describe('getDefaultViewModel', () => {
it('should return the correct default view model for Windows', () => {
@@ -149,86 +145,3 @@ describe('getNetworkMode', () => {
expect(getNetworkMode(config, mockNetworks)).toEqual(['bridge']);
});
});
describe('toViewModel', () => {
const mockNetworks: Array<DockerNetwork> = [
{
Name: 'bridge',
Id: 'bridge-id',
Driver: 'bridge',
Scope: 'local',
Attachable: false,
Internal: false,
IPAM: { Config: [], Driver: '', Options: {} },
Options: {},
Containers: {},
},
];
it('should copy network settings while clearing mac address', () => {
const config: ContainerDetailsJSON = {
Config: {
Hostname: 'test-host',
Domainname: 'test-domain',
},
HostConfig: {
NetworkMode: 'bridge',
Dns: ['8.8.8.8', '8.8.4.4'],
ExtraHosts: ['host1:127.0.0.1'],
},
NetworkSettings: {
Networks: {
bridge: {
MacAddress: '02:42:ac:11:00:02',
IPAMConfig: {
IPv4Address: '172.17.0.2',
IPv6Address: 'fe80::42:acff:fe11:2',
},
},
},
},
};
const result = toViewModel(config, mockNetworks);
expect(result.macAddress).toBe('');
expect(result.hostname).toBe('test-host');
expect(result.domain).toBe('test-domain');
expect(result.ipv4Address).toBe('172.17.0.2');
expect(result.ipv6Address).toBe('fe80::42:acff:fe11:2');
});
it('should return empty MAC address for new containers', () => {
const config: ContainerDetailsJSON = {
Config: {},
HostConfig: { NetworkMode: 'bridge' },
};
const result = toViewModel(config, mockNetworks);
expect(result.macAddress).toBe('');
});
it('should not duplicate MAC address when duplicating containers', () => {
const config: ContainerDetailsJSON = {
Config: {
Hostname: 'original-container',
},
HostConfig: {
NetworkMode: 'bridge',
},
NetworkSettings: {
Networks: {
bridge: {
MacAddress: '02:42:ac:11:00:99',
},
},
},
};
const result = toViewModel(config, mockNetworks);
expect(result.macAddress).toBe('');
expect(result.hostname).toBe('original-container');
});
});
@@ -53,11 +53,13 @@ export function toViewModel(
ipv6Address = networkSettings.IPAMConfig.IPv6Address || '';
}
const macAddress = networkSettings?.MacAddress || '';
return {
networkMode,
hostname: config.Config?.Hostname || '',
domain: config.Config?.Domainname || '',
macAddress: '', // mac address is cleared between edit/duplicate
macAddress,
ipv4Address,
ipv6Address,
primaryDns,
@@ -1,6 +1,6 @@
import { FormikErrors } from 'formik';
import { array, object, SchemaOf, string } from 'yup';
import { DeviceMapping } from 'docker-types/generated/1.44';
import { DeviceMapping } from 'docker-types/generated/1.41';
import { FormError } from '@@/form-components/FormError';
import { InputList, ItemProps } from '@@/form-components/InputList';
@@ -1,4 +1,4 @@
import { DeviceRequest } from 'docker-types/generated/1.44';
import { DeviceRequest } from 'docker-types/generated/1.41';
import { Values } from './types';
@@ -1,4 +1,4 @@
import { DeviceRequest } from 'docker-types/generated/1.44';
import { DeviceRequest } from 'docker-types/generated/1.41';
import { Values } from './types';
@@ -2,7 +2,7 @@ import {
ContainerConfig,
HostConfig,
NetworkingConfig,
} from 'docker-types/generated/1.44';
} from 'docker-types/generated/1.41';
export interface CreateContainerRequest extends ContainerConfig {
HostConfig: HostConfig;
@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { Network } from 'lucide-react';
import { EndpointSettings, NetworkSettings } from 'docker-types/generated/1.44';
import { EndpointSettings, NetworkSettings } from 'docker-types/generated/1.41';
import { createPersistedStore } from '@@/datatables/types';
import { useTableState } from '@@/datatables/useTableState';
@@ -1,5 +1,5 @@
import { TableMeta } from '@tanstack/react-table';
import { EndpointSettings } from 'docker-types/generated/1.44';
import { EndpointSettings } from 'docker-types/generated/1.41';
export type TableNetwork = EndpointSettings & { id: string; name: string };
@@ -6,7 +6,7 @@ import {
HostConfig,
MountPoint,
NetworkSettings,
} from 'docker-types/generated/1.44';
} from 'docker-types/generated/1.41';
import { PortainerResponse } from '@/react/docker/types';
import axios, { parseAxiosError } from '@/portainer/services/axios';
@@ -1,4 +1,4 @@
import { Resources, RestartPolicy } from 'docker-types/generated/1.44';
import { Resources, RestartPolicy } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,4 +1,4 @@
import { ContainerSummary } from 'docker-types/generated/1.44';
import { ContainerSummary } from 'docker-types/generated/1.41';
import { PortainerResponse } from '@/react/docker/types';
import { WithRequiredProperties } from '@/types';
+1 -1
View File
@@ -1,6 +1,6 @@
import { createColumnHelper } from '@tanstack/react-table';
import { Clock } from 'lucide-react';
import { EventMessage } from 'docker-types/generated/1.44';
import { EventMessage } from 'docker-types/generated/1.41';
import { isoDateFromTimestamp } from '@/portainer/filters/filters';
+1 -1
View File
@@ -1,4 +1,4 @@
import { EventMessage } from 'docker-types/generated/1.44';
import { EventMessage } from 'docker-types/generated/1.41';
type EventType = NonNullable<EventMessage['Type']>;
type Action = string;
+1 -1
View File
@@ -1,4 +1,4 @@
import { IPAMConfig } from 'docker-types/generated/1.44';
import { IPAMConfig } from 'docker-types/generated/1.41';
import { NetworkViewModel } from '@/docker/models/network';
@@ -1,4 +1,4 @@
import { EndpointSettings } from 'docker-types/generated/1.44';
import { EndpointSettings } from 'docker-types/generated/1.41';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios, { parseAxiosError } from '@/portainer/services/axios';
@@ -1,4 +1,4 @@
import { Network } from 'docker-types/generated/1.44';
import { Network } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,4 +1,4 @@
import { ImageInspect } from 'docker-types/generated/1.44';
import { ImageInspect } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,5 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import { ImageSummary } from 'docker-types/generated/1.44';
import { ImageSummary } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,4 +1,4 @@
import { Node } from 'docker-types/generated/1.44';
import { Node } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,4 +1,4 @@
import { Node } from 'docker-types/generated/1.44';
import { Node } from 'docker-types/generated/1.41';
import { useQuery } from '@tanstack/react-query';
import axios, { parseAxiosError } from '@/portainer/services/axios';
@@ -1,4 +1,4 @@
import { Node, NodeSpec } from 'docker-types/generated/1.44';
import { Node, NodeSpec } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,4 +1,4 @@
import { SecretSpec } from 'docker-types/generated/1.44';
import { SecretSpec } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,4 +1,4 @@
import { Secret } from 'docker-types/generated/1.44';
import { Secret } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,4 +1,4 @@
import { Secret } from 'docker-types/generated/1.44';
import { Secret } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,4 +1,4 @@
import { Task } from 'docker-types/generated/1.44';
import { Task } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
+1 -1
View File
@@ -1,4 +1,4 @@
import { EventMessage } from 'docker-types/generated/1.44';
import { EventMessage } from 'docker-types/generated/1.41';
import { useQuery } from '@tanstack/react-query';
import axios, {
+1 -1
View File
@@ -1,5 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import { SystemInfo } from 'docker-types/generated/1.44';
import { SystemInfo } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
+2 -2
View File
@@ -3,7 +3,7 @@ import {
Plugin,
PluginInterfaceType,
PluginsInfo,
} from 'docker-types/generated/1.44';
} from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -87,7 +87,7 @@ export function aggregateData(
(plugin) =>
plugin.Enabled &&
// docker has an error in their types, so we need to cast to unknown first
// see https://docs.docker.com/engine/api/v1.44/#tag/Plugin/operation/PluginList
// see https://docs.docker.com/engine/api/v1.41/#tag/Plugin/operation/PluginList
plugin.Config.Interface.Types.includes(
pluginTypeToVersionMap[pluginType] as unknown as PluginInterfaceType
)
+1 -1
View File
@@ -1,5 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import { Swarm } from 'docker-types/generated/1.44';
import { Swarm } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
+1 -1
View File
@@ -1,5 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import { SystemVersion } from 'docker-types/generated/1.44';
import { SystemVersion } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,4 +1,4 @@
import { EndpointPortConfig } from 'docker-types/generated/1.44';
import { EndpointPortConfig } from 'docker-types/generated/1.41';
import _ from 'lodash';
import { Values } from './PortsMappingField';
@@ -1,4 +1,4 @@
import { EndpointPortConfig } from 'docker-types/generated/1.44';
import { EndpointPortConfig } from 'docker-types/generated/1.41';
import _ from 'lodash';
import { PortBinding, Protocol, Value, isProtocol, isRange } from './types';
@@ -1,4 +1,4 @@
import { Node } from 'docker-types/generated/1.44';
import { Node } from 'docker-types/generated/1.41';
import { CellContext } from '@tanstack/react-table';
import { useNodes } from '@/react/docker/proxy/queries/nodes/useNodes';
@@ -1,4 +1,4 @@
import { Node } from 'docker-types/generated/1.44';
import { Node } from 'docker-types/generated/1.41';
import { ServiceViewModel } from '@/docker/models/service';
@@ -1,5 +1,5 @@
import { CellContext } from '@tanstack/react-table';
import { Node } from 'docker-types/generated/1.44';
import { Node } from 'docker-types/generated/1.41';
import { ServiceViewModel } from '@/docker/models/service';
import { useNodes } from '@/react/docker/proxy/queries/nodes/useNodes';
@@ -1,4 +1,4 @@
import { Service } from 'docker-types/generated/1.44';
import { Service } from 'docker-types/generated/1.41';
import { ServiceUpdateConfig } from '../types';
@@ -1,4 +1,4 @@
import { Service } from 'docker-types/generated/1.44';
import { Service } from 'docker-types/generated/1.41';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios, { parseAxiosError } from '@/portainer/services/axios';
@@ -1,5 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import { Service } from 'docker-types/generated/1.44';
import { Service } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { withGlobalError } from '@/react-tools/react-query';
@@ -1,5 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import { Service } from 'docker-types/generated/1.44';
import { Service } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { withGlobalError } from '@/react-tools/react-query';
@@ -1,4 +1,4 @@
import { ServiceUpdateResponse } from 'docker-types/generated/1.44';
import { ServiceUpdateResponse } from 'docker-types/generated/1.41';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios, { parseAxiosError } from '@/portainer/services/axios';
+1 -1
View File
@@ -1,4 +1,4 @@
import { ServiceSpec, TaskSpec } from 'docker-types/generated/1.44';
import { ServiceSpec, TaskSpec } from 'docker-types/generated/1.41';
export type ServiceId = string;
+1 -1
View File
@@ -1,4 +1,4 @@
import { Task } from 'docker-types/generated/1.44';
import { Task } from 'docker-types/generated/1.41';
import { useQuery } from '@tanstack/react-query';
import axios, { parseAxiosError } from '@/portainer/services/axios';
+1 -1
View File
@@ -1,4 +1,4 @@
import { Task } from 'docker-types/generated/1.44';
import { Task } from 'docker-types/generated/1.41';
export type TaskId = NonNullable<Task['ID']>;
@@ -1,4 +1,4 @@
import { Volume, VolumeCreateOptions } from 'docker-types/generated/1.44';
import { Volume, VolumeCreateOptions } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,4 +1,4 @@
import { Volume } from 'docker-types/generated/1.44';
import { Volume } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,4 +1,4 @@
import { Volume } from 'docker-types/generated/1.44';
import { Volume } from 'docker-types/generated/1.41';
import { EnvironmentId } from '@/react/portainer/environments/types';
import axios, { parseAxiosError } from '@/portainer/services/axios';
@@ -1,4 +1,4 @@
import { Volume } from 'docker-types/generated/1.44';
import { Volume } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { EnvironmentId } from '@/react/portainer/environments/types';
@@ -1,5 +1,5 @@
import { useQuery } from '@tanstack/react-query';
import { Volume } from 'docker-types/generated/1.44';
import { Volume } from 'docker-types/generated/1.41';
import axios, { parseAxiosError } from '@/portainer/services/axios';
import { buildDockerProxyUrl } from '@/react/docker/proxy/queries/buildDockerProxyUrl';
@@ -36,7 +36,6 @@ export function PrivateRegistryFieldset({
}: Props) {
const [checked, setChecked] = useState(isActive || false);
const [selected, setSelected] = useState(value);
const [isInitialMount, setIsInitialMount] = useState(true);
const tooltipMessage =
'This allows you to provide credentials when using a private registry that requires authentication';
@@ -48,13 +47,6 @@ export function PrivateRegistryFieldset({
}, [isActive]);
useEffect(() => {
// Skip onChange call on initial mount when checkbox is already checked
// to preserve the saved registry value
if (isInitialMount) {
setIsInitialMount(false);
return;
}
if (checked) {
onChange();
} else {
@@ -2,7 +2,6 @@
display: flex;
align-items: center;
justify-content: flex-end;
gap: 5px;
}
.sort-by-element {
@@ -1,4 +1,4 @@
import { RestartPolicy } from 'docker-types/generated/1.44';
import { RestartPolicy } from 'docker-types/generated/1.41';
import { BasicTableSettings } from '@@/datatables/types';
@@ -1,5 +1,5 @@
import _ from 'lodash';
import { RestartPolicy } from 'docker-types/generated/1.44';
import { RestartPolicy } from 'docker-types/generated/1.41';
import { PorImageRegistryModel } from 'Docker/models/porImageRegistry';
+1 -1
View File
@@ -1,5 +1,5 @@
import { http, HttpResponse } from 'msw';
import { SystemInfo, SystemVersion } from 'docker-types/generated/1.44';
import { SystemInfo, SystemVersion } from 'docker-types/generated/1.41';
export const dockerHandlers = [
http.get<never, never, SystemInfo>(
+1 -1
View File
@@ -1,4 +1,4 @@
{
"docker": "v29.1.2",
"docker": "v28.3.0",
"mingit": "2.49.0.1"
}
+2 -17
View File
@@ -59,24 +59,9 @@ ldflags="-s -X 'github.com/portainer/liblicense.LicenseServerBaseURL=https://api
echo "$ldflags"
# See: https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63
# For a list of valid GOOS and GOARCH values
PLATFORM=${1:-$(go env GOOS)}
ARCH=${2:-$(go env GOARCH)}
# if the default platform is darwin, set it to linux to allow it to run in the portainer/base image (which doesn't support darwin)
if [ "$PLATFORM" = "darwin" ]; then
PLATFORM="linux"
fi
BINARY_NAME="portainer"
if [ "$PLATFORM" = "windows" ]; then
BINARY_NAME="portainer.exe"
fi
GOOS=${PLATFORM} GOARCH=${ARCH} CGO_ENABLED=0 go build \
GOOS=${1:-$(go env GOOS)} GOARCH=${2:-$(go env GOARCH)} CGO_ENABLED=0 go build \
-trimpath \
--installsuffix cgo \
--ldflags "$ldflags" \
-o "../dist/${BINARY_NAME}" \
-o "../dist/portainer" \
./cmd/portainer/
+11 -11
View File
@@ -1,6 +1,6 @@
module github.com/portainer/portainer
go 1.24.11
go 1.24.9
require (
github.com/Masterminds/semver v1.5.0
@@ -50,11 +50,11 @@ require (
github.com/urfave/negroni v1.0.0
github.com/viney-shih/go-lock v1.1.1
go.etcd.io/bbolt v1.4.3
golang.org/x/crypto v0.45.0
golang.org/x/crypto v0.40.0
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0
golang.org/x/mod v0.29.0
golang.org/x/mod v0.27.0
golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.18.0
golang.org/x/sync v0.17.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
helm.sh/helm/v3 v3.18.5
@@ -111,9 +111,9 @@ require (
github.com/cloudflare/cfssl v1.6.4 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/containerd/console v1.0.5 // indirect
github.com/containerd/containerd v1.7.29 // indirect
github.com/containerd/containerd v1.7.27 // indirect
github.com/containerd/containerd/api v1.9.0 // indirect
github.com/containerd/containerd/v2 v2.1.5 // indirect
github.com/containerd/containerd/v2 v2.1.4 // indirect
github.com/containerd/continuity v0.4.5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
@@ -292,11 +292,11 @@ require (
go.uber.org/mock v0.6.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.37.0 // indirect
golang.org/x/term v0.33.0 // indirect
golang.org/x/text v0.27.0 // indirect
golang.org/x/time v0.11.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250528174236-200df99c418a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
google.golang.org/grpc v1.74.2 // indirect
+22 -22
View File
@@ -144,12 +144,12 @@ github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJ
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/containerd/containerd v1.7.29 h1:90fWABQsaN9mJhGkoVnuzEY+o1XDPbg9BTC9QTAHnuE=
github.com/containerd/containerd v1.7.29/go.mod h1:azUkWcOvHrWvaiUjSQH0fjzuHIwSPg1WL5PshGP4Szs=
github.com/containerd/containerd v1.7.27 h1:yFyEyojddO3MIGVER2xJLWoCIn+Up4GaHFquP7hsFII=
github.com/containerd/containerd v1.7.27/go.mod h1:xZmPnl75Vc+BLGt4MIfu6bp+fy03gdHAn9bz+FreFR0=
github.com/containerd/containerd/api v1.9.0 h1:HZ/licowTRazus+wt9fM6r/9BQO7S0vD5lMcWspGIg0=
github.com/containerd/containerd/api v1.9.0/go.mod h1:GhghKFmTR3hNtyznBoQ0EMWr9ju5AqHjcZPsSpTKutI=
github.com/containerd/containerd/v2 v2.1.5 h1:pWSmPxUszaLZKQPvOx27iD4iH+aM6o0BoN9+hg77cro=
github.com/containerd/containerd/v2 v2.1.5/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM=
github.com/containerd/containerd/v2 v2.1.4 h1:/hXWjiSFd6ftrBOBGfAZ6T30LJcx1dBjdKEeI8xucKQ=
github.com/containerd/containerd/v2 v2.1.4/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
@@ -865,8 +865,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -876,8 +876,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -898,8 +898,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
@@ -916,8 +916,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -953,8 +953,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -966,8 +966,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -982,10 +982,10 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
@@ -994,8 +994,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+2 -2
View File
@@ -2,7 +2,7 @@
"author": "Portainer.io",
"name": "portainer",
"homepage": "http://portainer.io",
"version": "2.33.6",
"version": "2.33.3",
"repository": {
"type": "git",
"url": "git@github.com:portainer/portainer.git"
@@ -92,7 +92,7 @@
"codemirror-json-schema": "^0.8.0",
"core-js": "^3.19.3",
"date-fns": "^2.29.3",
"docker-types": "^1.45.0",
"docker-types": "^1.43.1",
"fast-json-patch": "^3.1.1",
"file-saver": "^2.0.5",
"filesize": "~3.3.0",
+1 -5
View File
@@ -124,11 +124,7 @@ func (c *ComposeDeployer) Deploy(ctx context.Context, filePaths []string, option
project = project.WithoutUnnecessaryResources()
opts := api.UpOptions{
Start: api.StartOptions{
Project: project,
},
}
var opts api.UpOptions
if options.ForceRecreate {
opts.Create.Recreate = api.RecreateForce
}
@@ -80,65 +80,6 @@ services:
require.False(t, containerExists(composeContainerName))
}
// Detect regression in container injections.
// Ref BE-12432
// Ref https://github.com/portainer/portainer/issues/12909
func Test_UpAndDownWithInjection(t *testing.T) {
const content = `
services:
test:
image: alpine:latest
container_name: "composetest_alpine"
command: ["sh", "-c", "cat /test.txt"]
configs:
- source: test-config
target: /test.txt
configs:
test-config:
content: |
Hello from inline config!
This should appear in the container.
`
const projectName = "composetest"
const containerName = "composetest_alpine"
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})
require.NoError(t, err)
err = w.Pull(ctx, filePaths, libstack.Options{ProjectName: projectName})
require.NoError(t, err)
require.False(t, containerExists(containerName))
err = w.Deploy(ctx, filePaths, libstack.DeployOptions{
Options: libstack.Options{
ProjectName: projectName,
},
})
require.NoError(t, err)
require.True(t, containerExists(containerName))
waitResult := w.WaitForStatus(ctx, projectName, libstack.StatusCompleted)
require.Empty(t, waitResult.ErrorMsg)
require.Equal(t, libstack.StatusCompleted, waitResult.Status)
err = w.Remove(ctx, projectName, filePaths, libstack.RemoveOptions{})
require.NoError(t, err)
require.False(t, containerExists(containerName))
}
func TestRun(t *testing.T) {
w := NewComposeDeployer()
+4 -4
View File
@@ -9593,10 +9593,10 @@ dns-packet@^5.2.2:
dependencies:
"@leichtgewicht/ip-codec" "^2.0.1"
docker-types@^1.45.0:
version "1.45.0"
resolved "https://registry.yarnpkg.com/docker-types/-/docker-types-1.45.0.tgz#57f9eeb283364128fa472d25abc75563160aea4f"
integrity sha512-IvVAmwGbXVSEsD+Ld3pwIhCSm4YbnlZ9dPHbDVkgZUcE22MSkuIARREc7ipQ9JoMOzu7qMPD8H/KCpGnrZRo7A==
docker-types@^1.43.1:
version "1.43.1"
resolved "https://registry.yarnpkg.com/docker-types/-/docker-types-1.43.1.tgz#3f1a644e29c85392e95c207bba15f16b675dca2a"
integrity sha512-eiihBU5LvZlNaeh/hGycsaoeutlPJ/0vyvEKvuBWnyilCq+IRWnv9e2+oh6AIpXEuHiWFowwHXmIlKlt07X3Mw==
doctrine@^2.1.0:
version "2.1.0"