mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
cliconfig: Decode oci_default_credentials blocks
As of this commit we don't actually do anything with these once decoded. In future commits we'll add decoding of repository-specific oci_credentials blocks and then the logic for combining all of these settings together into an ociauthconfig.CredentialsConfigs representing the overall credentials selection policy for OCI repositories. Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
This commit is contained in:
parent
9b80b0e7a3
commit
e1b6411ad0
@ -65,6 +65,12 @@ type Config struct {
|
||||
// configuration, but we decode into a slice here so that we can handle
|
||||
// that validation at validation time rather than initial decode time.
|
||||
ProviderInstallation []*ProviderInstallation
|
||||
|
||||
// OCIDefaultCredentials represents any oci_default_credentials blocks
|
||||
// in the configuration. Only one of these is allowed across the whole
|
||||
// configuration, but we decode into a slice here so that we can handle
|
||||
// that validation at validation time rather than initial decode time.
|
||||
OCIDefaultCredentials []*OCIDefaultCredentials
|
||||
}
|
||||
|
||||
// ConfigHost is the structure of the "host" nested block within the CLI
|
||||
@ -177,12 +183,16 @@ func loadConfigFile(path string) (*Config, tfdiags.Diagnostics) {
|
||||
return result, diags
|
||||
}
|
||||
|
||||
// Deal with the provider_installation block, which is not handled using
|
||||
// DecodeObject because its structure is not compatible with the
|
||||
// limitations of that function.
|
||||
providerInstBlocks, moreDiags := decodeProviderInstallationFromConfig(obj)
|
||||
diags = diags.Append(moreDiags)
|
||||
// A few other blocks need some more special treatment because we are
|
||||
// using a structure that is not compatible with HCL 1's DecodeObject,
|
||||
// or HCL 1 would be too liberal in parsing and thus make it harder
|
||||
// for us to potentially transition to using HCL 2 later.
|
||||
providerInstBlocks, providerInstDiags := decodeProviderInstallationFromConfig(obj)
|
||||
diags = diags.Append(providerInstDiags)
|
||||
result.ProviderInstallation = providerInstBlocks
|
||||
ociDefaultCredsBlocks, ociDefaultCredsDiags := decodeOCIDefaultCredentialsFromConfig(obj)
|
||||
diags = diags.Append(ociDefaultCredsDiags)
|
||||
result.OCIDefaultCredentials = ociDefaultCredsBlocks
|
||||
|
||||
// Replace all env vars
|
||||
for k, v := range result.Providers {
|
||||
@ -327,6 +337,14 @@ func (c *Config) Validate() tfdiags.Diagnostics {
|
||||
)
|
||||
}
|
||||
|
||||
// Should have zero or one "oci_default_credentials" blocks
|
||||
if len(c.OCIDefaultCredentials) > 1 {
|
||||
diags = diags.Append(
|
||||
//nolint:stylecheck // Despite typical Go idiom, our existing precedent here is to return full sentences suitable for inclusion in diagnostics.
|
||||
fmt.Errorf("No more than one oci_default_credentials block may be specified"),
|
||||
)
|
||||
}
|
||||
|
||||
if c.PluginCacheDir != "" {
|
||||
_, err := os.Stat(c.PluginCacheDir)
|
||||
if err != nil {
|
||||
@ -413,6 +431,11 @@ func (c *Config) Merge(c2 *Config) *Config {
|
||||
result.ProviderInstallation = append(result.ProviderInstallation, c2.ProviderInstallation...)
|
||||
}
|
||||
|
||||
if (len(c.OCIDefaultCredentials) + len(c2.OCIDefaultCredentials)) > 0 {
|
||||
result.OCIDefaultCredentials = append(result.OCIDefaultCredentials, c.OCIDefaultCredentials...)
|
||||
result.OCIDefaultCredentials = append(result.OCIDefaultCredentials, c2.OCIDefaultCredentials...)
|
||||
}
|
||||
|
||||
return &result
|
||||
}
|
||||
|
||||
|
262
internal/command/cliconfig/oci_credentials.go
Normal file
262
internal/command/cliconfig/oci_credentials.go
Normal file
@ -0,0 +1,262 @@
|
||||
// Copyright (c) The OpenTofu Authors
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright (c) 2023 HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cliconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/hcl"
|
||||
hclast "github.com/hashicorp/hcl/hcl/ast"
|
||||
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
)
|
||||
|
||||
// OCIDefaultCredentials corresponds to one oci_default_credentials block in
|
||||
// the CLI configuration.
|
||||
//
|
||||
// This represents just one part of the overall OCI credentials policy, and so needs
|
||||
// to be considered in conjunction with all of the OCICredentials objects across
|
||||
// the CLI configuration too.
|
||||
type OCIDefaultCredentials struct {
|
||||
// DiscoverAmbientCredentials decides whether OpenTofu will attempt to find
|
||||
// credentials "ambiently" in the environment where OpenTofu is running, such
|
||||
// as searching the conventional locations for Docker-style configuration files.
|
||||
//
|
||||
// This defaults to true, but operators can set it to false to completely opt out
|
||||
// of OpenTofu using credentials from anywhere other than elsewhere in the
|
||||
// OpenTofu CLI configuration.
|
||||
DiscoverAmbientCredentials bool
|
||||
|
||||
// DockerStyleConfigFiles forces a specific set of filenames to try to use as
|
||||
// sources of OCI credentials, interpreting them as Docker CLI-style configuration
|
||||
// files.
|
||||
//
|
||||
// If this is nil, OpenTofu uses a default set of search locations mimicking the
|
||||
// behavior of other tools in the ecosystem such as Podman, Buildah, etc.
|
||||
//
|
||||
// If this is non-nil but zero length, it effectively disables using any Docker CLI-style
|
||||
// configuration files at all, but if DiscoverAmbientCredentials is also true then
|
||||
// future versions of OpenTofu might try to use other sources of ambient credentials.
|
||||
//
|
||||
// This field is always nil if DiscoverAmbientCredentials is false, because this field
|
||||
// exists only to customize one aspect of the "ambient credentials" discovery behavior.
|
||||
DockerStyleConfigFiles []string
|
||||
|
||||
// The name of a Docker-style credential helper program to use for any domain
|
||||
// that doesn't have its own specific credential helper configured.
|
||||
//
|
||||
// If this is not set then a default credential helper might still be discovered
|
||||
// from the ambient credentials sources, unless such discovery is disabled using
|
||||
// the other fields in this struct.
|
||||
DefaultDockerCredentialHelper string
|
||||
}
|
||||
|
||||
// newDefaultOCIDefaultCredentials returns an [OCIDefaultCredentials] object representing
|
||||
// the default settings used when no oci_default_credentials blocks are present.
|
||||
//
|
||||
// Each call to this function returns a distinct object, so it's safe for the caller
|
||||
// to modify the result to reflect any customizations.
|
||||
func newDefaultOCIDefaultCredentials() *OCIDefaultCredentials {
|
||||
return &OCIDefaultCredentials{
|
||||
DiscoverAmbientCredentials: true,
|
||||
DockerStyleConfigFiles: nil,
|
||||
DefaultDockerCredentialHelper: "",
|
||||
}
|
||||
}
|
||||
|
||||
// decodeOCIDefaultCredentialsFromConfig uses the HCL AST API directly to
|
||||
// decode "oci_default_credentials" blocks from the given file.
|
||||
//
|
||||
// The overall CLI configuration is only allowed to contain one
|
||||
// oci_default_credentials block, but the caller deals with that constraint
|
||||
// separately after searching all of the CLI configuration files.
|
||||
//
|
||||
// This uses the HCL AST directly, rather than HCL's decoder, to continue
|
||||
// our precedent of trying to constrain new features only to what could be
|
||||
// supported compatibly in a hypothetical future HCL 2-based implementation
|
||||
// of the CLI configuration language.
|
||||
//
|
||||
// Note that this function wants the top-level file object which might or
|
||||
// might not contain oci_default_credentials blocks, not an oci_default_credentials
|
||||
// block directly itself.
|
||||
func decodeOCIDefaultCredentialsFromConfig(hclFile *hclast.File) ([]*OCIDefaultCredentials, tfdiags.Diagnostics) {
|
||||
var ret []*OCIDefaultCredentials
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
root, ok := hclFile.Node.(*hclast.ObjectList)
|
||||
if !ok {
|
||||
// A HCL file that doesn't have an object list at its root is weird, but
|
||||
// dealing with that is outside the scope of this function.
|
||||
// (In practice both the native syntax and JSON parsers for HCL force
|
||||
// the root to be an ObjectList, so we should not get here for any real file.)
|
||||
return ret, diags
|
||||
}
|
||||
for _, block := range root.Items {
|
||||
if block.Keys[0].Token.Value() != "oci_default_credentials" {
|
||||
continue
|
||||
}
|
||||
|
||||
// HCL only tracks whether the input was JSON or native syntax inside
|
||||
// individual tokens, so we'll use our block type token to decide
|
||||
// and assume that the rest of the block must be written in the same
|
||||
// syntax, because syntax is a whole-file idea.
|
||||
const errInvalidSummary = "Invalid oci_default_credentials block"
|
||||
isJSON := block.Keys[0].Token.JSON
|
||||
if block.Assign.Line != 0 && !isJSON {
|
||||
// Seems to be an attribute rather than a block
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
errInvalidSummary,
|
||||
fmt.Sprintf("The oci_default_credentials block at %s must not be introduced with an equals sign.", block.Pos()),
|
||||
))
|
||||
continue
|
||||
}
|
||||
if len(block.Keys) > 1 && !isJSON {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
errInvalidSummary,
|
||||
fmt.Sprintf("The oci_default_credentials block at %s must not have any labels.", block.Pos()),
|
||||
))
|
||||
continue
|
||||
}
|
||||
body, ok := block.Val.(*hclast.ObjectType)
|
||||
if !ok {
|
||||
// We can't get in here with native HCL syntax because we
|
||||
// already checked above that we're using block syntax, but
|
||||
// if we're reading JSON then our value could potentially be
|
||||
// anything.
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
errInvalidSummary,
|
||||
fmt.Sprintf("The oci_default_credentials block at %s must be represented by a JSON object.", block.Pos()),
|
||||
))
|
||||
continue
|
||||
}
|
||||
|
||||
result, blockDiags := decodeOCIDefaultCredentialsBlockBody(body)
|
||||
diags = diags.Append(blockDiags)
|
||||
if result != nil {
|
||||
ret = append(ret, result)
|
||||
}
|
||||
}
|
||||
|
||||
return ret, diags
|
||||
}
|
||||
|
||||
func decodeOCIDefaultCredentialsBlockBody(body *hclast.ObjectType) (*OCIDefaultCredentials, tfdiags.Diagnostics) {
|
||||
const errInvalidSummary = "Invalid oci_default_credentials block"
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
// Although decodeOCIDefaultCredentialsFromConfig did some lower-level decoding
|
||||
// to try to force HCL 2-compatible syntax, the _content_ of this block is all
|
||||
// just relatively-simple arguments and so we can use HCL 1's decoder here.
|
||||
type BodyContent struct {
|
||||
DiscoverAmbientCredentials *bool `hcl:"discover_ambient_credentials"`
|
||||
DockerStyleConfigFiles *[]string `hcl:"docker_style_config_files"`
|
||||
DefaultDockerCredentialsHelper *string `hcl:"docker_credentials_helper"`
|
||||
}
|
||||
var bodyContent BodyContent
|
||||
err := hcl.DecodeObject(&bodyContent, body)
|
||||
if err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
errInvalidSummary,
|
||||
fmt.Sprintf("Invalid oci_default_credentials block at %s: %s.", body.Pos(), err),
|
||||
))
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
// We'll start with the default values and then override based on what was
|
||||
// specified in the configuration block.
|
||||
ret := newDefaultOCIDefaultCredentials()
|
||||
if bodyContent.DiscoverAmbientCredentials != nil {
|
||||
ret.DiscoverAmbientCredentials = *bodyContent.DiscoverAmbientCredentials
|
||||
}
|
||||
if bodyContent.DockerStyleConfigFiles != nil {
|
||||
ret.DockerStyleConfigFiles = *bodyContent.DockerStyleConfigFiles
|
||||
if ret.DockerStyleConfigFiles == nil {
|
||||
ret.DockerStyleConfigFiles = make([]string, 0) // non-nil represents explicitly nothing, rather that the default locations
|
||||
}
|
||||
}
|
||||
if bodyContent.DefaultDockerCredentialsHelper != nil {
|
||||
ret.DefaultDockerCredentialHelper = *bodyContent.DefaultDockerCredentialsHelper
|
||||
if !validDockerCredentialHelperName(ret.DefaultDockerCredentialHelper) {
|
||||
diags = append(diags, tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
errInvalidSummary,
|
||||
fmt.Sprintf(
|
||||
"The oci_default_credentials block at %s specifies the invalid Docker credential helper name %q. Must be a non-empty string that could be used as part of an executable filename.",
|
||||
body.Pos(), ret.DefaultDockerCredentialHelper,
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if !ret.DiscoverAmbientCredentials && ret.DockerStyleConfigFiles != nil {
|
||||
// docker_style_config_files is a modifier for the discover_ambient_credentials
|
||||
// behavior, so can't be used if discovery is totally disabled.
|
||||
diags = append(diags, tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
errInvalidSummary,
|
||||
fmt.Sprintf(
|
||||
"The oci_default_credentials block at %s disables discovery of ambient credentials, but also sets docker_style_config_files which is relevant only when ambient credentials discovery is enabled.",
|
||||
body.Pos(),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
return ret, diags
|
||||
}
|
||||
|
||||
// OCIRepositoryCredentials corresponds directly to a single oci_credentials block
|
||||
// in the CLI configuration, decoded in isolation. It represents the credentials
|
||||
// configuration for a set of OCI repositories with a specific registry domain and
|
||||
// optional repository path prefix.
|
||||
//
|
||||
// This represents just one part of the overall OCI credentials policy, and so needs
|
||||
// to be considered in conjunction with all of the other OCICredentials objects across
|
||||
// the CLI configuration, and the OCIDefaultCredentials object too.
|
||||
type OCIRepositoryCredentials struct {
|
||||
// RegistryDomain is the domain name (and optional port number) of the registry
|
||||
// containing the repositories that these credentials apply to.
|
||||
RegistryDomain string
|
||||
|
||||
// RepositoryPathPrefix is an optional path prefix that constrains which
|
||||
// repositories on RegistryDomain these credentials can be used for.
|
||||
RepositoryPathPrefix string
|
||||
|
||||
// Username and Password are credentials to use for a "Basic"-style
|
||||
// authentication method. These are mutually-exclusive with AccessToken
|
||||
// and RefreshToken.
|
||||
Username, Password string
|
||||
|
||||
// AccessToken and RefreshToken are credentials for an OAuth-style
|
||||
// authentication method. These are mutually-exclusive with Username
|
||||
// and Password.
|
||||
AccessToken, RefreshToken string
|
||||
}
|
||||
|
||||
// TODO: Implement decodeOCICredentialsFromConfig, returning []*OCIRepositoryCredentials
|
||||
|
||||
func validDockerCredentialHelperName(n string) bool {
|
||||
switch {
|
||||
case n == "":
|
||||
// It definitely can't be an empty string.
|
||||
return false
|
||||
case strings.Contains(filepath.ToSlash(n), `/`):
|
||||
// The exact details of what's valid here seem OS-specific and so we'll defer
|
||||
// the most detailed validation until we know we're actually going to try to
|
||||
// run the credentials helper, but at this point we do at least know that
|
||||
// the given name is going to be used as part of the filename of an executable
|
||||
// and so it definitely can't contain path separators accepted by the current
|
||||
// platform.
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
123
internal/command/cliconfig/oci_credentials_test.go
Normal file
123
internal/command/cliconfig/oci_credentials_test.go
Normal file
@ -0,0 +1,123 @@
|
||||
// Copyright (c) The OpenTofu Authors
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright (c) 2023 HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cliconfig
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestLoadConfig_ociDefaultCredentials(t *testing.T) {
|
||||
// The keys in this map correspond to fixture names under
|
||||
// the "testdata" directory.
|
||||
tests := map[string]struct {
|
||||
want *OCIDefaultCredentials
|
||||
wantErr string
|
||||
}{
|
||||
"oci-default-credentials": {
|
||||
&OCIDefaultCredentials{
|
||||
DiscoverAmbientCredentials: true,
|
||||
DockerStyleConfigFiles: []string{
|
||||
"/foo/bar/auth.json",
|
||||
},
|
||||
DefaultDockerCredentialHelper: "osxkeychain",
|
||||
},
|
||||
``,
|
||||
},
|
||||
"oci-default-credentials.json": {
|
||||
&OCIDefaultCredentials{
|
||||
DiscoverAmbientCredentials: true,
|
||||
DockerStyleConfigFiles: []string{
|
||||
"/foo/bar/auth.json",
|
||||
},
|
||||
DefaultDockerCredentialHelper: "osxkeychain",
|
||||
},
|
||||
``,
|
||||
},
|
||||
"oci-default-credentials-defaults": {
|
||||
&OCIDefaultCredentials{
|
||||
DiscoverAmbientCredentials: true,
|
||||
DockerStyleConfigFiles: nil, // represents "use the default search paths"
|
||||
DefaultDockerCredentialHelper: "", // represents no default credential helper at all
|
||||
},
|
||||
``,
|
||||
},
|
||||
"oci-default-credentials-no-docker": {
|
||||
&OCIDefaultCredentials{
|
||||
DiscoverAmbientCredentials: true,
|
||||
DockerStyleConfigFiles: []string{
|
||||
// Must be non-nil empty, because nil represents
|
||||
// "use the default search paths".
|
||||
},
|
||||
DefaultDockerCredentialHelper: "", // represents no default credential helper at all
|
||||
},
|
||||
``,
|
||||
},
|
||||
"oci-default-credentials-inconsistent": {
|
||||
&OCIDefaultCredentials{
|
||||
// The following is just a best-effort approximation of the
|
||||
// configuration despite the errors, so it's not super important
|
||||
// that it stay consistent in future releases but tested just
|
||||
// so that if it _does_ change we can review and make sure that
|
||||
// the change is reasonable.
|
||||
DiscoverAmbientCredentials: false,
|
||||
DockerStyleConfigFiles: []string{},
|
||||
DefaultDockerCredentialHelper: "",
|
||||
},
|
||||
`disables discovery of ambient credentials, but also sets docker_style_config_files`,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
fixtureFile := filepath.Join("testdata", name)
|
||||
gotConfig, diags := loadConfigFile(fixtureFile)
|
||||
if diags.HasErrors() {
|
||||
errStr := diags.Err().Error()
|
||||
if test.wantErr == "" {
|
||||
t.Errorf("unexpected errors: %s", errStr)
|
||||
}
|
||||
if !strings.Contains(errStr, test.wantErr) {
|
||||
t.Errorf("missing expected error\nwant substring: %s\ngot: %s", test.wantErr, errStr)
|
||||
}
|
||||
} else if test.wantErr != "" {
|
||||
t.Errorf("unexpected success\nwant error with substring: %s", test.wantErr)
|
||||
}
|
||||
|
||||
var got *OCIDefaultCredentials
|
||||
if len(gotConfig.OCIDefaultCredentials) > 0 {
|
||||
got = gotConfig.OCIDefaultCredentials[0]
|
||||
}
|
||||
if diff := cmp.Diff(test.want, got); diff != "" {
|
||||
t.Error("unexpected result\n" + diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("oci-default-credentials-duplicate", func(t *testing.T) {
|
||||
// This one is different than all of the others because it
|
||||
// only gets detected as invalid during the validation step,
|
||||
// so that (in the normal case) we can check it only after
|
||||
// we've merged all of the separate CLI config files together.
|
||||
fixtureFile := filepath.Join("testdata", "oci-default-credentials-duplicate")
|
||||
gotConfig, loadDiags := loadConfigFile(fixtureFile)
|
||||
if loadDiags.HasErrors() {
|
||||
t.Errorf("unexpected errors from loadConfigFile: %s", loadDiags.Err().Error())
|
||||
}
|
||||
|
||||
validateDiags := gotConfig.Validate()
|
||||
wantErr := `No more than one oci_default_credentials block may be specified`
|
||||
if !validateDiags.HasErrors() {
|
||||
t.Fatalf("unexpected success\nwant error with substring: %s", wantErr)
|
||||
}
|
||||
if errStr := validateDiags.Err().Error(); !strings.Contains(errStr, wantErr) {
|
||||
t.Errorf("missing expected error\nwant substring: %s\ngot: %s", wantErr, errStr)
|
||||
}
|
||||
})
|
||||
}
|
7
internal/command/cliconfig/testdata/oci-default-credentials
vendored
Normal file
7
internal/command/cliconfig/testdata/oci-default-credentials
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
oci_default_credentials {
|
||||
discover_ambient_credentials = true
|
||||
docker_style_config_files = [
|
||||
"/foo/bar/auth.json"
|
||||
]
|
||||
docker_credentials_helper = "osxkeychain"
|
||||
}
|
3
internal/command/cliconfig/testdata/oci-default-credentials-defaults
vendored
Normal file
3
internal/command/cliconfig/testdata/oci-default-credentials-defaults
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
oci_default_credentials {
|
||||
# Leaving the block completely empty selects all of the default settings
|
||||
}
|
6
internal/command/cliconfig/testdata/oci-default-credentials-duplicate
vendored
Normal file
6
internal/command/cliconfig/testdata/oci-default-credentials-duplicate
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
oci_default_credentials {
|
||||
}
|
||||
|
||||
oci_default_credentials {
|
||||
# Error: can't have more than one block of this type
|
||||
}
|
7
internal/command/cliconfig/testdata/oci-default-credentials-inconsistent
vendored
Normal file
7
internal/command/cliconfig/testdata/oci-default-credentials-inconsistent
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
oci_default_credentials {
|
||||
# Cannot set docker_style_config_files when discover_ambient_credentials is
|
||||
# disabled, because the Docker-style config files are only used to discover
|
||||
# ambient credentials.
|
||||
discover_ambient_credentials = false
|
||||
docker_style_config_files = []
|
||||
}
|
6
internal/command/cliconfig/testdata/oci-default-credentials-no-docker
vendored
Normal file
6
internal/command/cliconfig/testdata/oci-default-credentials-no-docker
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
oci_default_credentials {
|
||||
# Setting this to [] while leaving discover_ambient_credentials defaulted to
|
||||
# true means that OpenTofu won't search for docker-style files but a future
|
||||
# version of OpenTofu might still search for other "ambient" credentials.
|
||||
docker_style_config_files = []
|
||||
}
|
9
internal/command/cliconfig/testdata/oci-default-credentials.json
vendored
Normal file
9
internal/command/cliconfig/testdata/oci-default-credentials.json
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"oci_default_credentials": {
|
||||
"discover_ambient_credentials": true,
|
||||
"docker_style_config_files": [
|
||||
"/foo/bar/auth.json"
|
||||
],
|
||||
"docker_credentials_helper": "osxkeychain"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user