diff --git a/api/.golangci.yaml b/api/.golangci.yaml index 5675e6deb..5b885de19 100644 --- a/api/.golangci.yaml +++ b/api/.golangci.yaml @@ -13,6 +13,8 @@ linters-settings: rules: main: deny: + - pkg: 'encoding/json' + desc: 'use github.com/segmentio/encoding/json' - pkg: 'github.com/sirupsen/logrus' desc: 'logging is allowed only by github.com/rs/zerolog' - pkg: 'golang.org/x/exp' diff --git a/api/database/boltdb/db.go b/api/database/boltdb/db.go index 52b7cbea4..62aa8f3a6 100644 --- a/api/database/boltdb/db.go +++ b/api/database/boltdb/db.go @@ -255,7 +255,7 @@ func (connection *DbConnection) UpdateObjectFunc(bucketName string, key []byte, return fmt.Errorf("%w (bucket=%s, key=%s)", dserrors.ErrObjectNotFound, bucketName, keyToString(key)) } - err := connection.UnmarshalObjectWithJsoniter(data, object) + err := connection.UnmarshalObject(data, object) if err != nil { return err } diff --git a/api/database/boltdb/export.go b/api/database/boltdb/export.go index 00712c734..a95eb7dfe 100644 --- a/api/database/boltdb/export.go +++ b/api/database/boltdb/export.go @@ -1,10 +1,10 @@ package boltdb import ( - "encoding/json" "time" "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" bolt "go.etcd.io/bbolt" ) diff --git a/api/database/boltdb/json.go b/api/database/boltdb/json.go index 045eb2720..2c9192d4e 100644 --- a/api/database/boltdb/json.go +++ b/api/database/boltdb/json.go @@ -1,34 +1,41 @@ package boltdb import ( + "bytes" "crypto/aes" "crypto/cipher" "crypto/rand" - "encoding/json" "fmt" "io" - jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" + "github.com/segmentio/encoding/json" ) var errEncryptedStringTooShort = fmt.Errorf("encrypted string too short") // MarshalObject encodes an object to binary format -func (connection *DbConnection) MarshalObject(object interface{}) (data []byte, err error) { +func (connection *DbConnection) MarshalObject(object interface{}) ([]byte, error) { + buf := &bytes.Buffer{} + // Special case for the VERSION bucket. Here we're not using json if v, ok := object.(string); ok { - data = []byte(v) + buf.WriteString(v) } else { - data, err = json.Marshal(object) - if err != nil { - return data, err + enc := json.NewEncoder(buf) + enc.SetSortMapKeys(false) + enc.SetAppendNewline(false) + + if err := enc.Encode(object); err != nil { + return nil, err } } + if connection.getEncryptionKey() == nil { - return data, nil + return buf.Bytes(), nil } - return encrypt(data, connection.getEncryptionKey()) + + return encrypt(buf.Bytes(), connection.getEncryptionKey()) } // UnmarshalObject decodes an object from binary data @@ -54,31 +61,6 @@ func (connection *DbConnection) UnmarshalObject(data []byte, object interface{}) return err } -// UnmarshalObjectWithJsoniter decodes an object from binary data -// using the jsoniter library. It is mainly used to accelerate environment(endpoint) -// decoding at the moment. -func (connection *DbConnection) UnmarshalObjectWithJsoniter(data []byte, object interface{}) error { - if connection.getEncryptionKey() != nil { - var err error - data, err = decrypt(data, connection.getEncryptionKey()) - if err != nil { - return err - } - } - var jsoni = jsoniter.ConfigCompatibleWithStandardLibrary - err := jsoni.Unmarshal(data, &object) - if err != nil { - if s, ok := object.(*string); ok { - *s = string(data) - return nil - } - - return err - } - - return nil -} - // mmm, don't have a KMS .... aes GCM seems the most likely from // https://gist.github.com/atoponce/07d8d4c833873be2f68c34f9afc5a78a#symmetric-encryption diff --git a/api/database/boltdb/tx.go b/api/database/boltdb/tx.go index 7cc8bee7a..073b279bf 100644 --- a/api/database/boltdb/tx.go +++ b/api/database/boltdb/tx.go @@ -28,7 +28,7 @@ func (tx *DbTransaction) GetObject(bucketName string, key []byte, object interfa return fmt.Errorf("%w (bucket=%s, key=%s)", dserrors.ErrObjectNotFound, bucketName, keyToString(key)) } - return tx.conn.UnmarshalObjectWithJsoniter(value, object) + return tx.conn.UnmarshalObject(value, object) } func (tx *DbTransaction) UpdateObject(bucketName string, key []byte, object interface{}) error { @@ -134,7 +134,7 @@ func (tx *DbTransaction) GetAllWithJsoniter(bucketName string, obj interface{}, bucket := tx.tx.Bucket([]byte(bucketName)) return bucket.ForEach(func(k []byte, v []byte) error { - err := tx.conn.UnmarshalObjectWithJsoniter(v, obj) + err := tx.conn.UnmarshalObject(v, obj) if err == nil { obj, err = appendFn(obj) } @@ -147,7 +147,7 @@ func (tx *DbTransaction) GetAllWithKeyPrefix(bucketName string, keyPrefix []byte cursor := tx.tx.Bucket([]byte(bucketName)).Cursor() for k, v := cursor.Seek(keyPrefix); k != nil && bytes.HasPrefix(k, keyPrefix); k, v = cursor.Next() { - err := tx.conn.UnmarshalObjectWithJsoniter(v, obj) + err := tx.conn.UnmarshalObject(v, obj) if err != nil { return err } diff --git a/api/datastore/migrate_data_test.go b/api/datastore/migrate_data_test.go index 7917bcd83..389481b95 100644 --- a/api/datastore/migrate_data_test.go +++ b/api/datastore/migrate_data_test.go @@ -2,7 +2,6 @@ package datastore import ( "bytes" - "encoding/json" "fmt" "io" "os" @@ -11,10 +10,11 @@ import ( "testing" "github.com/portainer/portainer/api/database/boltdb" + "github.com/portainer/portainer/api/database/models" "github.com/google/go-cmp/cmp" - "github.com/portainer/portainer/api/database/models" "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) // testVersion is a helper which tests current store version against wanted version diff --git a/api/datastore/services.go b/api/datastore/services.go index b0e5c764e..802989d3d 100644 --- a/api/datastore/services.go +++ b/api/datastore/services.go @@ -1,7 +1,6 @@ package datastore import ( - "encoding/json" "fmt" "os" @@ -38,6 +37,7 @@ import ( "github.com/portainer/portainer/api/dataservices/webhook" "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) // Store defines the implementation of portainer.DataStore using diff --git a/api/datastore/test_data/output_24_to_latest.json b/api/datastore/test_data/output_24_to_latest.json index d10397ad5..9cd35f833 100644 --- a/api/datastore/test_data/output_24_to_latest.json +++ b/api/datastore/test_data/output_24_to_latest.json @@ -27,9 +27,7 @@ ], "endpoints": [ { - "Agent": { - "Version": "" - }, + "Agent": {}, "AuthorizedTeams": null, "AuthorizedUsers": null, "AzureCredentials": { @@ -38,71 +36,42 @@ "TenantID": "" }, "ComposeSyntaxMaxVersion": "", - "Edge": { - "AsyncMode": false, - "CommandInterval": 0, - "PingInterval": 0, - "SnapshotInterval": 0 - }, + "Edge": {}, "EdgeCheckinInterval": 0, - "EdgeKey": "", - "EnableGPUManagement": false, "Gpus": [], "GroupId": 1, - "Heartbeat": false, "Id": 1, - "IsEdgeDevice": false, "Kubernetes": { "Configuration": { - "AllowNoneIngressClass": false, - "EnableResourceOverCommit": false, "IngressAvailabilityPerNamespace": true, "IngressClasses": null, - "ResourceOverCommitPercentage": 0, - "RestrictDefaultNamespace": false, - "StorageClasses": null, - "UseLoadBalancer": false, - "UseServerMetrics": false - }, - "Flags": { - "IsServerIngressClassDetected": false, - "IsServerMetricsDetected": false, - "IsServerStorageDetected": false + "StorageClasses": null }, + "Flags": {}, "Snapshots": [] }, - "LastCheckInDate": 0, "Name": "local", "PostInitMigrations": { "MigrateGPUs": true, "MigrateIngresses": true }, - "PublicURL": "", - "QueryDate": 0, "SecuritySettings": { "allowBindMountsForRegularUsers": true, "allowContainerCapabilitiesForRegularUsers": true, "allowDeviceMappingForRegularUsers": true, "allowHostNamespaceForRegularUsers": true, "allowPrivilegedModeForRegularUsers": true, - "allowStackManagementForRegularUsers": true, - "allowSysctlSettingForRegularUsers": false, - "allowVolumeBrowserForRegularUsers": false, - "enableHostManagementFeatures": false + "allowStackManagementForRegularUsers": true }, "Snapshots": [], "Status": 1, - "TLSConfig": { - "TLS": false, - "TLSSkipVerify": false - }, + "TLSConfig": {}, "TagIds": [], "Tags": null, "TeamAccessPolicies": {}, "Type": 1, "URL": "unix:///var/run/docker.sock", - "UserAccessPolicies": {}, - "UserTrusted": false + "UserAccessPolicies": {} } ], "registries": [ @@ -110,7 +79,6 @@ "Authentication": true, "AuthorizedTeams": null, "AuthorizedUsers": null, - "BaseURL": "", "Ecr": { "Region": "" }, @@ -124,8 +92,7 @@ "Name": "canister.io", "Password": "MjWbx8A6YK7cw7", "Quay": { - "OrganisationName": "", - "UseOrganisation": false + "OrganisationName": "" }, "RegistryAccesses": { "1": { @@ -154,34 +121,28 @@ "UserAccesses": [] }, { - "AdministratorsOnly": false, "Id": 3, "Public": true, "ResourceId": "1_alpine", "SubResourceIds": [], - "System": false, "TeamAccesses": [], "Type": 6, "UserAccesses": [] }, { - "AdministratorsOnly": false, "Id": 4, "Public": true, "ResourceId": "1_redis", "SubResourceIds": [], - "System": false, "TeamAccesses": [], "Type": 6, "UserAccesses": [] }, { - "AdministratorsOnly": false, "Id": 5, "Public": false, "ResourceId": "1_nginx", "SubResourceIds": [], - "System": false, "TeamAccesses": [ { "AccessLevel": 1, @@ -577,18 +538,14 @@ } ], "settings": { - "AgentSecret": "", "AllowBindMountsForRegularUsers": true, "AllowContainerCapabilitiesForRegularUsers": true, "AllowDeviceMappingForRegularUsers": true, "AllowHostNamespaceForRegularUsers": true, "AllowPrivilegedModeForRegularUsers": true, "AllowStackManagementForRegularUsers": true, - "AllowVolumeBrowserForRegularUsers": false, "AuthenticationMethod": 1, "BlackListedLabels": [], - "DisplayDonationHeader": false, - "DisplayExternalContributors": false, "Edge": { "AsyncMode": false, "CommandInterval": 0, @@ -596,20 +553,13 @@ "SnapshotInterval": 0 }, "EdgeAgentCheckinInterval": 5, - "EdgePortainerUrl": "", - "EnableEdgeComputeFeatures": false, - "EnableHostManagementFeatures": false, "EnableTelemetry": true, - "EnforceEdgeID": false, "FeatureFlagSettings": null, - "GlobalDeploymentOptions": { - "hideStacksFunctionality": false - }, + "GlobalDeploymentOptions": {}, "HelmRepositoryURL": "https://charts.bitnami.com/bitnami", "InternalAuthSettings": { "RequiredPasswordLength": 12 }, - "IsDockerDesktopExtension": false, "KubeconfigExpiry": "0", "KubectlShellImage": "portainer/kubectl-shell", "LDAPSettings": { @@ -618,8 +568,7 @@ "GroupSearchSettings": [ { "GroupAttribute": "", - "GroupBaseDN": "", - "GroupFilter": "" + "GroupBaseDN": "" } ], "ReaderDN": "", @@ -630,14 +579,9 @@ "UserNameAttribute": "" } ], - "StartTLS": false, - "TLSConfig": { - "TLS": false, - "TLSSkipVerify": false - }, + "TLSConfig": {}, "URL": "" }, - "LogoURL": "", "OAuthSettings": { "AccessTokenURI": "", "AuthorizationURI": "", @@ -652,10 +596,8 @@ "Scopes": "", "UserIdentifier": "" }, - "ShowKomposeBuildOption": false, "SnapshotInterval": "5m", "TemplatesURL": "https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json", - "TrustOnFirstConnect": false, "UserSessionTimeout": "8h", "fdoConfiguration": { "enabled": false, @@ -794,34 +736,23 @@ "UnhealthyContainerCount": 0, "VolumeCount": 10 }, - "EndpointId": 1, - "Kubernetes": null + "EndpointId": 1 } ], "ssl": { - "certPath": "", - "httpEnabled": true, - "keyPath": "", - "selfSigned": false + "httpEnabled": true }, "stacks": [ { "AdditionalFiles": null, - "AutoUpdate": null, "CreatedBy": "", "CreationDate": 0, "EndpointId": 1, "EntryPoint": "docker/alpine37-compose.yml", "Env": [], - "FromAppTemplate": false, - "GitConfig": null, "Id": 2, - "IsComposeFormat": false, "Name": "alpine", - "Namespace": "", - "Option": null, "ProjectPath": "/home/prabhat/portainer/data/ce1.25/compose/2", - "ResourceControl": null, "Status": 1, "SwarmId": "s3fd604zdba7z13tbq2x6lyue", "Type": 1, @@ -830,46 +761,30 @@ }, { "AdditionalFiles": null, - "AutoUpdate": null, "CreatedBy": "", "CreationDate": 0, "EndpointId": 1, "EntryPoint": "docker-compose.yml", "Env": [], - "FromAppTemplate": false, - "GitConfig": null, "Id": 5, - "IsComposeFormat": false, "Name": "redis", - "Namespace": "", - "Option": null, "ProjectPath": "/home/prabhat/portainer/data/ce1.25/compose/5", - "ResourceControl": null, "Status": 1, - "SwarmId": "", "Type": 2, "UpdateDate": 0, "UpdatedBy": "" }, { "AdditionalFiles": null, - "AutoUpdate": null, "CreatedBy": "", "CreationDate": 0, "EndpointId": 1, "EntryPoint": "docker-compose.yml", "Env": [], - "FromAppTemplate": false, - "GitConfig": null, "Id": 6, - "IsComposeFormat": false, "Name": "nginx", - "Namespace": "", - "Option": null, "ProjectPath": "/home/prabhat/portainer/data/ce1.25/compose/6", - "ResourceControl": null, "Status": 1, - "SwarmId": "", "Type": 2, "UpdateDate": 0, "UpdatedBy": "" @@ -881,9 +796,7 @@ "Name": "hello" } ], - "tunnel_server": { - "PrivateKeySeed": "" - }, + "tunnel_server": {}, "users": [ { "EndpointAuthorizations": null, @@ -908,11 +821,8 @@ "PortainerUserRevokeToken": true }, "Role": 1, - "ThemeSettings": { - "color": "" - }, + "ThemeSettings": {}, "TokenIssueAt": 0, - "UserTheme": "", "Username": "admin" }, { @@ -938,11 +848,8 @@ "PortainerUserRevokeToken": true }, "Role": 1, - "ThemeSettings": { - "color": "" - }, + "ThemeSettings": {}, "TokenIssueAt": 0, - "UserTheme": "", "Username": "prabhat" } ], diff --git a/api/exec/swarm_stack.go b/api/exec/swarm_stack.go index f8b031976..5745098ee 100644 --- a/api/exec/swarm_stack.go +++ b/api/exec/swarm_stack.go @@ -2,7 +2,6 @@ package exec import ( "bytes" - "encoding/json" "errors" "fmt" "os" @@ -15,7 +14,9 @@ import ( "github.com/portainer/portainer/api/dataservices" "github.com/portainer/portainer/api/internal/registryutils" "github.com/portainer/portainer/api/stacks/stackutils" + "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) // SwarmStackManager represents a service for managing stacks. diff --git a/api/filesystem/filesystem.go b/api/filesystem/filesystem.go index 3042f7a8b..9eee3b97e 100644 --- a/api/filesystem/filesystem.go +++ b/api/filesystem/filesystem.go @@ -2,7 +2,6 @@ package filesystem import ( "bytes" - "encoding/json" "encoding/pem" "errors" "fmt" @@ -15,6 +14,7 @@ import ( "github.com/gofrs/uuid" "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) const ( diff --git a/api/git/azure.go b/api/git/azure.go index b85ffe7b4..9fabeb066 100644 --- a/api/git/azure.go +++ b/api/git/azure.go @@ -2,7 +2,6 @@ package git import ( "context" - "encoding/json" "fmt" "io" "net/http" @@ -11,12 +10,13 @@ import ( "strings" "time" - "github.com/go-git/go-git/v5/plumbing/filemode" "github.com/portainer/portainer/api/archive" "github.com/portainer/portainer/api/crypto" gittypes "github.com/portainer/portainer/api/git/types" + "github.com/go-git/go-git/v5/plumbing/filemode" "github.com/pkg/errors" + "github.com/segmentio/encoding/json" ) const ( diff --git a/api/hostmanagement/openamt/authorization.go b/api/hostmanagement/openamt/authorization.go index 463cb4dcf..1c8c2cfe4 100644 --- a/api/hostmanagement/openamt/authorization.go +++ b/api/hostmanagement/openamt/authorization.go @@ -2,12 +2,13 @@ package openamt import ( "bytes" - "encoding/json" "fmt" "io" "net/http" portainer "github.com/portainer/portainer/api" + + "github.com/segmentio/encoding/json" ) type authenticationResponse struct { diff --git a/api/hostmanagement/openamt/configCIRA.go b/api/hostmanagement/openamt/configCIRA.go index feac5821a..f76c8938a 100644 --- a/api/hostmanagement/openamt/configCIRA.go +++ b/api/hostmanagement/openamt/configCIRA.go @@ -2,7 +2,6 @@ package openamt import ( "encoding/base64" - "encoding/json" "encoding/pem" "fmt" "io" @@ -11,6 +10,8 @@ import ( "strings" portainer "github.com/portainer/portainer/api" + + "github.com/segmentio/encoding/json" ) type CIRAConfig struct { diff --git a/api/hostmanagement/openamt/configDevice.go b/api/hostmanagement/openamt/configDevice.go index 202d5be3e..8afdfbd42 100644 --- a/api/hostmanagement/openamt/configDevice.go +++ b/api/hostmanagement/openamt/configDevice.go @@ -1,11 +1,12 @@ package openamt import ( - "encoding/json" "fmt" "strings" portainer "github.com/portainer/portainer/api" + + "github.com/segmentio/encoding/json" ) type Device struct { diff --git a/api/hostmanagement/openamt/configDomain.go b/api/hostmanagement/openamt/configDomain.go index 5117defdf..46eb3b753 100644 --- a/api/hostmanagement/openamt/configDomain.go +++ b/api/hostmanagement/openamt/configDomain.go @@ -1,11 +1,12 @@ package openamt import ( - "encoding/json" "fmt" "net/http" portainer "github.com/portainer/portainer/api" + + "github.com/segmentio/encoding/json" ) type ( diff --git a/api/hostmanagement/openamt/configProfile.go b/api/hostmanagement/openamt/configProfile.go index 5e80205b4..d8a08ae50 100644 --- a/api/hostmanagement/openamt/configProfile.go +++ b/api/hostmanagement/openamt/configProfile.go @@ -1,11 +1,12 @@ package openamt import ( - "encoding/json" "fmt" "net/http" portainer "github.com/portainer/portainer/api" + + "github.com/segmentio/encoding/json" ) type ( diff --git a/api/hostmanagement/openamt/deviceActions.go b/api/hostmanagement/openamt/deviceActions.go index 39960add1..76b5344b7 100644 --- a/api/hostmanagement/openamt/deviceActions.go +++ b/api/hostmanagement/openamt/deviceActions.go @@ -1,12 +1,13 @@ package openamt import ( - "encoding/json" "fmt" "net/http" "strings" portainer "github.com/portainer/portainer/api" + + "github.com/segmentio/encoding/json" ) type ActionResponse struct { diff --git a/api/hostmanagement/openamt/deviceFeatures.go b/api/hostmanagement/openamt/deviceFeatures.go index cce18a8db..c74a1e029 100644 --- a/api/hostmanagement/openamt/deviceFeatures.go +++ b/api/hostmanagement/openamt/deviceFeatures.go @@ -1,11 +1,12 @@ package openamt import ( - "encoding/json" "fmt" "net/http" portainer "github.com/portainer/portainer/api" + + "github.com/segmentio/encoding/json" ) func (service *Service) enableDeviceFeatures(configuration portainer.OpenAMTConfiguration, deviceGUID string, features portainer.OpenAMTDeviceEnabledFeatures) error { diff --git a/api/hostmanagement/openamt/openamt.go b/api/hostmanagement/openamt/openamt.go index c7a738fc4..82fbeafbf 100644 --- a/api/hostmanagement/openamt/openamt.go +++ b/api/hostmanagement/openamt/openamt.go @@ -2,7 +2,6 @@ package openamt import ( "bytes" - "encoding/json" "errors" "fmt" "io" @@ -12,6 +11,7 @@ import ( portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/crypto" + "github.com/segmentio/encoding/json" "golang.org/x/sync/errgroup" ) diff --git a/api/http/client/client.go b/api/http/client/client.go index d28364e07..2a7d0bf9b 100644 --- a/api/http/client/client.go +++ b/api/http/client/client.go @@ -2,7 +2,6 @@ package client import ( "crypto/tls" - "encoding/json" "errors" "fmt" "io" @@ -14,6 +13,7 @@ import ( portainer "github.com/portainer/portainer/api" "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) var errInvalidResponseStatus = errors.New("invalid response status (expecting 200)") diff --git a/api/http/handler/customtemplates/customtemplate_create.go b/api/http/handler/customtemplates/customtemplate_create.go index a93f1facf..024ca1189 100644 --- a/api/http/handler/customtemplates/customtemplate_create.go +++ b/api/http/handler/customtemplates/customtemplate_create.go @@ -1,7 +1,6 @@ package customtemplates import ( - "encoding/json" "errors" "fmt" "net/http" @@ -21,6 +20,7 @@ import ( "github.com/asaskevich/govalidator" "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) func (handler *Handler) customTemplateCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { diff --git a/api/http/handler/customtemplates/customtemplate_git_fetch_test.go b/api/http/handler/customtemplates/customtemplate_git_fetch_test.go index 3765a30f6..1e645abd9 100644 --- a/api/http/handler/customtemplates/customtemplate_git_fetch_test.go +++ b/api/http/handler/customtemplates/customtemplate_git_fetch_test.go @@ -2,7 +2,6 @@ package customtemplates import ( "bytes" - "encoding/json" "fmt" "io" "io/fs" @@ -20,6 +19,8 @@ import ( "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/internal/authorization" "github.com/portainer/portainer/api/jwt" + + "github.com/segmentio/encoding/json" "github.com/stretchr/testify/assert" ) diff --git a/api/http/handler/edgestacks/edgestack_create_test.go b/api/http/handler/edgestacks/edgestack_create_test.go index cc62de94e..88ead7c99 100644 --- a/api/http/handler/edgestacks/edgestack_create_test.go +++ b/api/http/handler/edgestacks/edgestack_create_test.go @@ -2,13 +2,14 @@ package edgestacks import ( "bytes" - "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" portainer "github.com/portainer/portainer/api" + + "github.com/segmentio/encoding/json" ) // Create diff --git a/api/http/handler/edgestacks/edgestack_delete_test.go b/api/http/handler/edgestacks/edgestack_delete_test.go index a225ff468..212dccb7e 100644 --- a/api/http/handler/edgestacks/edgestack_delete_test.go +++ b/api/http/handler/edgestacks/edgestack_delete_test.go @@ -1,13 +1,14 @@ package edgestacks import ( - "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" portainer "github.com/portainer/portainer/api" + + "github.com/segmentio/encoding/json" ) // Delete diff --git a/api/http/handler/edgestacks/edgestack_status_update_test.go b/api/http/handler/edgestacks/edgestack_status_update_test.go index 4ae63aa3f..27a6c1d39 100644 --- a/api/http/handler/edgestacks/edgestack_status_update_test.go +++ b/api/http/handler/edgestacks/edgestack_status_update_test.go @@ -2,13 +2,14 @@ package edgestacks import ( "bytes" - "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" portainer "github.com/portainer/portainer/api" + + "github.com/segmentio/encoding/json" ) // Update Status diff --git a/api/http/handler/edgestacks/edgestack_update_test.go b/api/http/handler/edgestacks/edgestack_update_test.go index 90542178a..973cec42d 100644 --- a/api/http/handler/edgestacks/edgestack_update_test.go +++ b/api/http/handler/edgestacks/edgestack_update_test.go @@ -2,7 +2,6 @@ package edgestacks import ( "bytes" - "encoding/json" "fmt" "net/http" "net/http/httptest" @@ -10,6 +9,8 @@ import ( "testing" portainer "github.com/portainer/portainer/api" + + "github.com/segmentio/encoding/json" ) // Update diff --git a/api/http/handler/edgetemplates/edgetemplate_list.go b/api/http/handler/edgetemplates/edgetemplate_list.go index 70f6b585c..91fa9cf6c 100644 --- a/api/http/handler/edgetemplates/edgetemplate_list.go +++ b/api/http/handler/edgetemplates/edgetemplate_list.go @@ -1,13 +1,14 @@ package edgetemplates import ( - "encoding/json" "net/http" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/client" httperror "github.com/portainer/portainer/pkg/libhttp/error" "github.com/portainer/portainer/pkg/libhttp/response" + + "github.com/segmentio/encoding/json" ) type templateFileFormat struct { diff --git a/api/http/handler/endpointedge/endpointedge_status_inspect_test.go b/api/http/handler/endpointedge/endpointedge_status_inspect_test.go index 936261783..41b8eb2de 100644 --- a/api/http/handler/endpointedge/endpointedge_status_inspect_test.go +++ b/api/http/handler/endpointedge/endpointedge_status_inspect_test.go @@ -2,7 +2,6 @@ package endpointedge import ( "context" - "encoding/json" "fmt" "net/http" "net/http/httptest" @@ -17,6 +16,7 @@ import ( "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/jwt" + "github.com/segmentio/encoding/json" "github.com/stretchr/testify/assert" ) diff --git a/api/http/handler/endpoints/endpoint_dockerhub_status.go b/api/http/handler/endpoints/endpoint_dockerhub_status.go index cb4f8a6aa..4207d7735 100644 --- a/api/http/handler/endpoints/endpoint_dockerhub_status.go +++ b/api/http/handler/endpoints/endpoint_dockerhub_status.go @@ -1,7 +1,6 @@ package endpoints import ( - "encoding/json" "errors" "fmt" "io" @@ -15,6 +14,8 @@ import ( httperror "github.com/portainer/portainer/pkg/libhttp/error" "github.com/portainer/portainer/pkg/libhttp/request" "github.com/portainer/portainer/pkg/libhttp/response" + + "github.com/segmentio/encoding/json" ) type dockerhubStatusResponse struct { diff --git a/api/http/handler/endpoints/endpoint_list_test.go b/api/http/handler/endpoints/endpoint_list_test.go index 6806de38e..cd53091b4 100644 --- a/api/http/handler/endpoints/endpoint_list_test.go +++ b/api/http/handler/endpoints/endpoint_list_test.go @@ -1,7 +1,6 @@ package endpoints import ( - "encoding/json" "fmt" "io" "net/http" @@ -13,6 +12,8 @@ import ( "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/internal/snapshot" "github.com/portainer/portainer/api/internal/testhelpers" + + "github.com/segmentio/encoding/json" "github.com/stretchr/testify/assert" ) @@ -27,13 +28,13 @@ func Test_EndpointList_AgentVersion(t *testing.T) { GroupID: 1, Type: portainer.AgentOnDockerEnvironment, Agent: struct { - Version string "example:\"1.0.0\"" + Version string `json:"Version,omitempty" example:"1.0.0"` }{ Version: "1.0.0", }, } version2Endpoint := portainer.Endpoint{ID: 2, GroupID: 1, Type: portainer.AgentOnDockerEnvironment, Agent: struct { - Version string "example:\"1.0.0\"" + Version string `json:"Version,omitempty" example:"1.0.0"` }{Version: "2.0.0"}} noVersionEndpoint := portainer.Endpoint{ID: 3, Type: portainer.AgentOnDockerEnvironment, GroupID: 1} notAgentEnvironments := portainer.Endpoint{ID: 4, Type: portainer.DockerEnvironment, GroupID: 1} diff --git a/api/http/handler/endpoints/filter_test.go b/api/http/handler/endpoints/filter_test.go index dc1b10184..27f4aa216 100644 --- a/api/http/handler/endpoints/filter_test.go +++ b/api/http/handler/endpoints/filter_test.go @@ -22,12 +22,12 @@ func Test_Filter_AgentVersion(t *testing.T) { version1Endpoint := portainer.Endpoint{ID: 1, GroupID: 1, Type: portainer.AgentOnDockerEnvironment, Agent: struct { - Version string "example:\"1.0.0\"" + Version string `json:"Version,omitempty" example:"1.0.0"` }{Version: "1.0.0"}} version2Endpoint := portainer.Endpoint{ID: 2, GroupID: 1, Type: portainer.AgentOnDockerEnvironment, Agent: struct { - Version string "example:\"1.0.0\"" + Version string `json:"Version,omitempty" example:"1.0.0"` }{Version: "2.0.0"}} noVersionEndpoint := portainer.Endpoint{ID: 3, GroupID: 1, Type: portainer.AgentOnDockerEnvironment, diff --git a/api/http/handler/helm/helm_install_test.go b/api/http/handler/helm/helm_install_test.go index 6b061a16c..5e3904550 100644 --- a/api/http/handler/helm/helm_install_test.go +++ b/api/http/handler/helm/helm_install_test.go @@ -2,15 +2,13 @@ package helm import ( "bytes" - "encoding/json" "io" "net/http" "net/http/httptest" "testing" - "github.com/portainer/portainer/api/datastore" - portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/datastore" "github.com/portainer/portainer/api/exec/exectest" "github.com/portainer/portainer/api/http/security" helper "github.com/portainer/portainer/api/internal/testhelpers" @@ -19,6 +17,8 @@ import ( "github.com/portainer/portainer/pkg/libhelm/binary/test" "github.com/portainer/portainer/pkg/libhelm/options" "github.com/portainer/portainer/pkg/libhelm/release" + + "github.com/segmentio/encoding/json" "github.com/stretchr/testify/assert" ) diff --git a/api/http/handler/helm/helm_list_test.go b/api/http/handler/helm/helm_list_test.go index d05ba7a73..4de16589f 100644 --- a/api/http/handler/helm/helm_list_test.go +++ b/api/http/handler/helm/helm_list_test.go @@ -1,7 +1,6 @@ package helm import ( - "encoding/json" "io" "net/http" "net/http/httptest" @@ -11,14 +10,15 @@ import ( "github.com/portainer/portainer/api/datastore" "github.com/portainer/portainer/api/exec/exectest" "github.com/portainer/portainer/api/http/security" + helper "github.com/portainer/portainer/api/internal/testhelpers" "github.com/portainer/portainer/api/jwt" "github.com/portainer/portainer/api/kubernetes" "github.com/portainer/portainer/pkg/libhelm/binary/test" "github.com/portainer/portainer/pkg/libhelm/options" "github.com/portainer/portainer/pkg/libhelm/release" - "github.com/stretchr/testify/assert" - helper "github.com/portainer/portainer/api/internal/testhelpers" + "github.com/segmentio/encoding/json" + "github.com/stretchr/testify/assert" ) func Test_helmList(t *testing.T) { diff --git a/api/http/handler/hostmanagement/openamt/amtrpc.go b/api/http/handler/hostmanagement/openamt/amtrpc.go index 07aa71320..ec3c457aa 100644 --- a/api/http/handler/hostmanagement/openamt/amtrpc.go +++ b/api/http/handler/hostmanagement/openamt/amtrpc.go @@ -2,7 +2,6 @@ package openamt import ( "context" - "encoding/json" "fmt" "io" "net/http" @@ -20,6 +19,7 @@ import ( "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) type HostInfo struct { diff --git a/api/http/handler/motd/motd.go b/api/http/handler/motd/motd.go index 669f6e7b2..dd2112c16 100644 --- a/api/http/handler/motd/motd.go +++ b/api/http/handler/motd/motd.go @@ -1,7 +1,6 @@ package motd import ( - "encoding/json" "net/http" "strings" @@ -9,6 +8,8 @@ import ( "github.com/portainer/portainer/api/http/client" "github.com/portainer/portainer/pkg/libcrypto" "github.com/portainer/portainer/pkg/libhttp/response" + + "github.com/segmentio/encoding/json" ) type motdResponse struct { diff --git a/api/http/handler/system/version.go b/api/http/handler/system/version.go index 374f4e0a8..8972627f7 100644 --- a/api/http/handler/system/version.go +++ b/api/http/handler/system/version.go @@ -1,7 +1,6 @@ package system import ( - "encoding/json" "net/http" portainer "github.com/portainer/portainer/api" @@ -11,6 +10,7 @@ import ( "github.com/coreos/go-semver/semver" "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) type versionResponse struct { diff --git a/api/http/handler/system/version_test.go b/api/http/handler/system/version_test.go index 47a748bf3..f7628fc59 100644 --- a/api/http/handler/system/version_test.go +++ b/api/http/handler/system/version_test.go @@ -1,7 +1,6 @@ package system import ( - "encoding/json" "fmt" "io" "net/http" @@ -15,6 +14,8 @@ import ( "github.com/portainer/portainer/api/demo" "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/jwt" + + "github.com/segmentio/encoding/json" "github.com/stretchr/testify/assert" ) diff --git a/api/http/handler/teams/team_list_test.go b/api/http/handler/teams/team_list_test.go index 063aca097..a94c966cb 100644 --- a/api/http/handler/teams/team_list_test.go +++ b/api/http/handler/teams/team_list_test.go @@ -1,7 +1,6 @@ package teams import ( - "encoding/json" "fmt" "io" "net/http" @@ -15,6 +14,8 @@ import ( "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/internal/authorization" "github.com/portainer/portainer/api/jwt" + + "github.com/segmentio/encoding/json" "github.com/stretchr/testify/assert" ) diff --git a/api/http/handler/templates/template_file.go b/api/http/handler/templates/template_file.go index 043d0d0a1..e56ccc305 100644 --- a/api/http/handler/templates/template_file.go +++ b/api/http/handler/templates/template_file.go @@ -1,7 +1,6 @@ package templates import ( - "encoding/json" "errors" "net/http" @@ -12,6 +11,7 @@ import ( "github.com/asaskevich/govalidator" "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) type filePayload struct { diff --git a/api/http/handler/users/user_create_access_token_test.go b/api/http/handler/users/user_create_access_token_test.go index 9d33f204c..2c4a54489 100644 --- a/api/http/handler/users/user_create_access_token_test.go +++ b/api/http/handler/users/user_create_access_token_test.go @@ -2,7 +2,6 @@ package users import ( "bytes" - "encoding/json" "fmt" "io" "net/http" @@ -15,6 +14,8 @@ import ( "github.com/portainer/portainer/api/datastore" "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/jwt" + + "github.com/segmentio/encoding/json" "github.com/stretchr/testify/assert" ) @@ -105,7 +106,7 @@ func Test_userCreateAccessToken(t *testing.T) { body, err := io.ReadAll(rr.Body) is.NoError(err, "ReadAll should not return error") - is.Equal("{\"message\":\"Auth not supported\",\"details\":\"JWT Authentication required\"}\n", string(body)) + is.Equal(`{"message":"Auth not supported","details":"JWT Authentication required"}`, string(body)) }) } diff --git a/api/http/handler/users/user_get_access_tokens_test.go b/api/http/handler/users/user_get_access_tokens_test.go index f07d98e6f..849dc2f11 100644 --- a/api/http/handler/users/user_get_access_tokens_test.go +++ b/api/http/handler/users/user_get_access_tokens_test.go @@ -1,7 +1,6 @@ package users import ( - "encoding/json" "fmt" "io" "net/http" @@ -14,6 +13,8 @@ import ( "github.com/portainer/portainer/api/datastore" "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/jwt" + + "github.com/segmentio/encoding/json" "github.com/stretchr/testify/assert" ) diff --git a/api/http/handler/users/user_list_test.go b/api/http/handler/users/user_list_test.go index 261857c38..41c0a0e09 100644 --- a/api/http/handler/users/user_list_test.go +++ b/api/http/handler/users/user_list_test.go @@ -1,7 +1,6 @@ package users import ( - "encoding/json" "fmt" "io" "net/http" @@ -17,6 +16,8 @@ import ( "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/internal/authorization" "github.com/portainer/portainer/api/jwt" + + "github.com/segmentio/encoding/json" "github.com/stretchr/testify/assert" ) diff --git a/api/http/handler/websocket/exec.go b/api/http/handler/websocket/exec.go index 6a1df05e2..be66e255b 100644 --- a/api/http/handler/websocket/exec.go +++ b/api/http/handler/websocket/exec.go @@ -2,20 +2,20 @@ package websocket import ( "bytes" - "encoding/json" - "github.com/portainer/portainer/api/http/security" - "github.com/rs/zerolog/log" "net" "net/http" "net/http/httputil" "time" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/http/security" httperror "github.com/portainer/portainer/pkg/libhttp/error" "github.com/portainer/portainer/pkg/libhttp/request" "github.com/asaskevich/govalidator" "github.com/gorilla/websocket" + "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) type execStartOperationPayload struct { diff --git a/api/http/middlewares/endpoint.go b/api/http/middlewares/endpoint.go index b2a8a6552..ca9d3fbf8 100644 --- a/api/http/middlewares/endpoint.go +++ b/api/http/middlewares/endpoint.go @@ -13,9 +13,7 @@ import ( "github.com/gorilla/mux" ) -const ( - contextEndpoint = "endpoint" -) +const contextEndpoint = "endpoint" func WithEndpoint(endpointService dataservices.EndpointService, endpointIDParam string) mux.MiddlewareFunc { return func(next http.Handler) http.Handler { diff --git a/api/http/proxy/factory/docker/build.go b/api/http/proxy/factory/docker/build.go index 27f3622d7..ea71ce312 100644 --- a/api/http/proxy/factory/docker/build.go +++ b/api/http/proxy/factory/docker/build.go @@ -2,7 +2,6 @@ package docker import ( "bytes" - "encoding/json" "errors" "io" "mime" @@ -11,6 +10,7 @@ import ( "github.com/portainer/portainer/api/archive" "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) const OneMegabyte = 1024768 diff --git a/api/http/proxy/factory/docker/containers.go b/api/http/proxy/factory/docker/containers.go index c137e2606..745359c7b 100644 --- a/api/http/proxy/factory/docker/containers.go +++ b/api/http/proxy/factory/docker/containers.go @@ -3,22 +3,21 @@ package docker import ( "bytes" "context" - "encoding/json" "errors" "io" "net/http" "strings" - "github.com/docker/docker/client" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/proxy/factory/utils" "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/internal/authorization" + + "github.com/docker/docker/client" + "github.com/segmentio/encoding/json" ) -const ( - containerObjectIdentifier = "Id" -) +const containerObjectIdentifier = "Id" func getInheritedResourceControlFromContainerLabels(dockerClient *client.Client, endpointID portainer.EndpointID, containerID string, resourceControls []portainer.ResourceControl) (*portainer.ResourceControl, error) { container, err := dockerClient.ContainerInspect(context.Background(), containerID) diff --git a/api/http/proxy/factory/docker/services.go b/api/http/proxy/factory/docker/services.go index 4df1d316a..4a51311dc 100644 --- a/api/http/proxy/factory/docker/services.go +++ b/api/http/proxy/factory/docker/services.go @@ -3,22 +3,20 @@ package docker import ( "bytes" "context" - "encoding/json" "errors" "io" "net/http" - "github.com/docker/docker/api/types" - "github.com/docker/docker/client" - portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/proxy/factory/utils" "github.com/portainer/portainer/api/internal/authorization" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/client" + "github.com/segmentio/encoding/json" ) -const ( - serviceObjectIdentifier = "ID" -) +const serviceObjectIdentifier = "ID" func getInheritedResourceControlFromServiceLabels(dockerClient *client.Client, endpointID portainer.EndpointID, serviceID string, resourceControls []portainer.ResourceControl) (*portainer.ResourceControl, error) { service, _, err := dockerClient.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) diff --git a/api/http/proxy/factory/docker/transport.go b/api/http/proxy/factory/docker/transport.go index a7bc06f4c..61ab1c711 100644 --- a/api/http/proxy/factory/docker/transport.go +++ b/api/http/proxy/factory/docker/transport.go @@ -3,7 +3,6 @@ package docker import ( "bytes" "encoding/base64" - "encoding/json" "errors" "fmt" "io" @@ -15,12 +14,13 @@ import ( portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/dataservices" - dockerclient "github.com/portainer/portainer/api/docker/client" "github.com/portainer/portainer/api/http/proxy/factory/utils" "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/internal/authorization" + dockerclient "github.com/portainer/portainer/api/docker/client" "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) var apiVersionRe = regexp.MustCompile(`(/v[0-9]\.[0-9]*)?`) diff --git a/api/http/proxy/factory/kubernetes/transport.go b/api/http/proxy/factory/kubernetes/transport.go index 2de13c695..c77fb97b4 100644 --- a/api/http/proxy/factory/kubernetes/transport.go +++ b/api/http/proxy/factory/kubernetes/transport.go @@ -2,7 +2,6 @@ package kubernetes import ( "bytes" - "encoding/json" "fmt" "io" "net/http" @@ -18,6 +17,7 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) type baseTransport struct { diff --git a/api/http/proxy/factory/utils/json.go b/api/http/proxy/factory/utils/json.go index 7f215a685..b29aa8550 100644 --- a/api/http/proxy/factory/utils/json.go +++ b/api/http/proxy/factory/utils/json.go @@ -2,12 +2,12 @@ package utils import ( "compress/gzip" - "encoding/json" "errors" "fmt" "io" "mime" + "github.com/segmentio/encoding/json" "gopkg.in/yaml.v3" ) diff --git a/api/internal/registryutils/auth_header.go b/api/internal/registryutils/auth_header.go index 0676a367d..ce09f1e00 100644 --- a/api/internal/registryutils/auth_header.go +++ b/api/internal/registryutils/auth_header.go @@ -2,18 +2,17 @@ package registryutils import ( "encoding/base64" - "encoding/json" portainer "github.com/portainer/portainer/api" + + "github.com/segmentio/encoding/json" ) -type ( - authHeader struct { - Username string `json:"username"` - Password string `json:"password"` - ServerAddress string `json:"serveraddress"` - } -) +type authHeader struct { + Username string `json:"username"` + Password string `json:"password"` + ServerAddress string `json:"serveraddress"` +} // GetRegistryAuthHeader generate the X-Registry-Auth header from registry func GetRegistryAuthHeader(registry *portainer.Registry) (header string, err error) { diff --git a/api/kubernetes/cli/access.go b/api/kubernetes/cli/access.go index 20a8e070e..3d306447c 100644 --- a/api/kubernetes/cli/access.go +++ b/api/kubernetes/cli/access.go @@ -2,10 +2,11 @@ package cli import ( "context" - "encoding/json" + + portainer "github.com/portainer/portainer/api" "github.com/pkg/errors" - portainer "github.com/portainer/portainer/api" + "github.com/segmentio/encoding/json" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) diff --git a/api/kubernetes/cli/metrics.go b/api/kubernetes/cli/metrics.go index 669ce27b3..6b1b85049 100644 --- a/api/kubernetes/cli/metrics.go +++ b/api/kubernetes/cli/metrics.go @@ -2,9 +2,10 @@ package cli import ( "context" - "encoding/json" models "github.com/portainer/portainer/api/http/models/kubernetes" + + "github.com/segmentio/encoding/json" ) func (kcl *KubeClient) GetMetrics() (models.K8sMetrics, error) { diff --git a/api/kubernetes/cli/registries.go b/api/kubernetes/cli/registries.go index f58c65eba..5fc261ce0 100644 --- a/api/kubernetes/cli/registries.go +++ b/api/kubernetes/cli/registries.go @@ -2,13 +2,14 @@ package cli import ( "context" - "encoding/json" "fmt" "strconv" - "github.com/pkg/errors" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/internal/registryutils" + + "github.com/pkg/errors" + "github.com/segmentio/encoding/json" v1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/api/oauth/oauth.go b/api/oauth/oauth.go index 3f15f8f92..678c2fff8 100644 --- a/api/oauth/oauth.go +++ b/api/oauth/oauth.go @@ -2,7 +2,6 @@ package oauth import ( "context" - "encoding/json" "io" "mime" "net/http" @@ -14,6 +13,7 @@ import ( "github.com/golang-jwt/jwt/v4" "github.com/pkg/errors" "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" "golang.org/x/oauth2" ) diff --git a/api/oauth/oauthtest/oauth_server.go b/api/oauth/oauthtest/oauth_server.go index 62ae0823e..ab3d8b9f2 100644 --- a/api/oauth/oauthtest/oauth_server.go +++ b/api/oauth/oauthtest/oauth_server.go @@ -1,19 +1,18 @@ package oauthtest import ( - "encoding/json" "fmt" "net/http" "net/http/httptest" "strings" - "github.com/gorilla/mux" portainer "github.com/portainer/portainer/api" + + "github.com/gorilla/mux" + "github.com/segmentio/encoding/json" ) -const ( - AccessToken = "test-token" -) +const AccessToken = "test-token" // OAuthRoutes is an OAuth 2.0 compliant handler func OAuthRoutes(code string, config *portainer.OAuthSettings) http.Handler { diff --git a/api/portainer.go b/api/portainer.go index dfc7ac369..647623c11 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -171,7 +171,7 @@ type ( // User identifier who created this template CreatedByUserID UserID `json:"CreatedByUserId" example:"3"` // A note that will be displayed in the UI. Supports HTML content - Note string `json:"Note" example:"This is my custom template"` + Note string `json:"Note,omitempty" example:"This is my custom template"` // Platform associated to the template. // Valid values are: 1 - 'linux', 2 - 'windows' Platform CustomTemplatePlatform `json:"Platform" example:"1" enums:"1,2"` @@ -182,11 +182,11 @@ type ( // * 2 - compose // * 3 - kubernetes Type StackType `json:"Type" example:"1" enums:"1,2,3"` - ResourceControl *ResourceControl `json:"ResourceControl"` + ResourceControl *ResourceControl `json:"ResourceControl,omitempty"` Variables []CustomTemplateVariableDefinition - GitConfig *gittypes.RepoConfig `json:"GitConfig"` + GitConfig *gittypes.RepoConfig `json:"GitConfig,omitempty"` // IsComposeFormat indicates if the Kubernetes template is created from a Docker Compose file - IsComposeFormat bool `example:"false"` + IsComposeFormat bool `json:"IsComposeFormat,omitempty" example:"false"` } // CustomTemplateID represents a custom template identifier @@ -249,10 +249,10 @@ type ( // EdgeGroup Identifier ID EdgeGroupID `json:"Id" example:"1"` Name string `json:"Name"` - Dynamic bool `json:"Dynamic"` + Dynamic bool `json:"Dynamic,omitempty"` TagIDs []TagID `json:"TagIds"` Endpoints []EndpointID `json:"Endpoints"` - PartialMatch bool `json:"PartialMatch"` + PartialMatch bool `json:"PartialMatch,omitempty"` } // EdgeGroupID represents an Edge group identifier @@ -268,7 +268,7 @@ type ( EdgeGroups []EdgeGroupID `json:"EdgeGroups"` Name string `json:"Name"` ScriptPath string `json:"ScriptPath"` - Recurring bool `json:"Recurring"` + Recurring bool `json:"Recurring,omitempty"` Version int `json:"Version"` // Field used for log collection of Endpoints belonging to EdgeGroups @@ -278,7 +278,7 @@ type ( // EdgeJobEndpointMeta represents a meta data object for an Edge job and Environment(Endpoint) relation EdgeJobEndpointMeta struct { LogsStatus EdgeJobLogsStatus - CollectLogs bool + CollectLogs bool `json:"CollectLogs,omitempty"` } // EdgeJobID represents an Edge job identifier @@ -325,10 +325,10 @@ type ( ManifestPath string DeploymentType EdgeStackDeploymentType // Uses the manifest's namespaces instead of the default one - UseManifestNamespaces bool + UseManifestNamespaces bool `json:"UseManifestNamespaces,omitempty"` // Deprecated - Prune bool `json:"Prune"` + Prune bool `json:"Prune,omitempty"` } EdgeStackDeploymentType int @@ -392,11 +392,11 @@ type ( // Environment(Endpoint) environment(endpoint) type. 1 for a Docker environment(endpoint), 2 for an agent on Docker environment(endpoint) or 3 for an Azure environment(endpoint). Type EndpointType `json:"Type" example:"1"` // URL or IP address of the Docker host associated to this environment(endpoint) - URL string `json:"URL" example:"docker.mydomain.tld:2375"` + URL string `json:"URL,omitempty" example:"docker.mydomain.tld:2375"` // Environment(Endpoint) group identifier 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"` + PublicURL string `json:"PublicURL,omitempty" example:"docker.mydomain.tld:2375"` Gpus []Pair `json:"Gpus"` TLSConfig TLSConfiguration `json:"TLSConfig"` AzureCredentials AzureCredentials `json:"AzureCredentials,omitempty"` @@ -413,7 +413,7 @@ type ( // The identifier of the edge agent associated with this environment(endpoint) EdgeID string `json:"EdgeID,omitempty"` // The key which is used to map the agent to Portainer - EdgeKey string `json:"EdgeKey"` + EdgeKey string `json:"EdgeKey,omitempty"` // The check in interval for edge agent (in seconds) EdgeCheckinInterval int `json:"EdgeCheckinInterval" example:"5"` // Associated Kubernetes data @@ -425,14 +425,14 @@ type ( // The identifier of the AMT Device associated with this environment(endpoint) AMTDeviceGUID string `json:"AMTDeviceGUID,omitempty" example:"4c4c4544-004b-3910-8037-b6c04f504633"` // LastCheckInDate mark last check-in date on checkin - LastCheckInDate int64 + LastCheckInDate int64 `json:"LastCheckInDate,omitempty"` // QueryDate of each query with the endpoints list - QueryDate int64 + QueryDate int64 `json:"QueryDate,omitempty"` // Heartbeat indicates the heartbeat status of an edge environment - Heartbeat bool `json:"Heartbeat" example:"true"` + Heartbeat bool `json:"Heartbeat,omitempty" example:"true"` // Whether the device has been trusted or not by the user - UserTrusted bool + UserTrusted bool `json:"UserTrusted,omitempty"` // Whether we need to run any "post init migrations". PostInitMigrations EndpointPostInitMigrations `json:"PostInitMigrations"` @@ -440,10 +440,10 @@ type ( Edge EnvironmentEdgeSettings Agent struct { - Version string `example:"1.0.0"` + Version string `json:"Version,omitempty" example:"1.0.0"` } - EnableGPUManagement bool `json:"EnableGPUManagement"` + EnableGPUManagement bool `json:"EnableGPUManagement,omitempty"` // Deprecated fields // Deprecated in DBVersion == 4 @@ -460,18 +460,18 @@ type ( Tags []string `json:"Tags"` // Deprecated v2.18 - IsEdgeDevice bool + IsEdgeDevice bool `json:"IsEdgeDevice,omitempty"` } EnvironmentEdgeSettings struct { // Whether the device has been started in edge async mode - AsyncMode bool + AsyncMode bool `json:"AsyncMode,omitempty"` // The ping interval for edge agent - used in edge async mode [seconds] - PingInterval int `json:"PingInterval" example:"60"` + PingInterval int `json:"PingInterval,omitempty" example:"60"` // The snapshot interval for edge agent - used in edge async mode [seconds] - SnapshotInterval int `json:"SnapshotInterval" example:"60"` + SnapshotInterval int `json:"SnapshotInterval,omitempty" example:"60"` // The command list interval for edge agent - used in edge async mode [seconds] - CommandInterval int `json:"CommandInterval" example:"60"` + CommandInterval int `json:"CommandInterval,omitempty" example:"60"` } // EndpointAuthorizations represents the authorizations associated to a set of environments(endpoints) @@ -486,7 +486,7 @@ type ( // Environment(Endpoint) group name Name string `json:"Name" example:"my-environment-group"` // Description associated to the environment(endpoint) group - Description string `json:"Description" example:"Environment(Endpoint) group description"` + Description string `json:"Description,omitempty" example:"Environment(Endpoint) group description"` UserAccessPolicies UserAccessPolicies `json:"UserAccessPolicies"` TeamAccessPolicies TeamAccessPolicies `json:"TeamAccessPolicies"` // List of tags associated to this environment(endpoint) group @@ -500,7 +500,7 @@ type ( AuthorizedTeams []TeamID `json:"AuthorizedTeams"` // Deprecated in DBVersion == 22 - Tags []string `json:"Tags"` + Tags []string `json:"Tags,omitempty"` } // EndpointGroupID represents an environment(endpoint) group identifier @@ -519,23 +519,23 @@ type ( // EndpointSecuritySettings represents settings for an environment(endpoint) EndpointSecuritySettings struct { // Whether non-administrator should be able to use bind mounts when creating containers - AllowBindMountsForRegularUsers bool `json:"allowBindMountsForRegularUsers" example:"false"` + AllowBindMountsForRegularUsers bool `json:"allowBindMountsForRegularUsers,omitempty" example:"false"` // Whether non-administrator should be able to use privileged mode when creating containers - AllowPrivilegedModeForRegularUsers bool `json:"allowPrivilegedModeForRegularUsers" example:"false"` + AllowPrivilegedModeForRegularUsers bool `json:"allowPrivilegedModeForRegularUsers,omitempty" example:"false"` // Whether non-administrator should be able to browse volumes - AllowVolumeBrowserForRegularUsers bool `json:"allowVolumeBrowserForRegularUsers" example:"true"` + AllowVolumeBrowserForRegularUsers bool `json:"allowVolumeBrowserForRegularUsers,omitempty" example:"true"` // Whether non-administrator should be able to use the host pid - AllowHostNamespaceForRegularUsers bool `json:"allowHostNamespaceForRegularUsers" example:"true"` + AllowHostNamespaceForRegularUsers bool `json:"allowHostNamespaceForRegularUsers,omitempty" example:"true"` // Whether non-administrator should be able to use device mapping - AllowDeviceMappingForRegularUsers bool `json:"allowDeviceMappingForRegularUsers" example:"true"` + AllowDeviceMappingForRegularUsers bool `json:"allowDeviceMappingForRegularUsers,omitempty" example:"true"` // Whether non-administrator should be able to manage stacks - AllowStackManagementForRegularUsers bool `json:"allowStackManagementForRegularUsers" example:"true"` + AllowStackManagementForRegularUsers bool `json:"allowStackManagementForRegularUsers,omitempty" example:"true"` // Whether non-administrator should be able to use container capabilities - AllowContainerCapabilitiesForRegularUsers bool `json:"allowContainerCapabilitiesForRegularUsers" example:"true"` + AllowContainerCapabilitiesForRegularUsers bool `json:"allowContainerCapabilitiesForRegularUsers,omitempty" example:"true"` // Whether non-administrator should be able to use sysctl settings - AllowSysctlSettingForRegularUsers bool `json:"allowSysctlSettingForRegularUsers" example:"true"` + AllowSysctlSettingForRegularUsers bool `json:"allowSysctlSettingForRegularUsers,omitempty" example:"true"` // Whether host management features are enabled - EnableHostManagementFeatures bool `json:"enableHostManagementFeatures" example:"true"` + EnableHostManagementFeatures bool `json:"enableHostManagementFeatures,omitempty" example:"true"` } // EndpointType represents the type of an environment(endpoint) @@ -549,15 +549,15 @@ type ( // EndpointPostInitMigrations EndpointPostInitMigrations struct { - MigrateIngresses bool `json:"MigrateIngresses"` - MigrateGPUs bool `json:"MigrateGPUs"` + MigrateIngresses bool `json:"MigrateIngresses,omitempty"` + MigrateGPUs bool `json:"MigrateGPUs,omitempty"` } // Extension represents a deprecated Portainer extension Extension struct { // Extension Identifier ID ExtensionID `json:"Id" example:"1"` - Enabled bool `json:"Enabled"` + Enabled bool `json:"Enabled,omitempty"` Name string `json:"Name,omitempty"` ShortDescription string `json:"ShortDescription,omitempty"` Description string `json:"Description,omitempty"` @@ -598,7 +598,7 @@ type ( // QuayRegistryData represents data required for Quay registry to work QuayRegistryData struct { - UseOrganisation bool `json:"UseOrganisation"` + UseOrganisation bool `json:"UseOrganisation,omitempty"` OrganisationName string `json:"OrganisationName"` } @@ -611,13 +611,13 @@ type ( JobType int K8sNamespaceInfo struct { - IsSystem bool `json:"IsSystem"` - IsDefault bool `json:"IsDefault"` + IsSystem bool `json:"IsSystem,omitempty"` + IsDefault bool `json:"IsDefault,omitempty"` } K8sNodeLimits struct { - CPU int64 `json:"CPU"` - Memory int64 `json:"Memory"` + CPU int64 `json:"CPU,omitempty"` + Memory int64 `json:"Memory,omitempty"` } K8sNodesLimits map[string]*K8sNodeLimits @@ -637,9 +637,9 @@ type ( // KubernetesFlags are used to detect if we need to run initial cluster // detection again. KubernetesFlags struct { - IsServerMetricsDetected bool `json:"IsServerMetricsDetected"` - IsServerIngressClassDetected bool `json:"IsServerIngressClassDetected"` - IsServerStorageDetected bool `json:"IsServerStorageDetected"` + IsServerMetricsDetected bool `json:"IsServerMetricsDetected,omitempty"` + IsServerIngressClassDetected bool `json:"IsServerIngressClassDetected,omitempty"` + IsServerStorageDetected bool `json:"IsServerStorageDetected,omitempty"` } // KubernetesSnapshot represents a snapshot of a specific Kubernetes environment(endpoint) at a specific time @@ -653,15 +653,15 @@ type ( // KubernetesConfiguration represents the configuration of a Kubernetes environment(endpoint) KubernetesConfiguration struct { - UseLoadBalancer bool `json:"UseLoadBalancer"` - UseServerMetrics bool `json:"UseServerMetrics"` - EnableResourceOverCommit bool `json:"EnableResourceOverCommit"` - ResourceOverCommitPercentage int `json:"ResourceOverCommitPercentage"` + UseLoadBalancer bool `json:"UseLoadBalancer,omitempty"` + UseServerMetrics bool `json:"UseServerMetrics,omitempty"` + EnableResourceOverCommit bool `json:"EnableResourceOverCommit,omitempty"` + ResourceOverCommitPercentage int `json:"ResourceOverCommitPercentage,omitempty"` StorageClasses []KubernetesStorageClassConfig `json:"StorageClasses"` IngressClasses []KubernetesIngressClassConfig `json:"IngressClasses"` - RestrictDefaultNamespace bool `json:"RestrictDefaultNamespace"` - IngressAvailabilityPerNamespace bool `json:"IngressAvailabilityPerNamespace"` - AllowNoneIngressClass bool `json:"AllowNoneIngressClass"` + RestrictDefaultNamespace bool `json:"RestrictDefaultNamespace,omitempty"` + IngressAvailabilityPerNamespace bool `json:"IngressAvailabilityPerNamespace,omitempty"` + AllowNoneIngressClass bool `json:"AllowNoneIngressClass,omitempty"` } // KubernetesStorageClassConfig represents a Kubernetes Storage Class configuration @@ -669,14 +669,14 @@ type ( Name string `json:"Name"` AccessModes []string `json:"AccessModes"` Provisioner string `json:"Provisioner"` - AllowVolumeExpansion bool `json:"AllowVolumeExpansion"` + AllowVolumeExpansion bool `json:"AllowVolumeExpansion,omitempty"` } // KubernetesIngressClassConfig represents a Kubernetes Ingress Class configuration KubernetesIngressClassConfig struct { Name string `json:"Name"` Type string `json:"Type"` - GloballyBlocked bool `json:"Blocked"` + GloballyBlocked bool `json:"Blocked,omitempty"` BlockedNamespaces []string `json:"BlockedNamespaces"` } @@ -690,7 +690,7 @@ type ( // InternalAuthSettings represents settings used for the default 'internal' authentication InternalAuthSettings struct { - RequiredPasswordLength int + RequiredPasswordLength int `json:"RequiredPasswordLength,omitempty"` } // LDAPGroupSearchSettings represents settings used to search for groups in a LDAP server @@ -698,7 +698,7 @@ type ( // The distinguished name of the element from which the LDAP server will search for groups GroupBaseDN string `json:"GroupBaseDN" example:"dc=ldap,dc=domain,dc=tld"` // The LDAP search filter used to select group elements, optional - GroupFilter string `json:"GroupFilter" example:"(objectClass=account"` + GroupFilter string `json:"GroupFilter,omitempty" example:"(objectClass=account"` // LDAP attribute which denotes the group membership GroupAttribute string `json:"GroupAttribute" example:"member"` } @@ -716,7 +716,7 @@ type ( // LDAPSettings represents the settings used to connect to a LDAP server LDAPSettings struct { // Enable this option if the server is configured for Anonymous access. When enabled, ReaderDN and Password will not be used - AnonymousMode bool `json:"AnonymousMode" example:"true" validate:"validate_bool"` + AnonymousMode bool `json:"AnonymousMode,omitempty" example:"true" validate:"validate_bool"` // Account that will be used to search for users ReaderDN string `json:"ReaderDN" example:"cn=readonly-account,dc=ldap,dc=domain,dc=tld" validate:"required_if=AnonymousMode false"` // Password of the account that will be used to search users @@ -725,11 +725,11 @@ type ( URL string `json:"URL" example:"myldap.domain.tld:389" validate:"hostname_port"` TLSConfig TLSConfiguration `json:"TLSConfig"` // Whether LDAP connection should use StartTLS - StartTLS bool `json:"StartTLS" example:"true"` + StartTLS bool `json:"StartTLS,omitempty" example:"true"` SearchSettings []LDAPSearchSettings `json:"SearchSettings"` GroupSearchSettings []LDAPGroupSearchSettings `json:"GroupSearchSettings"` // Automatically provision users and assign them to matching LDAP group names - AutoCreateUsers bool `json:"AutoCreateUsers" example:"true"` + AutoCreateUsers bool `json:"AutoCreateUsers,omitempty" example:"true"` } // LDAPUser represents a LDAP user @@ -769,7 +769,7 @@ type ( // Pair defines a key/value string pair Pair struct { Name string `json:"name" example:"name"` - Value string `json:"value" example:"value"` + Value string `json:"value,omitempty" example:"value"` } // Registry represents a Docker registry with all the info required @@ -782,13 +782,13 @@ type ( // Registry Name Name string `json:"Name" example:"my-registry"` // URL or IP address of the Docker registry - URL string `json:"URL" example:"registry.mydomain.tld:2375"` + URL string `json:"URL,omitempty" example:"registry.mydomain.tld:2375"` // Base URL, introduced for ProGet registry - BaseURL string `json:"BaseURL" example:"registry.mydomain.tld:2375"` + BaseURL string `json:"BaseURL,omitempty" example:"registry.mydomain.tld:2375"` // Is authentication against this registry enabled - Authentication bool `json:"Authentication" example:"true"` + Authentication bool `json:"Authentication,omitempty" example:"true"` // Username or AccessKeyID used to authenticate against this registry - Username string `json:"Username" example:"registry user"` + Username string `json:"Username,omitempty" example:"registry user"` // Password or SecretAccessKey used to authenticate against this registry Password string `json:"Password,omitempty" example:"registry_password"` ManagementConfiguration *RegistryManagementConfiguration `json:"ManagementConfiguration"` @@ -828,9 +828,9 @@ type ( // the registry API via the registry management extension. RegistryManagementConfiguration struct { Type RegistryType `json:"Type"` - Authentication bool `json:"Authentication"` - Username string `json:"Username"` - Password string `json:"Password"` + Authentication bool `json:"Authentication,omitempty"` + Username string `json:"Username,omitempty"` + Password string `json:"Password,omitempty"` TLSConfig TLSConfiguration `json:"TLSConfig"` Ecr EcrData `json:"Ecr"` AccessToken string `json:"AccessToken,omitempty"` @@ -860,8 +860,8 @@ type ( // Permit access to the associated resource to any user Public bool `json:"Public" example:"true"` // Permit access to resource only to admins - AdministratorsOnly bool `json:"AdministratorsOnly" example:"true"` - System bool `json:"System"` + AdministratorsOnly bool `json:"AdministratorsOnly,omitempty" example:"true"` + System bool `json:"System,omitempty"` // Deprecated fields // Deprecated in DBVersion == 2 @@ -883,10 +883,10 @@ type ( // Role name Name string `json:"Name" example:"HelpDesk"` // Role description - Description string `json:"Description" example:"Read-only access of all resources in an environment(endpoint)"` + Description string `json:"Description,omitempty" example:"Read-only access of all resources in an environment(endpoint)"` // Authorizations associated to a role Authorizations Authorizations `json:"Authorizations"` - Priority int `json:"Priority"` + Priority int `json:"Priority,omitempty"` } // RoleID represents a role identifier @@ -899,7 +899,7 @@ type ( APIKey struct { ID APIKeyID `json:"id" example:"1"` UserID UserID `json:"userId" example:"1"` - Description string `json:"description" example:"portainer-api-key"` + Description string `json:"description,omitempty" example:"portainer-api-key"` Prefix string `json:"prefix"` // API key identifier (7 char prefix) DateCreated int64 `json:"dateCreated"` // Unix timestamp (UTC) when the API key was created LastUsed int64 `json:"lastUsed"` // Unix timestamp (UTC) when the API key was last used @@ -916,10 +916,10 @@ type ( ID ScheduleID `json:"Id" example:"1"` Name string CronExpression string - Recurring bool + Recurring bool `json:"Recurring,omitempty"` Created int64 JobType JobType - EdgeSchedule *EdgeSchedule + EdgeSchedule *EdgeSchedule `json:"EdgeSchedule,omitempty"` } // ScheduleID represents a schedule identifier. @@ -931,18 +931,18 @@ type ( Endpoints []EndpointID Image string ScriptPath string - RetryCount int - RetryInterval int + RetryCount int `json:"RetryCount,omitempty"` + RetryInterval int `json:"RetryInterval,omitempty"` } GlobalDeploymentOptions struct { - HideStacksFunctionality bool `json:"hideStacksFunctionality" example:"false"` + HideStacksFunctionality bool `json:"hideStacksFunctionality,omitempty" example:"false"` } // Settings represents the application settings Settings 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 `json:"LogoURL" example:"https://mycompany.mydomain.tld/logo.png"` + LogoURL string `json:"LogoURL,omitempty" 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"` // Active authentication method for the Portainer instance. Valid values are: 1 for internal, 2 for LDAP, or 3 for oauth @@ -962,27 +962,27 @@ type ( // The default check in interval for edge agent (in seconds) EdgeAgentCheckinInterval int `json:"EdgeAgentCheckinInterval" example:"5"` // Show the Kompose build option (discontinued in 2.18) - ShowKomposeBuildOption bool `json:"ShowKomposeBuildOption" example:"false"` + ShowKomposeBuildOption bool `json:"ShowKomposeBuildOption,omitempty" example:"false"` // Whether edge compute features are enabled - EnableEdgeComputeFeatures bool `json:"EnableEdgeComputeFeatures"` + EnableEdgeComputeFeatures bool `json:"EnableEdgeComputeFeatures,omitempty"` // The duration of a user session UserSessionTimeout string `json:"UserSessionTimeout" example:"5m"` // The expiry of a Kubeconfig KubeconfigExpiry string `json:"KubeconfigExpiry" example:"24h"` // Whether telemetry is enabled - EnableTelemetry bool `json:"EnableTelemetry" example:"false"` + EnableTelemetry bool `json:"EnableTelemetry,omitempty" example:"false"` // Helm repository URL, defaults to "https://charts.bitnami.com/bitnami" HelmRepositoryURL string `json:"HelmRepositoryURL" example:"https://charts.bitnami.com/bitnami"` // KubectlImage, defaults to portainer/kubectl-shell KubectlShellImage string `json:"KubectlShellImage" example:"portainer/kubectl-shell"` // TrustOnFirstConnect makes Portainer accepting edge agent connection by default - TrustOnFirstConnect bool `json:"TrustOnFirstConnect" example:"false"` + TrustOnFirstConnect bool `json:"TrustOnFirstConnect,omitempty" example:"false"` // EnforceEdgeID makes Portainer store the Edge ID instead of accepting anyone - EnforceEdgeID bool `json:"EnforceEdgeID" example:"false"` + EnforceEdgeID bool `json:"EnforceEdgeID,omitempty" example:"false"` // Container environment parameter AGENT_SECRET - AgentSecret string `json:"AgentSecret"` + AgentSecret string `json:"AgentSecret,omitempty"` // EdgePortainerURL is the URL that is exposed to edge agents - EdgePortainerURL string `json:"EdgePortainerUrl"` + EdgePortainerURL string `json:"EdgePortainerUrl,omitempty"` Edge struct { // The command list interval for edge agent - used in edge async mode (in seconds) @@ -997,20 +997,20 @@ type ( } // Deprecated fields - DisplayDonationHeader bool - DisplayExternalContributors bool + DisplayDonationHeader bool `json:"DisplayDonationHeader,omitempty"` + DisplayExternalContributors bool `json:"DisplayExternalContributors,omitempty"` // Deprecated fields v26 - EnableHostManagementFeatures bool `json:"EnableHostManagementFeatures"` - AllowVolumeBrowserForRegularUsers bool `json:"AllowVolumeBrowserForRegularUsers"` - AllowBindMountsForRegularUsers bool `json:"AllowBindMountsForRegularUsers"` - AllowPrivilegedModeForRegularUsers bool `json:"AllowPrivilegedModeForRegularUsers"` - AllowHostNamespaceForRegularUsers bool `json:"AllowHostNamespaceForRegularUsers"` - AllowStackManagementForRegularUsers bool `json:"AllowStackManagementForRegularUsers"` - AllowDeviceMappingForRegularUsers bool `json:"AllowDeviceMappingForRegularUsers"` - AllowContainerCapabilitiesForRegularUsers bool `json:"AllowContainerCapabilitiesForRegularUsers"` + EnableHostManagementFeatures bool `json:"EnableHostManagementFeatures,omitempty"` + AllowVolumeBrowserForRegularUsers bool `json:"AllowVolumeBrowserForRegularUsers,omitempty"` + AllowBindMountsForRegularUsers bool `json:"AllowBindMountsForRegularUsers,omitempty"` + AllowPrivilegedModeForRegularUsers bool `json:"AllowPrivilegedModeForRegularUsers,omitempty"` + AllowHostNamespaceForRegularUsers bool `json:"AllowHostNamespaceForRegularUsers,omitempty"` + AllowStackManagementForRegularUsers bool `json:"AllowStackManagementForRegularUsers,omitempty"` + AllowDeviceMappingForRegularUsers bool `json:"AllowDeviceMappingForRegularUsers,omitempty"` + AllowContainerCapabilitiesForRegularUsers bool `json:"AllowContainerCapabilitiesForRegularUsers,omitempty"` - IsDockerDesktopExtension bool `json:"IsDockerDesktopExtension"` + IsDockerDesktopExtension bool `json:"IsDockerDesktopExtension,omitempty"` } // SnapshotJob represents a scheduled job that can create environment(endpoint) snapshots @@ -1021,10 +1021,10 @@ type ( // SSLSettings represents a pair of SSL certificate and key SSLSettings struct { - CertPath string `json:"certPath"` - KeyPath string `json:"keyPath"` - SelfSigned bool `json:"selfSigned"` - HTTPEnabled bool `json:"httpEnabled"` + CertPath string `json:"certPath,omitempty"` + KeyPath string `json:"keyPath,omitempty"` + SelfSigned bool `json:"selfSigned,omitempty"` + HTTPEnabled bool `json:"httpEnabled,omitempty"` } // Stack represents a Docker stack created via docker stack deploy @@ -1038,13 +1038,13 @@ type ( // Environment(Endpoint) identifier. Reference the environment(endpoint) that will be used for deployment EndpointID EndpointID `json:"EndpointId" example:"1"` // Cluster identifier of the Swarm cluster where the stack is deployed - SwarmID string `json:"SwarmId" example:"jpofkc0i9uo9wtx1zesuk649w"` + SwarmID string `json:"SwarmId,omitempty" 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"` // - ResourceControl *ResourceControl `json:"ResourceControl"` + ResourceControl *ResourceControl `json:"ResourceControl,omitempty"` // Stack status (1 - active, 2 - inactive) Status StackStatus `json:"Status" example:"1"` // Path on disk to the repository hosting the Stack file @@ -1060,23 +1060,23 @@ type ( // Only applies when deploying stack with multiple files AdditionalFiles []string `json:"AdditionalFiles"` // The GitOps update settings of a git stack - AutoUpdate *AutoUpdateSettings `json:"AutoUpdate"` + AutoUpdate *AutoUpdateSettings `json:"AutoUpdate,omitempty"` // The stack deployment option - Option *StackOption `json:"Option"` + Option *StackOption `json:"Option,omitempty"` // The git config of this stack - GitConfig *gittypes.RepoConfig + GitConfig *gittypes.RepoConfig `json:"GitConfig,omitempty"` // Whether the stack is from a app template - FromAppTemplate bool `example:"false"` + FromAppTemplate bool `json:"FromAppTemplate,omitempty" example:"false"` // Kubernetes namespace if stack is a kube application - Namespace string `example:"default"` + Namespace string `json:"Namespace,omitempty" example:"default"` // IsComposeFormat indicates if the Kubernetes stack is created from a Docker Compose file - IsComposeFormat bool `example:"false"` + IsComposeFormat bool `json:"IsComposeFormat,omitempty" example:"false"` } // StackOption represents the options for stack deployment StackOption struct { // Prune services that are no longer referenced - Prune bool `example:"false"` + Prune bool `json:"Prune,omitempty" example:"false"` } // StackID represents a stack identifier (it must be composed of Name + "_" + SwarmID to create a unique identifier) @@ -1159,9 +1159,9 @@ type ( // Title of the template Title string `json:"title" example:"Nginx"` // Description of the template - Description string `json:"description" example:"High performance web server"` + Description string `json:"description,omitempty" example:"High performance web server"` // Whether the template should be available to administrators only - AdministratorOnly bool `json:"administrator_only" example:"true"` + AdministratorOnly bool `json:"administrator_only,omitempty" example:"true"` // Mandatory container fields // Image associated to a container template. Mandatory for a container template @@ -1237,7 +1237,7 @@ type ( // A value that will be associated to the choice Value string `json:"value" example:"value"` // Will set this choice as the default choice - Default bool `json:"default" example:"false"` + Default bool `json:"default,omitempty" example:"false"` } // TemplateID represents a template identifier @@ -1267,9 +1267,9 @@ type ( // TLSConfiguration represents a TLS configuration TLSConfiguration struct { // Use TLS - TLS bool `json:"TLS" example:"true"` + TLS bool `json:"TLS,omitempty" example:"true"` // Skip the verification of the server TLS certificate - TLSSkipVerify bool `json:"TLSSkipVerify" example:"false"` + TLSSkipVerify bool `json:"TLSSkipVerify,omitempty" example:"false"` // Path to the TLS CA certificate file TLSCACertPath string `json:"TLSCACert,omitempty" example:"/data/tls/ca.pem"` // Path to the TLS client certificate file @@ -1287,7 +1287,7 @@ type ( ID UserID Username string Role UserRole - ForceChangePassword bool + ForceChangePassword bool `json:"ForceChangePassword,omitempty"` Token string } @@ -1302,7 +1302,7 @@ type ( // TunnelServerInfo represents information associated to the tunnel server TunnelServerInfo struct { - PrivateKeySeed string `json:"PrivateKeySeed"` + PrivateKeySeed string `json:"PrivateKeySeed,omitempty"` } // User represents a user account @@ -1319,7 +1319,7 @@ type ( // Deprecated fields // Deprecated - UserTheme string `example:"dark"` + UserTheme string `json:"UserTheme,omitempty" example:"dark"` // Deprecated in DBVersion == 25 PortainerAuthorizations Authorizations // Deprecated in DBVersion == 25 @@ -1345,7 +1345,7 @@ type ( // UserThemeSettings represents the theme settings for a user UserThemeSettings struct { // Color represents the color theme of the UI - Color string `json:"color" example:"dark" enums:"dark,light,highcontrast,auto"` + Color string `json:"color,omitempty" example:"dark" enums:"dark,light,highcontrast,auto"` } // Webhook represents a url webhook that can be used to update a service @@ -1368,8 +1368,8 @@ type ( Snapshot struct { EndpointID EndpointID `json:"EndpointId"` - Docker *DockerSnapshot `json:"Docker"` - Kubernetes *KubernetesSnapshot `json:"Kubernetes"` + Docker *DockerSnapshot `json:"Docker,omitempty"` + Kubernetes *KubernetesSnapshot `json:"Kubernetes,omitempty"` } // CLIService represents a service for managing CLI diff --git a/api/stacks/deployments/deployer_remote.go b/api/stacks/deployments/deployer_remote.go index 8b1ab4c5d..33bec796e 100644 --- a/api/stacks/deployments/deployer_remote.go +++ b/api/stacks/deployments/deployer_remote.go @@ -3,7 +3,6 @@ package deployments import ( "bytes" "context" - "encoding/json" "fmt" "io" "math/rand" @@ -11,16 +10,17 @@ import ( "strings" "time" + portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/filesystem" + "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/swarm" + dockerclient "github.com/docker/docker/client" "github.com/docker/docker/pkg/stdcopy" "github.com/pkg/errors" - portainer "github.com/portainer/portainer/api" - "github.com/portainer/portainer/api/filesystem" "github.com/rs/zerolog/log" - - dockerclient "github.com/docker/docker/client" + "github.com/segmentio/encoding/json" ) const ( diff --git a/go.mod b/go.mod index 160628b61..ea9f1cbf1 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,6 @@ require ( github.com/hashicorp/golang-lru v0.5.4 github.com/joho/godotenv v1.4.0 github.com/jpillora/chisel v1.9.0 - github.com/json-iterator/go v1.1.12 github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c github.com/opencontainers/go-digest v1.0.0 github.com/orcaman/concurrent-map v1.0.0 @@ -39,6 +38,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.1 github.com/rs/zerolog v1.29.0 + github.com/segmentio/encoding v0.3.6 github.com/stretchr/testify v1.8.2 github.com/viney-shih/go-lock v1.1.1 go.etcd.io/bbolt v1.3.7 @@ -106,6 +106,7 @@ require ( github.com/jpillora/ansi v1.0.3 // indirect github.com/jpillora/requestlog v1.0.0 // indirect github.com/jpillora/sizestr v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect github.com/klauspost/compress v1.16.3 // indirect github.com/klauspost/pgzip v1.2.6-0.20220930104621-17e8dac29df8 // indirect @@ -125,6 +126,7 @@ require ( github.com/opencontainers/runc v1.1.5 // indirect github.com/opencontainers/runtime-spec v1.1.0-rc.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/segmentio/asm v1.1.3 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/spf13/pflag v1.0.5 // indirect diff --git a/go.sum b/go.sum index c8e0cfa90..faf6527e5 100644 --- a/go.sum +++ b/go.sum @@ -330,6 +330,10 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= +github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= +github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= +github.com/segmentio/encoding v0.3.6 h1:E6lVLyDPseWEulBmCmAKPanDd3jiyGDo5gMcugCRwZQ= +github.com/segmentio/encoding v0.3.6/go.mod h1:n0JeuIqEQrQoPDGsjo8UNd1iA0U8d8+oHAA4E3G3OxM= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= @@ -450,6 +454,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pkg/libhelm/binary/install.go b/pkg/libhelm/binary/install.go index dbd2e5dd0..937df1500 100644 --- a/pkg/libhelm/binary/install.go +++ b/pkg/libhelm/binary/install.go @@ -1,11 +1,11 @@ package binary import ( - "encoding/json" - - "github.com/pkg/errors" "github.com/portainer/portainer/pkg/libhelm/options" "github.com/portainer/portainer/pkg/libhelm/release" + + "github.com/pkg/errors" + "github.com/segmentio/encoding/json" ) // Install runs `helm install` with specified install options. diff --git a/pkg/libhelm/binary/list.go b/pkg/libhelm/binary/list.go index 7859c77f4..886d4c5ea 100644 --- a/pkg/libhelm/binary/list.go +++ b/pkg/libhelm/binary/list.go @@ -1,11 +1,11 @@ package binary import ( - "encoding/json" - - "github.com/pkg/errors" "github.com/portainer/portainer/pkg/libhelm/options" "github.com/portainer/portainer/pkg/libhelm/release" + + "github.com/pkg/errors" + "github.com/segmentio/encoding/json" ) // List runs `helm list --output json --filter --selector --namespace ` with specified list options. diff --git a/pkg/libhelm/binary/search_repo.go b/pkg/libhelm/binary/search_repo.go index cbe524c75..81ebd172e 100644 --- a/pkg/libhelm/binary/search_repo.go +++ b/pkg/libhelm/binary/search_repo.go @@ -4,15 +4,16 @@ package binary // The functionality does not rely on the implementation of `HelmPackageManager` import ( - "encoding/json" "fmt" "net/http" "net/url" "path" "time" - "github.com/pkg/errors" "github.com/portainer/portainer/pkg/libhelm/options" + + "github.com/pkg/errors" + "github.com/segmentio/encoding/json" "gopkg.in/yaml.v3" ) diff --git a/pkg/libhelm/binary/test/mock.go b/pkg/libhelm/binary/test/mock.go index 06c9e9105..e019ee248 100644 --- a/pkg/libhelm/binary/test/mock.go +++ b/pkg/libhelm/binary/test/mock.go @@ -1,13 +1,14 @@ package test import ( - "encoding/json" "strings" - "github.com/pkg/errors" "github.com/portainer/portainer/pkg/libhelm" "github.com/portainer/portainer/pkg/libhelm/options" "github.com/portainer/portainer/pkg/libhelm/release" + + "github.com/pkg/errors" + "github.com/segmentio/encoding/json" "gopkg.in/yaml.v3" ) diff --git a/pkg/libhelm/time/time_test.go b/pkg/libhelm/time/time_test.go index 20f0f8e29..9de753f05 100644 --- a/pkg/libhelm/time/time_test.go +++ b/pkg/libhelm/time/time_test.go @@ -17,9 +17,10 @@ limitations under the License. package time import ( - "encoding/json" "testing" "time" + + "github.com/segmentio/encoding/json" ) var ( diff --git a/pkg/libhttp/error/error.go b/pkg/libhttp/error/error.go index 929e4570e..a7b307198 100644 --- a/pkg/libhttp/error/error.go +++ b/pkg/libhttp/error/error.go @@ -2,11 +2,11 @@ package error import ( - "encoding/json" "errors" "net/http" "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) type ( @@ -36,7 +36,11 @@ func writeErrorResponse(rw http.ResponseWriter, err *HandlerError) { rw.Header().Set("Content-Type", "application/json") rw.WriteHeader(err.StatusCode) - json.NewEncoder(rw).Encode(&errorResponse{Message: err.Message, Details: err.Err.Error()}) + enc := json.NewEncoder(rw) + enc.SetSortMapKeys(false) + enc.SetAppendNewline(false) + + enc.Encode(&errorResponse{Message: err.Message, Details: err.Err.Error()}) } // WriteError is a convenience function that creates a new HandlerError before calling writeErrorResponse. diff --git a/pkg/libhttp/request/payload.go b/pkg/libhttp/request/payload.go index 9e563ddd2..19192c3c5 100644 --- a/pkg/libhttp/request/payload.go +++ b/pkg/libhttp/request/payload.go @@ -1,10 +1,10 @@ package request import ( - "encoding/json" "net/http" "github.com/pkg/errors" + "github.com/segmentio/encoding/json" ) // PayloadValidation is an interface used to validate the payload of a request. diff --git a/pkg/libhttp/request/payload_test.go b/pkg/libhttp/request/payload_test.go index 4f107c2f9..66408f9a7 100644 --- a/pkg/libhttp/request/payload_test.go +++ b/pkg/libhttp/request/payload_test.go @@ -2,13 +2,13 @@ package request_test import ( "bytes" - "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/portainer/portainer/pkg/libhttp/request" + "github.com/segmentio/encoding/json" "github.com/stretchr/testify/assert" ) diff --git a/pkg/libhttp/request/request.go b/pkg/libhttp/request/request.go index e5c2ed5fc..83c4876fe 100644 --- a/pkg/libhttp/request/request.go +++ b/pkg/libhttp/request/request.go @@ -4,13 +4,13 @@ package request import ( - "encoding/json" "errors" "io/ioutil" "net/http" "strconv" "github.com/gorilla/mux" + "github.com/segmentio/encoding/json" ) const ( diff --git a/pkg/libhttp/response/response.go b/pkg/libhttp/response/response.go index 3a5bddfb7..250a9859d 100644 --- a/pkg/libhttp/response/response.go +++ b/pkg/libhttp/response/response.go @@ -2,12 +2,13 @@ package response import ( - "encoding/json" "errors" "fmt" "net/http" httperror "github.com/portainer/portainer/pkg/libhttp/error" + + "github.com/segmentio/encoding/json" ) // JSON encodes data to rw in JSON format. Returns a pointer to a @@ -15,7 +16,11 @@ import ( func JSON(rw http.ResponseWriter, data interface{}) *httperror.HandlerError { rw.Header().Set("Content-Type", "application/json") - err := json.NewEncoder(rw).Encode(data) + enc := json.NewEncoder(rw) + enc.SetSortMapKeys(false) + enc.SetAppendNewline(false) + + err := enc.Encode(data) if err != nil { return httperror.InternalServerError("Unable to write JSON response", err) } diff --git a/pkg/libstack/compose/internal/composeplugin/status.go b/pkg/libstack/compose/internal/composeplugin/status.go index 4b6d9299d..ab06a8ac5 100644 --- a/pkg/libstack/compose/internal/composeplugin/status.go +++ b/pkg/libstack/compose/internal/composeplugin/status.go @@ -2,12 +2,13 @@ package composeplugin import ( "context" - "encoding/json" "fmt" "time" "github.com/portainer/portainer/pkg/libstack" + "github.com/rs/zerolog/log" + "github.com/segmentio/encoding/json" ) type publisher struct {