diff --git a/internal/apply/README.md b/internal/apply/README.md index 3b4263ce..d9ea54c0 100644 --- a/internal/apply/README.md +++ b/internal/apply/README.md @@ -1,6 +1,6 @@ # nais apply -Apply Valkey and OpenSearch resources to a Nais environment. +Apply Valkey, OpenSearch and Config resources to a Nais environment. ## Usage @@ -54,9 +54,12 @@ spec: ... ``` -Resources that have a dedicated nais-api mutation (Valkey, OpenSearch) are -created or updated through that mutation. Other kinds are converted back into a -native CRD and sent to the generic apply endpoint. +Some resources (e.g. Config) use `data`/`binaryData` at the top level instead +of `spec`. See the Config section below. + +Resources that have a dedicated nais-api mutation (Valkey, OpenSearch, Config) +are created or updated through that mutation. Other kinds are converted back +into a native CRD and sent to the generic apply endpoint. Multiple resources can be placed in the same file separated by `---`, and the native format may be mixed with regular Kubernetes CRDs (see below). @@ -117,6 +120,27 @@ spec: storageGB: 50 ``` +### Config + +Config uses `data` and `binaryData` at the top level instead of `spec`. The +manifest is fully declarative: keys present in the manifest are added or +updated, and keys missing from the manifest are removed. + +```yaml +version: v1 +kind: Config +metadata: + name: my-config + labels: # optional + purpose: backend +data: + DATABASE_HOST: db.example.com + LOG_LEVEL: info + PORT: "8080" +binaryData: # optional; values must be base64-encoded + keystore.p12: aGVsbG8gd29ybGQ= +``` + ### Multi-resource file ```yaml diff --git a/internal/apply/apply.go b/internal/apply/apply.go index 70e3547b..830920d7 100644 --- a/internal/apply/apply.go +++ b/internal/apply/apply.go @@ -110,7 +110,7 @@ func Run(ctx context.Context, filePath string, flags *flag.Apply, out *naistrix. TeamSlug: flags.Team, EnvironmentName: environment, Labels: m.Labels, - }, &m.Spec) + }, m) if err != nil { out.Warnf("%s/%s: %v\n", m.Kind, m.Name, err) errs = append(errs, fmt.Sprintf("%s/%s: %v", m.Kind, m.Name, err)) diff --git a/internal/apply/resource/config.go b/internal/apply/resource/config.go new file mode 100644 index 00000000..5bda0042 --- /dev/null +++ b/internal/apply/resource/config.go @@ -0,0 +1,110 @@ +package resource + +import ( + "context" + "fmt" + + "github.com/nais/cli/internal/config" + "github.com/nais/cli/internal/naisapi" + "github.com/nais/cli/internal/naisapi/gql" +) + +func init() { + register(configResource{kindSupport{kind: "Config", strippedVersion: "v1"}}) +} + +// configResource applies Config instances through the nais-api Config mutations. +// Unlike Valkey and OpenSearch, Config uses top-level data/binaryData fields +// instead of spec, and the API operates on individual values rather than a +// single create-or-update mutation. +type configResource struct{ kindSupport } + +func (c configResource) Apply(ctx context.Context, meta Metadata, m Manifest) (Action, error) { + cmeta := config.Metadata{ + Name: meta.Name, + TeamSlug: meta.TeamSlug, + EnvironmentName: meta.EnvironmentName, + } + + existing, action, err := c.ensureExists(ctx, cmeta) + if err != nil { + return "", err + } + + // Build a set of existing values for efficient lookup. + existingValues := make(map[string]gql.GetConfigTeamEnvironmentConfigValuesConfigValue, len(existing.Values)) + for _, v := range existing.Values { + existingValues[v.Name] = v + } + + // Desired state: merge data (PLAIN_TEXT) and binaryData (BASE64). + desired := make(map[string]configValue, len(m.Data)+len(m.BinaryData)) + for k, v := range m.Data { + desired[k] = configValue{value: v, encoding: gql.ValueEncodingPlainText} + } + for k, v := range m.BinaryData { + desired[k] = configValue{value: v, encoding: gql.ValueEncodingBase64} + } + + // Add or update values present in the manifest. + for key, dv := range desired { + if ev, exists := existingValues[key]; exists { + if ev.Value == dv.value && ev.Encoding == dv.encoding { + continue // unchanged + } + if _, err := config.SetValue(ctx, cmeta, key, dv.value, dv.encoding); err != nil { + return "", fmt.Errorf("updating value %q: %w", key, err) + } + action = ActionUpdated + } else { + if _, err := config.SetValue(ctx, cmeta, key, dv.value, dv.encoding); err != nil { + return "", fmt.Errorf("adding value %q: %w", key, err) + } + if action != ActionCreated { + action = ActionUpdated + } + } + } + + // Remove values not present in the manifest. + for key := range existingValues { + if _, keep := desired[key]; !keep { + if err := config.RemoveValue(ctx, cmeta, key); err != nil { + return "", fmt.Errorf("removing value %q: %w", key, err) + } + action = ActionUpdated + } + } + + return action, nil +} + +type configValue struct { + value string + encoding gql.ValueEncoding +} + +// ensureExists creates the config if it does not exist, returning the current +// state and whether it was just created. +func (c configResource) ensureExists(ctx context.Context, meta config.Metadata) (*gql.GetConfigTeamEnvironmentConfig, Action, error) { + existing, err := config.Get(ctx, meta) + if err == nil { + return existing, ActionUpdated, nil + } + + if !naisapi.IsNotFound(err) { + return nil, "", err + } + + if _, err := config.Create(ctx, meta); err != nil { + return nil, "", fmt.Errorf("creating config: %w", err) + } + + // Fetch the newly created config to get a consistent state. + existing, err = config.Get(ctx, meta) + if err != nil { + return nil, "", fmt.Errorf("fetching newly created config: %w", err) + } + + return existing, ActionCreated, nil +} diff --git a/internal/apply/resource/opensearch.go b/internal/apply/resource/opensearch.go index 49366207..63b14307 100644 --- a/internal/apply/resource/opensearch.go +++ b/internal/apply/resource/opensearch.go @@ -6,7 +6,6 @@ import ( "github.com/nais/cli/internal/naisapi" "github.com/nais/cli/internal/naisapi/gql" "github.com/nais/cli/internal/opensearch" - "gopkg.in/yaml.v3" ) func init() { @@ -46,9 +45,9 @@ var ( } ) -func (o openSearchResource) Apply(ctx context.Context, meta Metadata, spec *yaml.Node) (Action, error) { +func (o openSearchResource) Apply(ctx context.Context, meta Metadata, m Manifest) (Action, error) { var s openSearchSpec - if err := decodeSpec(spec, &s); err != nil { + if err := decodeSpec(&m.Spec, &s); err != nil { return "", err } diff --git a/internal/apply/resource/parse.go b/internal/apply/resource/parse.go index 6aa718e4..eb581e3e 100644 --- a/internal/apply/resource/parse.go +++ b/internal/apply/resource/parse.go @@ -19,7 +19,7 @@ var allowedVersions = map[string]struct{}{ // allowedTopLevel and allowedMetadata are the only fields a stripped manifest may // contain; anything else is reported as an ignored field. var ( - allowedTopLevel = map[string]struct{}{"version": {}, "kind": {}, "metadata": {}, "spec": {}} + allowedTopLevel = map[string]struct{}{"version": {}, "kind": {}, "metadata": {}, "spec": {}, "data": {}, "binaryData": {}} allowedMetadata = map[string]struct{}{"name": {}, "labels": {}} ) @@ -32,6 +32,11 @@ type Manifest struct { Labels map[string]string Spec yaml.Node + // Data holds top-level key-value data for resources that use data/binaryData + // instead of spec (e.g. Config). + Data map[string]string + BinaryData map[string]string + // IgnoredFields are envelope fields that are not part of the nais-native // format (e.g. "metadata.namespace"). IgnoredFields []string @@ -80,7 +85,9 @@ func ParseManifest(root *yaml.Node) (Manifest, error) { Name string `yaml:"name"` Labels map[string]string `yaml:"labels,omitempty"` } `yaml:"metadata"` - Spec yaml.Node `yaml:"spec"` + Spec yaml.Node `yaml:"spec"` + Data map[string]string `yaml:"data,omitempty"` + BinaryData map[string]string `yaml:"binaryData,omitempty"` } if err := root.Decode(&raw); err != nil { return Manifest{}, fmt.Errorf("failed to decode manifest: %w", err) @@ -105,6 +112,8 @@ func ParseManifest(root *yaml.Node) (Manifest, error) { Name: raw.Metadata.Name, Spec: raw.Spec, Labels: raw.Metadata.Labels, + Data: raw.Data, + BinaryData: raw.BinaryData, IgnoredFields: ignoredFields(root), }, nil } diff --git a/internal/apply/resource/parse_test.go b/internal/apply/resource/parse_test.go index aad85e39..fc61c4b7 100644 --- a/internal/apply/resource/parse_test.go +++ b/internal/apply/resource/parse_test.go @@ -223,6 +223,79 @@ func TestDocuments_SplitsAndSkipsEmpty(t *testing.T) { } } +func TestParse_Config(t *testing.T) { + manifests, err := Parse([]byte(` +version: v1 +kind: Config +metadata: + name: my-config + labels: + purpose: backend +data: + DATABASE_HOST: db.example.com + LOG_LEVEL: info + PORT: "8080" +binaryData: + keystore.p12: aGVsbG8gd29ybGQ= +`)) + if err != nil { + t.Fatalf("Parse: %v", err) + } + if len(manifests) != 1 { + t.Fatalf("expected 1 manifest, got %d", len(manifests)) + } + + m := manifests[0] + if got, want := m.Kind, "Config"; got != want { + t.Errorf("kind = %q, want %q", got, want) + } + if got, want := m.Name, "my-config"; got != want { + t.Errorf("name = %q, want %q", got, want) + } + if len(m.IgnoredFields) != 0 { + t.Errorf("expected no ignored fields, got %v", m.IgnoredFields) + } + + wantLabels := map[string]string{"purpose": "backend"} + if diff := cmp.Diff(wantLabels, m.Labels); diff != "" { + t.Errorf("labels mismatch (-want +got):\n%s", diff) + } + + wantData := map[string]string{ + "DATABASE_HOST": "db.example.com", + "LOG_LEVEL": "info", + "PORT": "8080", + } + if diff := cmp.Diff(wantData, m.Data); diff != "" { + t.Errorf("data mismatch (-want +got):\n%s", diff) + } + + wantBinaryData := map[string]string{ + "keystore.p12": "aGVsbG8gd29ybGQ=", + } + if diff := cmp.Diff(wantBinaryData, m.BinaryData); diff != "" { + t.Errorf("binaryData mismatch (-want +got):\n%s", diff) + } +} + +func TestParse_ConfigNoIgnoredFields(t *testing.T) { + // Config uses data/binaryData at top level — these should NOT be ignored fields. + manifests, err := Parse([]byte(` +version: v1 +kind: Config +metadata: + name: test +data: + KEY: value +`)) + if err != nil { + t.Fatalf("Parse: %v", err) + } + if len(manifests[0].IgnoredFields) != 0 { + t.Errorf("expected no ignored fields, got %v", manifests[0].IgnoredFields) + } +} + func mustDocument(t *testing.T, manifest string) *yaml.Node { t.Helper() docs, err := Documents([]byte(manifest)) diff --git a/internal/apply/resource/resource.go b/internal/apply/resource/resource.go index 37915c6d..801e7843 100644 --- a/internal/apply/resource/resource.go +++ b/internal/apply/resource/resource.go @@ -16,7 +16,6 @@ import ( "time" "github.com/nais/naistrix" - "gopkg.in/yaml.v3" ) // Resource is a kind that `nais apply` knows about, scoped to the manifest @@ -36,7 +35,7 @@ type Resource interface { // Applier is implemented by resources with a dedicated nais-api mutation, using // create-or-update semantics. type Applier interface { - Apply(ctx context.Context, meta Metadata, spec *yaml.Node) (Action, error) + Apply(ctx context.Context, meta Metadata, m Manifest) (Action, error) } // Waiter is implemented by resources that support --wait. since is the apply diff --git a/internal/apply/resource/resource_test.go b/internal/apply/resource/resource_test.go index b2a381c7..562883f5 100644 --- a/internal/apply/resource/resource_test.go +++ b/internal/apply/resource/resource_test.go @@ -44,6 +44,9 @@ func TestForManifest(t *testing.T) { "opensearch v1 resolves to a mutation": { kind: "OpenSearch", version: "v1", wantFound: true, wantApplier: true, wantWaiter: false, }, + "config v1 resolves to a mutation": { + kind: "Config", version: "v1", wantFound: true, wantApplier: true, wantWaiter: false, + }, "application is not handled as a stripped manifest": { kind: "Application", version: "v1", wantFound: false, }, diff --git a/internal/apply/resource/valkey.go b/internal/apply/resource/valkey.go index 030c41f4..4ba69a87 100644 --- a/internal/apply/resource/valkey.go +++ b/internal/apply/resource/valkey.go @@ -6,7 +6,6 @@ import ( "github.com/nais/cli/internal/naisapi" "github.com/nais/cli/internal/naisapi/gql" "github.com/nais/cli/internal/valkey" - "gopkg.in/yaml.v3" ) func init() { @@ -63,9 +62,9 @@ var ( } ) -func (v valkeyResource) Apply(ctx context.Context, meta Metadata, spec *yaml.Node) (Action, error) { +func (v valkeyResource) Apply(ctx context.Context, meta Metadata, m Manifest) (Action, error) { var s valkeySpec - if err := decodeSpec(spec, &s); err != nil { + if err := decodeSpec(&m.Spec, &s); err != nil { return "", err } diff --git a/internal/apply/testdata/config.yaml b/internal/apply/testdata/config.yaml new file mode 100644 index 00000000..2ca86d07 --- /dev/null +++ b/internal/apply/testdata/config.yaml @@ -0,0 +1,10 @@ +version: v1 +kind: Config +metadata: + name: my-backend-config +data: + DATABASE_HOST: db.example.com + LOG_LEVEL: info + PORT: "8080" +binaryData: + keystore.p12: aGVsbG8gd29ybGQ= diff --git a/internal/config/config.go b/internal/config/config.go index 7c8a256f..b8c25478 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -191,6 +191,50 @@ func Create(ctx context.Context, metadata Metadata) (*gql.CreateConfigCreateConf return &resp.CreateConfig.Config, nil } +// CreateWithValues creates a new config with the given values and labels in a single operation. +func CreateWithValues(ctx context.Context, metadata Metadata, values []gql.ConfigValueInput, labels []gql.ResourceLabelInput) error { + _ = `# @genqlient + mutation CreateConfigWithValues($name: String!, $environmentName: String!, $teamSlug: Slug!, $values: [ConfigValueInput!], $labels: [ResourceLabelInput!]) { + createConfig(input: {name: $name, environmentName: $environmentName, teamSlug: $teamSlug, values: $values, labels: $labels}) { + config { + id + name + } + } + } + ` + + client, err := naisapi.GraphqlClient(ctx) + if err != nil { + return err + } + + _, err = gql.CreateConfigWithValues(ctx, client, metadata.Name, metadata.EnvironmentName, metadata.TeamSlug, values, labels) + return err +} + +// UpdateWithValues replaces all values and labels on an existing config in a single operation. +func UpdateWithValues(ctx context.Context, metadata Metadata, values []gql.ConfigValueInput, labels []gql.ResourceLabelInput) error { + _ = `# @genqlient + mutation UpdateConfigWithValues($name: String!, $environmentName: String!, $teamSlug: Slug!, $values: [ConfigValueInput!], $labels: [ResourceLabelInput!]) { + updateConfig(input: {name: $name, environmentName: $environmentName, teamSlug: $teamSlug, values: $values, labels: $labels}) { + config { + id + name + } + } + } + ` + + client, err := naisapi.GraphqlClient(ctx) + if err != nil { + return err + } + + _, err = gql.UpdateConfigWithValues(ctx, client, metadata.Name, metadata.EnvironmentName, metadata.TeamSlug, values, labels) + return err +} + // Delete deletes a config and all its values. func Delete(ctx context.Context, metadata Metadata) (bool, error) { _ = `# @genqlient diff --git a/internal/naisapi/gql/generated.go b/internal/naisapi/gql/generated.go index 96404cac..6a7e3866 100644 --- a/internal/naisapi/gql/generated.go +++ b/internal/naisapi/gql/generated.go @@ -761,6 +761,45 @@ func (v *CreateConfigResponse) GetCreateConfig() CreateConfigCreateConfigCreateC return v.CreateConfig } +// CreateConfigWithValuesCreateConfigCreateConfigPayload includes the requested fields of the GraphQL type CreateConfigPayload. +type CreateConfigWithValuesCreateConfigCreateConfigPayload struct { + // The created config. + Config CreateConfigWithValuesCreateConfigCreateConfigPayloadConfig `json:"config"` +} + +// GetConfig returns CreateConfigWithValuesCreateConfigCreateConfigPayload.Config, and is useful for accessing the field via an interface. +func (v *CreateConfigWithValuesCreateConfigCreateConfigPayload) GetConfig() CreateConfigWithValuesCreateConfigCreateConfigPayloadConfig { + return v.Config +} + +// CreateConfigWithValuesCreateConfigCreateConfigPayloadConfig includes the requested fields of the GraphQL type Config. +// The GraphQL type's documentation follows. +// +// A config is a collection of key-value pairs. +type CreateConfigWithValuesCreateConfigCreateConfigPayloadConfig struct { + // The globally unique ID of the config. + Id string `json:"id"` + // The name of the config. + Name string `json:"name"` +} + +// GetId returns CreateConfigWithValuesCreateConfigCreateConfigPayloadConfig.Id, and is useful for accessing the field via an interface. +func (v *CreateConfigWithValuesCreateConfigCreateConfigPayloadConfig) GetId() string { return v.Id } + +// GetName returns CreateConfigWithValuesCreateConfigCreateConfigPayloadConfig.Name, and is useful for accessing the field via an interface. +func (v *CreateConfigWithValuesCreateConfigCreateConfigPayloadConfig) GetName() string { return v.Name } + +// CreateConfigWithValuesResponse is returned by CreateConfigWithValues on success. +type CreateConfigWithValuesResponse struct { + // Create a new config. + CreateConfig CreateConfigWithValuesCreateConfigCreateConfigPayload `json:"createConfig"` +} + +// GetCreateConfig returns CreateConfigWithValuesResponse.CreateConfig, and is useful for accessing the field via an interface. +func (v *CreateConfigWithValuesResponse) GetCreateConfig() CreateConfigWithValuesCreateConfigCreateConfigPayload { + return v.CreateConfig +} + // CreateKafkaCredentialsCreateKafkaCredentialsCreateKafkaCredentialsPayload includes the requested fields of the GraphQL type CreateKafkaCredentialsPayload. type CreateKafkaCredentialsCreateKafkaCredentialsCreateKafkaCredentialsPayload struct { // The generated credentials. @@ -32309,6 +32348,45 @@ func (v *UpdateConfigValueUpdateConfigValueUpdateConfigValuePayloadConfig) GetNa return v.Name } +// UpdateConfigWithValuesResponse is returned by UpdateConfigWithValues on success. +type UpdateConfigWithValuesResponse struct { + // Update the user-defined labels of a config. + UpdateConfig UpdateConfigWithValuesUpdateConfigUpdateConfigPayload `json:"updateConfig"` +} + +// GetUpdateConfig returns UpdateConfigWithValuesResponse.UpdateConfig, and is useful for accessing the field via an interface. +func (v *UpdateConfigWithValuesResponse) GetUpdateConfig() UpdateConfigWithValuesUpdateConfigUpdateConfigPayload { + return v.UpdateConfig +} + +// UpdateConfigWithValuesUpdateConfigUpdateConfigPayload includes the requested fields of the GraphQL type UpdateConfigPayload. +type UpdateConfigWithValuesUpdateConfigUpdateConfigPayload struct { + // The updated config. + Config UpdateConfigWithValuesUpdateConfigUpdateConfigPayloadConfig `json:"config"` +} + +// GetConfig returns UpdateConfigWithValuesUpdateConfigUpdateConfigPayload.Config, and is useful for accessing the field via an interface. +func (v *UpdateConfigWithValuesUpdateConfigUpdateConfigPayload) GetConfig() UpdateConfigWithValuesUpdateConfigUpdateConfigPayloadConfig { + return v.Config +} + +// UpdateConfigWithValuesUpdateConfigUpdateConfigPayloadConfig includes the requested fields of the GraphQL type Config. +// The GraphQL type's documentation follows. +// +// A config is a collection of key-value pairs. +type UpdateConfigWithValuesUpdateConfigUpdateConfigPayloadConfig struct { + // The globally unique ID of the config. + Id string `json:"id"` + // The name of the config. + Name string `json:"name"` +} + +// GetId returns UpdateConfigWithValuesUpdateConfigUpdateConfigPayloadConfig.Id, and is useful for accessing the field via an interface. +func (v *UpdateConfigWithValuesUpdateConfigUpdateConfigPayloadConfig) GetId() string { return v.Id } + +// GetName returns UpdateConfigWithValuesUpdateConfigUpdateConfigPayloadConfig.Name, and is useful for accessing the field via an interface. +func (v *UpdateConfigWithValuesUpdateConfigUpdateConfigPayloadConfig) GetName() string { return v.Name } + // UpdateOpenSearchResponse is returned by UpdateOpenSearch on success. type UpdateOpenSearchResponse struct { // Update an existing OpenSearch instance. @@ -33020,6 +33098,30 @@ func (v *__CreateConfigInput) GetEnvironmentName() string { return v.Environment // GetTeamSlug returns __CreateConfigInput.TeamSlug, and is useful for accessing the field via an interface. func (v *__CreateConfigInput) GetTeamSlug() string { return v.TeamSlug } +// __CreateConfigWithValuesInput is used internally by genqlient +type __CreateConfigWithValuesInput struct { + Name string `json:"name"` + EnvironmentName string `json:"environmentName"` + TeamSlug string `json:"teamSlug"` + Values []ConfigValueInput `json:"values"` + Labels []ResourceLabelInput `json:"labels"` +} + +// GetName returns __CreateConfigWithValuesInput.Name, and is useful for accessing the field via an interface. +func (v *__CreateConfigWithValuesInput) GetName() string { return v.Name } + +// GetEnvironmentName returns __CreateConfigWithValuesInput.EnvironmentName, and is useful for accessing the field via an interface. +func (v *__CreateConfigWithValuesInput) GetEnvironmentName() string { return v.EnvironmentName } + +// GetTeamSlug returns __CreateConfigWithValuesInput.TeamSlug, and is useful for accessing the field via an interface. +func (v *__CreateConfigWithValuesInput) GetTeamSlug() string { return v.TeamSlug } + +// GetValues returns __CreateConfigWithValuesInput.Values, and is useful for accessing the field via an interface. +func (v *__CreateConfigWithValuesInput) GetValues() []ConfigValueInput { return v.Values } + +// GetLabels returns __CreateConfigWithValuesInput.Labels, and is useful for accessing the field via an interface. +func (v *__CreateConfigWithValuesInput) GetLabels() []ResourceLabelInput { return v.Labels } + // __CreateKafkaCredentialsInput is used internally by genqlient type __CreateKafkaCredentialsInput struct { TeamSlug string `json:"teamSlug"` @@ -33984,6 +34086,30 @@ func (v *__UpdateConfigValueInput) GetTeamSlug() string { return v.TeamSlug } // GetValue returns __UpdateConfigValueInput.Value, and is useful for accessing the field via an interface. func (v *__UpdateConfigValueInput) GetValue() ConfigValueInput { return v.Value } +// __UpdateConfigWithValuesInput is used internally by genqlient +type __UpdateConfigWithValuesInput struct { + Name string `json:"name"` + EnvironmentName string `json:"environmentName"` + TeamSlug string `json:"teamSlug"` + Values []ConfigValueInput `json:"values"` + Labels []ResourceLabelInput `json:"labels"` +} + +// GetName returns __UpdateConfigWithValuesInput.Name, and is useful for accessing the field via an interface. +func (v *__UpdateConfigWithValuesInput) GetName() string { return v.Name } + +// GetEnvironmentName returns __UpdateConfigWithValuesInput.EnvironmentName, and is useful for accessing the field via an interface. +func (v *__UpdateConfigWithValuesInput) GetEnvironmentName() string { return v.EnvironmentName } + +// GetTeamSlug returns __UpdateConfigWithValuesInput.TeamSlug, and is useful for accessing the field via an interface. +func (v *__UpdateConfigWithValuesInput) GetTeamSlug() string { return v.TeamSlug } + +// GetValues returns __UpdateConfigWithValuesInput.Values, and is useful for accessing the field via an interface. +func (v *__UpdateConfigWithValuesInput) GetValues() []ConfigValueInput { return v.Values } + +// GetLabels returns __UpdateConfigWithValuesInput.Labels, and is useful for accessing the field via an interface. +func (v *__UpdateConfigWithValuesInput) GetLabels() []ResourceLabelInput { return v.Labels } + // __UpdateOpenSearchInput is used internally by genqlient type __UpdateOpenSearchInput struct { Name string `json:"name,omitempty"` @@ -34323,6 +34449,51 @@ func CreateConfig( return data_, err_ } +// The mutation executed by CreateConfigWithValues. +const CreateConfigWithValues_Operation = ` +mutation CreateConfigWithValues ($name: String!, $environmentName: String!, $teamSlug: Slug!, $values: [ConfigValueInput!], $labels: [ResourceLabelInput!]) { + createConfig(input: {name:$name,environmentName:$environmentName,teamSlug:$teamSlug,values:$values,labels:$labels}) { + config { + id + name + } + } +} +` + +func CreateConfigWithValues( + ctx_ context.Context, + client_ graphql.Client, + name string, + environmentName string, + teamSlug string, + values []ConfigValueInput, + labels []ResourceLabelInput, +) (data_ *CreateConfigWithValuesResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "CreateConfigWithValues", + Query: CreateConfigWithValues_Operation, + Variables: &__CreateConfigWithValuesInput{ + Name: name, + EnvironmentName: environmentName, + TeamSlug: teamSlug, + Values: values, + Labels: labels, + }, + } + + data_ = &CreateConfigWithValuesResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return data_, err_ +} + // The mutation executed by CreateKafkaCredentials. const CreateKafkaCredentials_Operation = ` mutation CreateKafkaCredentials ($teamSlug: Slug!, $environmentName: String!, $ttl: String!) { @@ -37623,6 +37794,51 @@ func UpdateConfigValue( return data_, err_ } +// The mutation executed by UpdateConfigWithValues. +const UpdateConfigWithValues_Operation = ` +mutation UpdateConfigWithValues ($name: String!, $environmentName: String!, $teamSlug: Slug!, $values: [ConfigValueInput!], $labels: [ResourceLabelInput!]) { + updateConfig(input: {name:$name,environmentName:$environmentName,teamSlug:$teamSlug,values:$values,labels:$labels}) { + config { + id + name + } + } +} +` + +func UpdateConfigWithValues( + ctx_ context.Context, + client_ graphql.Client, + name string, + environmentName string, + teamSlug string, + values []ConfigValueInput, + labels []ResourceLabelInput, +) (data_ *UpdateConfigWithValuesResponse, err_ error) { + req_ := &graphql.Request{ + OpName: "UpdateConfigWithValues", + Query: UpdateConfigWithValues_Operation, + Variables: &__UpdateConfigWithValuesInput{ + Name: name, + EnvironmentName: environmentName, + TeamSlug: teamSlug, + Values: values, + Labels: labels, + }, + } + + data_ = &UpdateConfigWithValuesResponse{} + resp_ := &graphql.Response{Data: data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return data_, err_ +} + // The mutation executed by UpdateOpenSearch. const UpdateOpenSearch_Operation = ` mutation UpdateOpenSearch ($name: String!, $environmentName: String!, $teamSlug: Slug!, $memory: OpenSearchMemory!, $tier: OpenSearchTier!, $version: OpenSearchMajorVersion!, $storageGB: Int!) { diff --git a/schema.graphql b/schema.graphql index f9267d88..ccd7048e 100644 --- a/schema.graphql +++ b/schema.graphql @@ -2509,6 +2509,8 @@ input CreateConfigInput { name: String! environmentName: String! teamSlug: Slug! + labels: [ResourceLabelInput!] + values: [ConfigValueInput!] } type CreateConfigPayload { @@ -12063,7 +12065,8 @@ input UpdateConfigInput { name: String! environmentName: String! teamSlug: Slug! - labels: [ResourceLabelInput!]! + labels: [ResourceLabelInput!] + values: [ConfigValueInput!] } type UpdateConfigPayload {