diff --git a/api/cli/pairlist.go b/api/cli/pairlist.go index 43d05e910..d3069e971 100644 --- a/api/cli/pairlist.go +++ b/api/cli/pairlist.go @@ -9,7 +9,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) -type pairList []portainer.Pair +type pairList portainer.MultiPair // Set implementation for a list of portainer.Pair func (l *pairList) Set(value string) error { @@ -34,8 +34,8 @@ func (l *pairList) IsCumulative() bool { return true } -func pairs(s kingpin.Settings) (target *[]portainer.Pair) { - target = new([]portainer.Pair) +func pairs(s kingpin.Settings) (target *portainer.MultiPair) { + target = new(portainer.MultiPair) s.SetValue((*pairList)(target)) return } diff --git a/api/cli/pairlistbool.go b/api/cli/pairlistbool.go index 923629470..4ca325005 100644 --- a/api/cli/pairlistbool.go +++ b/api/cli/pairlistbool.go @@ -8,7 +8,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) -type pairListBool []portainer.Pair +type pairListBool portainer.MultiPair // Set implementation for a list of portainer.Pair func (l *pairListBool) Set(value string) error { @@ -38,8 +38,8 @@ func (l *pairListBool) IsCumulative() bool { return true } -func BoolPairs(s kingpin.Settings) (target *[]portainer.Pair) { - target = new([]portainer.Pair) +func BoolPairs(s kingpin.Settings) (target *portainer.MultiPair) { + target = new(portainer.MultiPair) s.SetValue((*pairListBool)(target)) return } diff --git a/api/dataservices/endpoint/endpoint.go b/api/dataservices/endpoint/endpoint.go index 61506ed6e..c630769a1 100644 --- a/api/dataservices/endpoint/endpoint.go +++ b/api/dataservices/endpoint/endpoint.go @@ -62,7 +62,8 @@ func (service *Service) Endpoint(ID portainer.EndpointID) (*portainer.Endpoint, // UpdateEndpoint updates an environment(endpoint). func (service *Service) UpdateEndpoint(ID portainer.EndpointID, endpoint *portainer.Endpoint) error { db := service.connection.GetDB() - tx := db.Model(&portainer.Endpoint{}).Where("id = ?", ID).UpdateColumns(&endpoint) + endpoint.ID = ID + tx := db.Save(&endpoint) if tx.Error != nil { return tx.Error } diff --git a/api/dataservices/stack/tests/stack_test.go b/api/dataservices/stack/tests/stack_test.go index 2a90832f5..4bda84fbb 100644 --- a/api/dataservices/stack/tests/stack_test.go +++ b/api/dataservices/stack/tests/stack_test.go @@ -58,7 +58,7 @@ func (b *stackBuilder) createNewStack(webhookID string) portainer.Stack { Type: portainer.DockerComposeStack, EndpointID: 2, EntryPoint: filesystem.ComposeFileDefaultName, - Env: []portainer.Pair{{Name: "Name1", Value1: "Value1"}}, + Env: portainer.MultiPair{{Name: "Name1", Value1: "Value1"}}, Status: portainer.StackStatusActive, CreationDate: time.Now().Unix(), ProjectPath: "/tmp/project", diff --git a/api/dataservices/user/user.go b/api/dataservices/user/user.go index d79e041f0..214e0f01c 100644 --- a/api/dataservices/user/user.go +++ b/api/dataservices/user/user.go @@ -73,7 +73,8 @@ func (service *Service) UsersByRole(role portainer.UserRole) ([]portainer.User, func (service *Service) UpdateUser(ID portainer.UserID, user *portainer.User) error { db := service.connection.GetDB() - tx := db.Model(&portainer.User{}).Where("id = ?", ID).UpdateColumns(&user) + user.ID = ID + tx := db.Save(&user) if tx.Error != nil { return tx.Error } @@ -84,7 +85,7 @@ func (service *Service) UpdateUser(ID portainer.UserID, user *portainer.User) er // CreateUser creates a new user. func (service *Service) Create(user *portainer.User) error { db := service.connection.GetDB() - tx := db.Model(&portainer.User{}).Create(&user) + tx := db.Create(&user) if tx.Error != nil { return tx.Error } diff --git a/api/datastore/init.go b/api/datastore/init.go index fd2011be3..9fbe48ea7 100644 --- a/api/datastore/init.go +++ b/api/datastore/init.go @@ -37,7 +37,7 @@ func (store *Store) checkOrCreateDefaultSettings() error { defaultSettings := &portainer.Settings{ EnableTelemetry: false, AuthenticationMethod: portainer.AuthenticationInternal, - BlackListedLabels: make([]portainer.Pair, 0), + BlackListedLabels: make(portainer.MultiPair, 0), InternalAuthSettings: portainer.InternalAuthSettings{ RequiredPasswordLength: 12, }, @@ -102,7 +102,7 @@ func (store *Store) checkOrCreateDefaultData() error { unassignedGroup := &portainer.EndpointGroup{ Name: "Unassigned", Description: "Unassigned environments", - Labels: []portainer.Pair{}, + Labels: portainer.MultiPair{}, UserAccessPolicies: portainer.UserAccessPolicies{}, TeamAccessPolicies: portainer.TeamAccessPolicies{}, TagIDs: []portainer.TagID{}, diff --git a/api/datastore/migrator/migrate_dbversion60.go b/api/datastore/migrator/migrate_dbversion60.go index fc7fa50bb..f6f8c2db7 100644 --- a/api/datastore/migrator/migrate_dbversion60.go +++ b/api/datastore/migrator/migrate_dbversion60.go @@ -20,7 +20,7 @@ func (m *Migrator) addGpuInputFieldDB60() error { for _, endpoint := range endpoints { if endpoint.Gpus == nil { - endpoint.Gpus = []portainer.Pair{} + endpoint.Gpus = portainer.MultiPair{} err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint) if err != nil { return err diff --git a/api/exec/compose_stack_test.go b/api/exec/compose_stack_test.go index 8e34c6e9d..5b5f0bdae 100644 --- a/api/exec/compose_stack_test.go +++ b/api/exec/compose_stack_test.go @@ -30,7 +30,7 @@ func Test_createEnvFile(t *testing.T) { name: "should not add env file option if stack's env variables are empty", stack: &portainer.Stack{ ProjectPath: dir, - Env: []portainer.Pair{}, + Env: portainer.MultiPair{}, }, expected: "", }, @@ -38,7 +38,7 @@ func Test_createEnvFile(t *testing.T) { name: "should add env file option if stack has env variables", stack: &portainer.Stack{ ProjectPath: dir, - Env: []portainer.Pair{ + Env: portainer.MultiPair{ {Name: "var1", Value1: "value1"}, {Name: "var2", Value1: "value2"}, }, @@ -70,7 +70,7 @@ func Test_createEnvFile_mergesDefultAndInplaceEnvVars(t *testing.T) { os.WriteFile(path.Join(dir, ".env"), []byte("VAR1=VAL1\nVAR2=VAL2\n"), 0600) stack := &portainer.Stack{ ProjectPath: dir, - Env: []portainer.Pair{ + Env: portainer.MultiPair{ {Name: "VAR1", Value1: "NEW_VAL1"}, {Name: "VAR3", Value1: "VAL3"}, }, diff --git a/api/http/handler/endpoints/endpoint_create.go b/api/http/handler/endpoints/endpoint_create.go index 57374f4c1..330b085a0 100644 --- a/api/http/handler/endpoints/endpoint_create.go +++ b/api/http/handler/endpoints/endpoint_create.go @@ -25,7 +25,7 @@ type endpointCreatePayload struct { URL string EndpointCreationType endpointCreationEnum PublicURL string - Gpus []portainer.Pair + Gpus portainer.MultiPair GroupID int TLS bool TLSSkipVerify bool @@ -153,7 +153,7 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error { payload.PublicURL = publicURL } - gpus := make([]portainer.Pair, 0) + gpus := make(portainer.MultiPair, 0) err = request.RetrieveMultiPartFormJSONValue(r, "Gpus", &gpus, true) if err != nil { return errors.New("invalid Gpus parameter") diff --git a/api/http/handler/endpoints/endpoint_settings_update.go b/api/http/handler/endpoints/endpoint_settings_update.go index 34a9abf6a..b8d0447d8 100644 --- a/api/http/handler/endpoints/endpoint_settings_update.go +++ b/api/http/handler/endpoints/endpoint_settings_update.go @@ -31,7 +31,7 @@ type endpointSettingsUpdatePayload struct { EnableGPUManagement *bool `json:"enableGPUManagement" example:"false"` - Gpus []portainer.Pair `json:"gpus"` + Gpus portainer.MultiPair `json:"gpus"` } func (payload *endpointSettingsUpdatePayload) Validate(r *http.Request) error { diff --git a/api/http/handler/endpoints/endpoint_update.go b/api/http/handler/endpoints/endpoint_update.go index 8e2400512..7276eb9bd 100644 --- a/api/http/handler/endpoints/endpoint_update.go +++ b/api/http/handler/endpoints/endpoint_update.go @@ -22,7 +22,7 @@ type endpointUpdatePayload struct { // Defaults to URL if not specified PublicURL *string `example:"docker.mydomain.tld:2375"` // GPUs information - Gpus []portainer.Pair + Gpus portainer.MultiPair // Group identifier GroupID *int `example:"1"` // Require TLS to connect against this environment(endpoint) diff --git a/api/http/handler/settings/settings_update.go b/api/http/handler/settings/settings_update.go index b770efc4f..cc818cf63 100644 --- a/api/http/handler/settings/settings_update.go +++ b/api/http/handler/settings/settings_update.go @@ -23,7 +23,7 @@ type settingsUpdatePayload struct { // URL to a logo that will be displayed on the login page as well as on top of the sidebar. Will use default Portainer logo when value is empty string LogoURL *string `example:"https://mycompany.mydomain.tld/logo.png"` // A list of label name & value that will be used to hide containers when querying containers - BlackListedLabels []portainer.Pair + BlackListedLabels portainer.MultiPair // Active authentication method for the Portainer instance. Valid values are: 1 for internal, 2 for LDAP, or 3 for oauth AuthenticationMethod *int `example:"1"` InternalAuthSettings *portainer.InternalAuthSettings diff --git a/api/http/handler/stacks/create_compose_stack.go b/api/http/handler/stacks/create_compose_stack.go index cff765e93..171c42318 100644 --- a/api/http/handler/stacks/create_compose_stack.go +++ b/api/http/handler/stacks/create_compose_stack.go @@ -25,7 +25,7 @@ type composeStackFromFileContentPayload struct { // Content of the Stack file StackFileContent string `example:"version: 3\n services:\n web:\n image:nginx" validate:"required"` // A list of environment variables used during stack deployment - Env []portainer.Pair + Env portainer.MultiPair // Whether the stack is from a app template FromAppTemplate bool `example:"false"` } @@ -41,7 +41,7 @@ func (payload *composeStackFromFileContentPayload) Validate(r *http.Request) err return nil } -func createStackPayloadFromComposeFileContentPayload(name string, fileContent string, env []portainer.Pair, fromAppTemplate bool) stackbuilders.StackPayload { +func createStackPayloadFromComposeFileContentPayload(name string, fileContent string, env portainer.MultiPair, fromAppTemplate bool) stackbuilders.StackPayload { return stackbuilders.StackPayload{ Name: name, StackFileContent: fileContent, @@ -174,14 +174,14 @@ type composeStackFromGitRepositoryPayload struct { // Optional auto update configuration AutoUpdate *portainer.AutoUpdateSettings // A list of environment variables used during stack deployment - Env []portainer.Pair + Env portainer.MultiPair // Whether the stack is from a app template FromAppTemplate bool `example:"false"` // TLSSkipVerify skips SSL verification when cloning the Git repository TLSSkipVerify bool `example:"false"` } -func createStackPayloadFromComposeGitPayload(name, repoUrl, repoReference, repoUsername, repoPassword string, repoAuthentication bool, composeFile string, additionalFiles []string, autoUpdate *portainer.AutoUpdateSettings, env []portainer.Pair, fromAppTemplate bool, repoSkipSSLVerify bool) stackbuilders.StackPayload { +func createStackPayloadFromComposeGitPayload(name, repoUrl, repoReference, repoUsername, repoPassword string, repoAuthentication bool, composeFile string, additionalFiles []string, autoUpdate *portainer.AutoUpdateSettings, env portainer.MultiPair, fromAppTemplate bool, repoSkipSSLVerify bool) stackbuilders.StackPayload { return stackbuilders.StackPayload{ Name: name, RepositoryConfigPayload: stackbuilders.RepositoryConfigPayload{ @@ -314,10 +314,10 @@ func (handler *Handler) createComposeStackFromGitRepository(w http.ResponseWrite type composeStackFromFileUploadPayload struct { Name string StackFileContent []byte - Env []portainer.Pair + Env portainer.MultiPair } -func createStackPayloadFromComposeFileUploadPayload(name string, fileContentBytes []byte, env []portainer.Pair) stackbuilders.StackPayload { +func createStackPayloadFromComposeFileUploadPayload(name string, fileContentBytes []byte, env portainer.MultiPair) stackbuilders.StackPayload { return stackbuilders.StackPayload{ Name: name, StackFileContentBytes: fileContentBytes, @@ -339,7 +339,7 @@ func decodeRequestForm(r *http.Request) (*composeStackFromFileUploadPayload, err } payload.StackFileContent = composeFileContent - var env []portainer.Pair + var env portainer.MultiPair err = request.RetrieveMultiPartFormJSONValue(r, "Env", &env, true) if err != nil { return nil, errors.New("Invalid Env parameter") diff --git a/api/http/handler/stacks/create_swarm_stack.go b/api/http/handler/stacks/create_swarm_stack.go index 22117f68b..4c85eb6b1 100644 --- a/api/http/handler/stacks/create_swarm_stack.go +++ b/api/http/handler/stacks/create_swarm_stack.go @@ -24,7 +24,7 @@ type swarmStackFromFileContentPayload struct { // Content of the Stack file StackFileContent string `example:"version: 3\n services:\n web:\n image:nginx" validate:"required"` // A list of environment variables used during stack deployment - Env []portainer.Pair + Env portainer.MultiPair // Whether the stack is from a app template FromAppTemplate bool `example:"false"` } @@ -42,7 +42,7 @@ func (payload *swarmStackFromFileContentPayload) Validate(r *http.Request) error return nil } -func createStackPayloadFromSwarmFileContentPayload(name string, swarmID string, fileContent string, env []portainer.Pair, fromAppTemplate bool) stackbuilders.StackPayload { +func createStackPayloadFromSwarmFileContentPayload(name string, swarmID string, fileContent string, env portainer.MultiPair, fromAppTemplate bool) stackbuilders.StackPayload { return stackbuilders.StackPayload{ Name: name, SwarmID: swarmID, @@ -112,7 +112,7 @@ type swarmStackFromGitRepositoryPayload struct { // Swarm cluster identifier SwarmID string `example:"jpofkc0i9uo9wtx1zesuk649w" validate:"required"` // A list of environment variables used during stack deployment - Env []portainer.Pair + Env portainer.MultiPair // URL of a Git repository hosting the Stack file RepositoryURL string `example:"https://github.com/openfaas/faas" validate:"required"` @@ -155,7 +155,7 @@ func (payload *swarmStackFromGitRepositoryPayload) Validate(r *http.Request) err return nil } -func createStackPayloadFromSwarmGitPayload(name, swarmID, repoUrl, repoReference, repoUsername, repoPassword string, repoAuthentication bool, composeFile string, additionalFiles []string, autoUpdate *portainer.AutoUpdateSettings, env []portainer.Pair, fromAppTemplate bool, repoSkipSSLVerify bool) stackbuilders.StackPayload { +func createStackPayloadFromSwarmGitPayload(name, swarmID, repoUrl, repoReference, repoUsername, repoPassword string, repoAuthentication bool, composeFile string, additionalFiles []string, autoUpdate *portainer.AutoUpdateSettings, env portainer.MultiPair, fromAppTemplate bool, repoSkipSSLVerify bool) stackbuilders.StackPayload { return stackbuilders.StackPayload{ Name: name, SwarmID: swarmID, @@ -258,10 +258,10 @@ type swarmStackFromFileUploadPayload struct { Name string SwarmID string StackFileContent []byte - Env []portainer.Pair + Env portainer.MultiPair } -func createStackPayloadFromSwarmFileUploadPayload(name, swarmID string, fileContentBytes []byte, env []portainer.Pair) stackbuilders.StackPayload { +func createStackPayloadFromSwarmFileUploadPayload(name, swarmID string, fileContentBytes []byte, env portainer.MultiPair) stackbuilders.StackPayload { return stackbuilders.StackPayload{ Name: name, SwarmID: swarmID, @@ -289,7 +289,7 @@ func (payload *swarmStackFromFileUploadPayload) Validate(r *http.Request) error } payload.StackFileContent = composeFileContent - var env []portainer.Pair + var env portainer.MultiPair err = request.RetrieveMultiPartFormJSONValue(r, "Env", &env, true) if err != nil { return errors.New("Invalid Env parameter") diff --git a/api/http/handler/stacks/stack_update.go b/api/http/handler/stacks/stack_update.go index 0dc3f25dc..7c820523a 100644 --- a/api/http/handler/stacks/stack_update.go +++ b/api/http/handler/stacks/stack_update.go @@ -23,7 +23,7 @@ type updateComposeStackPayload struct { // New content of the Stack file StackFileContent string `example:"version: 3\n services:\n web:\n image:nginx"` // A list of environment(endpoint) variables used during stack deployment - Env []portainer.Pair + Env portainer.MultiPair // Force a pulling to current image with the original tag though the image is already the latest PullImage bool `example:"false"` } @@ -39,7 +39,7 @@ type updateSwarmStackPayload struct { // New content of the Stack file StackFileContent string `example:"version: 3\n services:\n web:\n image:nginx"` // A list of environment(endpoint) variables used during stack deployment - Env []portainer.Pair + Env portainer.MultiPair // Prune services that are no longer referenced (only available for Swarm stacks) Prune bool `example:"true"` // Force a pulling to current image with the original tag though the image is already the latest diff --git a/api/http/handler/stacks/stack_update_git.go b/api/http/handler/stacks/stack_update_git.go index 3ccbb7633..0f3708702 100644 --- a/api/http/handler/stacks/stack_update_git.go +++ b/api/http/handler/stacks/stack_update_git.go @@ -19,7 +19,7 @@ import ( type stackGitUpdatePayload struct { AutoUpdate *portainer.AutoUpdateSettings - Env []portainer.Pair + Env portainer.MultiPair Prune bool RepositoryReferenceName string RepositoryAuthentication bool diff --git a/api/http/handler/stacks/stack_update_git_redeploy.go b/api/http/handler/stacks/stack_update_git_redeploy.go index b30085156..97aa901cb 100644 --- a/api/http/handler/stacks/stack_update_git_redeploy.go +++ b/api/http/handler/stacks/stack_update_git_redeploy.go @@ -22,7 +22,7 @@ type stackGitRedployPayload struct { RepositoryAuthentication bool RepositoryUsername string RepositoryPassword string - Env []portainer.Pair + Env portainer.MultiPair Prune bool // Force a pulling to current image with the original tag though the image is already the latest PullImage bool `example:"false"` diff --git a/api/http/proxy/factory/docker/containers.go b/api/http/proxy/factory/docker/containers.go index f72761dc1..f4af1ada0 100644 --- a/api/http/proxy/factory/docker/containers.go +++ b/api/http/proxy/factory/docker/containers.go @@ -123,7 +123,7 @@ func selectorContainerLabelsFromContainerListOperation(responseObject map[string // filterContainersWithLabels loops through a list of containers, and filters containers that do not contains // any labels in the labels black list. -func filterContainersWithBlackListedLabels(containerData []interface{}, labelBlackList []portainer.Pair) ([]interface{}, error) { +func filterContainersWithBlackListedLabels(containerData []interface{}, labelBlackList portainer.MultiPair) ([]interface{}, error) { filteredContainerData := make([]interface{}, 0) for _, container := range containerData { @@ -142,7 +142,7 @@ func filterContainersWithBlackListedLabels(containerData []interface{}, labelBla return filteredContainerData, nil } -func containerHasBlackListedLabel(containerLabels map[string]interface{}, labelBlackList []portainer.Pair) bool { +func containerHasBlackListedLabel(containerLabels map[string]interface{}, labelBlackList portainer.MultiPair) bool { for key, value := range containerLabels { labelName := key labelValue := value.(string) diff --git a/api/http/proxy/factory/docker/transport.go b/api/http/proxy/factory/docker/transport.go index 832d383f1..2a0dd9b0e 100644 --- a/api/http/proxy/factory/docker/transport.go +++ b/api/http/proxy/factory/docker/transport.go @@ -56,7 +56,7 @@ type ( operationExecutor struct { operationContext *restrictedDockerOperationContext - labelBlackList []portainer.Pair + labelBlackList portainer.MultiPair } restrictedOperationRequest func(*http.Response, *operationExecutor) error operationRequest func(*http.Request) error diff --git a/api/portainer.go b/api/portainer.go index 80c1c354f..86a5595ca 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -127,7 +127,7 @@ type ( DemoEnvironment *bool EnableEdgeComputeFeatures *bool EndpointURL *string - Labels *[]Pair + Labels *MultiPair Logo *string NoAnalytics *bool Templates *string @@ -173,7 +173,7 @@ type ( // Path to the Stack file EntryPoint string `json:"EntryPoint" example:"docker-compose.yml"` // User identifier who created this template - CreatedByUserID UserID `json:"CreatedByUserId" example:"3"` + CreatedByUserID UserID `json:"CreatedByUserId" example:"3" gorm:"foreignKey=ID"` // A note that will be displayed in the UI. Supports HTML content Note string `json:"Note" example:"This is my custom template"` // Platform associated to the template. @@ -186,9 +186,9 @@ type ( // * 2 - compose // * 3 - kubernetes Type StackType `json:"Type" example:"1"` - ResourceControl *ResourceControl `json:"ResourceControl" gorm:"foreignKey"` - Variables []CustomTemplateVariableDefinition `json:"Variables" gorm:"serialize:json"` - GitConfig *gittypes.RepoConfig `json:"GitConfig" gorm:"serialize:json"` + ResourceControl *ResourceControl `json:"ResourceControl" gorm:"serializer:json"` + Variables []CustomTemplateVariableDefinition `json:"Variables" gorm:"serializer:json"` + GitConfig *gittypes.RepoConfig `json:"GitConfig" gorm:"serializer:json"` // IsComposeFormat indicates if the Kubernetes template is created from a Docker Compose file IsComposeFormat bool `example:"false"` } @@ -276,7 +276,7 @@ type ( Version int `json:"Version"` // Field used for log collection of Endpoints belonging to EdgeGroups - GroupLogsCollection map[EndpointID]EdgeJobEndpointMeta + GroupLogsCollection map[EndpointID]EdgeJobEndpointMeta `json:"GroupLogsCollection" gorm:"serializer:json"` } // EdgeJobEndpointMeta represents a meta data object for an Edge job and Environment(Endpoint) relation @@ -300,7 +300,7 @@ type ( CronExpression string `json:"CronExpression"` Script string `json:"Script"` Version int `json:"Version"` - Endpoints []EndpointID `json:"Endpoints" gorm:"serealize:json"` + Endpoints []EndpointID `json:"Endpoints" gorm:"serializer:json"` } //EdgeStack represents an edge stack @@ -308,9 +308,9 @@ type ( // EdgeStack Identifier ID EdgeStackID `json:"Id" example:"1" gorm:"unique,primaryKey,autoIncrement"` Name string `json:"Name"` - Status map[EndpointID]EdgeStackStatus `json:"Status" gorm:"serealize:json"` + Status map[EndpointID]EdgeStackStatus `json:"Status" gorm:"serializer:json"` CreationDate int64 `json:"CreationDate"` - EdgeGroups []EdgeGroupID `json:"EdgeGroups" gorm:"serealize:json"` + EdgeGroups []EdgeGroupID `json:"EdgeGroups" gorm:"serializer:json"` ProjectPath string `json:"ProjectPath"` EntryPoint string `json:"EntryPoint"` Version int `json:"Version"` @@ -343,7 +343,7 @@ type ( EdgeStackStatus struct { Details EdgeStackStatusDetails `json:"Details"` Error string `json:"Error"` - EndpointID EndpointID `json:"EndpointID"` + EndpointID EndpointID `json:"EndpointID" gorm:"foreignKey=ID"` // Deprecated Type EdgeStackStatusType `json:"Type"` @@ -352,7 +352,7 @@ type ( //EdgeStackStatusType represents an edge stack status type EdgeStackStatusType int - GPUs []Pair + MultiPair []Pair // Environment(Endpoint) represents a Docker environment(endpoint) with all the info required // to connect to it @@ -369,7 +369,7 @@ type ( GroupID EndpointGroupID `json:"GroupId" example:"1"` // URL or IP address where exposed containers will be reachable PublicURL string `json:"PublicURL" example:"docker.mydomain.tld:2375"` - Gpus GPUs `json:"Gpus" gorm:"serializer:json"` + Gpus MultiPair `json:"Gpus" gorm:"serializer:json"` TLSConfig TLSConfiguration `json:"TLSConfig" gorm:"serializer:json"` AzureCredentials AzureCredentials `json:"AzureCredentials,omitempty" gorm:"serializer:json"` // List of tag identifiers to which this environment(endpoint) is associated @@ -463,7 +463,7 @@ type ( TagIDs []TagID `json:"TagIds" gorm:"serializer:json"` // Deprecated fields - Labels []Pair `json:"Labels" gorm:"serializer:json"` + Labels MultiPair `json:"Labels" gorm:"serializer:json"` // Deprecated in DBVersion == 18 AuthorizedUsers []UserID `json:"AuthorizedUsers" gorm:"serializer:json"` @@ -513,8 +513,8 @@ type ( // EndpointRelation represents a environment(endpoint) relation object EndpointRelation struct { - EndpointID EndpointID - EdgeStacks map[EdgeStackID]bool + EndpointID EndpointID `gorm:"foreignKey=ID"` + EdgeStacks map[EdgeStackID]bool `gorm:"serializer:json"` } // EndpointPostInitMigrations @@ -889,7 +889,7 @@ type ( Recurring bool Created int64 JobType JobType - EdgeSchedule *EdgeSchedule `json:"EdgeSchedule,omitempty" gorm:"foreignKey:ScheduleID"` + EdgeSchedule *EdgeSchedule `json:"EdgeSchedule,omitempty" gorm:"serializer:json"` } // ScheduleID represents a schedule identifier. @@ -911,7 +911,7 @@ type ( // URL to a logo that will be displayed on the login page as well as on top of the sidebar. Will use default Portainer logo when value is empty string LogoURL string `json:"LogoURL" example:"https://mycompany.mydomain.tld/logo.png"` // A list of label name & value that will be used to hide containers when querying containers - BlackListedLabels []Pair `json:"BlackListedLabels" gorm:"serializer:json"` + BlackListedLabels MultiPair `json:"BlackListedLabels" gorm:"serializer:json"` // Active authentication method for the Portainer instance. Valid values are: 1 for internal, 2 for LDAP, or 3 for oauth AuthenticationMethod AuthenticationMethod `json:"AuthenticationMethod" example:"1"` InternalAuthSettings InternalAuthSettings `json:"InternalAuthSettings" gorm:"serializer:json"` @@ -1001,13 +1001,13 @@ type ( // Stack type. 1 for a Swarm stack, 2 for a Compose stack Type StackType `json:"Type" example:"2"` // Environment(Endpoint) identifier. Reference the environment(endpoint) that will be used for deployment - EndpointID EndpointID `json:"EndpointId" example:"1"` + EndpointID EndpointID `json:"EndpointId" example:"1" gorm:"foreignKey=ID"` // Cluster identifier of the Swarm cluster where the stack is deployed SwarmID string `json:"SwarmId" example:"jpofkc0i9uo9wtx1zesuk649w"` // Path to the Stack file EntryPoint string `json:"EntryPoint" example:"docker-compose.yml"` // A list of environment(endpoint) variables used during stack deployment - Env []Pair `json:"Env"` + Env MultiPair `json:"Env"` // ResourceControl *ResourceControl `json:"ResourceControl" gorm:"serializer:json"` // Stack status (1 - active, 2 - inactive) @@ -1023,7 +1023,7 @@ type ( // The username which last updated this stack UpdatedBy string `example:"bob"` // Only applies when deploying stack with multiple files - AdditionalFiles []string `json:"AdditionalFiles"` + AdditionalFiles []string `json:"AdditionalFiles" gorm:"serializer:json"` // The auto update settings of a git stack AutoUpdate *AutoUpdateSettings `json:"AutoUpdate" gorm:"serializer:json"` // The stack deployment option @@ -1164,7 +1164,7 @@ type ( // A list of ports exposed by the container Ports []string `json:"ports,omitempty" example:"8080:80/tcp" gorm:"serializer:json"` // Container labels - Labels []Pair `json:"labels,omitempty" gorm:"serializer:json"` + Labels MultiPair `json:"labels,omitempty" gorm:"serializer:json"` // Whether the container should be started in privileged mode Privileged bool `json:"privileged,omitempty" example:"true"` // Whether the container should be started in @@ -1316,7 +1316,7 @@ type ( ID WebhookID `json:"Id" example:"1" gorm:"unique,primaryKey,autoIncrement"` Token string `json:"Token"` ResourceID string `json:"ResourceId"` - EndpointID EndpointID `json:"EndpointId"` + EndpointID EndpointID `json:"EndpointId" gorm:"foreignKey=ID"` RegistryID RegistryID `json:"RegistryId"` WebhookType WebhookType `json:"Type"` } @@ -1328,7 +1328,7 @@ type ( WebhookType int Snapshot struct { - EndpointID EndpointID `json:"EndpointId"` + EndpointID EndpointID `json:"EndpointId" gorm:"foreignKey=ID"` Docker *DockerSnapshot `json:"Docker" gorm:"serializer:json"` Kubernetes *KubernetesSnapshot `json:"Kubernetes" gorm:"serializer:json"` } @@ -2050,12 +2050,12 @@ func (j Pair) Value() (driver.Value, error) { return json.Marshal(j) } -func (GPUs) GormDataType() string { +func (MultiPair) GormDataType() string { return "json" } // Scan scan value into Jsonb, implements sql.Scanner interface -func (j *GPUs) Scan(value interface{}) error { +func (j *MultiPair) Scan(value interface{}) error { bytes, ok := value.([]byte) if !ok { return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value)) @@ -2066,7 +2066,7 @@ func (j *GPUs) Scan(value interface{}) error { } // Value return json value, implement driver.Valuer interface -func (j GPUs) Value() (driver.Value, error) { +func (j MultiPair) Value() (driver.Value, error) { if len(j) == 0 { return nil, nil } diff --git a/api/stacks/stackbuilders/stack_payload.go b/api/stacks/stackbuilders/stack_payload.go index 86a90456d..84b42b3a6 100644 --- a/api/stacks/stackbuilders/stack_payload.go +++ b/api/stacks/stackbuilders/stack_payload.go @@ -16,7 +16,7 @@ type StackPayload struct { StackFileContent string Webhook string // A list of environment(endpoint) variables used during stack deployment - Env []portainer.Pair + Env portainer.MultiPair // Optional auto update configuration AutoUpdate *portainer.AutoUpdateSettings // Whether the stack is from a app template