Skip to content

Commit 45e3127

Browse files
authored
feat: adds config file content as plain string (kreuzwerker#232)
Closes kreuzwerker#224 * feat(provider): adds plain registry content property * docs(provider): new plain config property * chore: comments why the order of auth parsing should not be changed
1 parent f4dd218 commit 45e3127

File tree

3 files changed

+89
-4
lines changed

3 files changed

+89
-4
lines changed

docker/provider.go

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func Provider() terraform.ResourceProvider {
7171
"username": {
7272
Type: schema.TypeString,
7373
Optional: true,
74-
ConflictsWith: []string{"registry_auth.config_file"},
74+
ConflictsWith: []string{"registry_auth.config_file", "registry_auth.config_file_content"},
7575
DefaultFunc: schema.EnvDefaultFunc("DOCKER_REGISTRY_USER", ""),
7676
Description: "Username for the registry",
7777
},
@@ -80,18 +80,25 @@ func Provider() terraform.ResourceProvider {
8080
Type: schema.TypeString,
8181
Optional: true,
8282
Sensitive: true,
83-
ConflictsWith: []string{"registry_auth.config_file"},
83+
ConflictsWith: []string{"registry_auth.config_file", "registry_auth.config_file_content"},
8484
DefaultFunc: schema.EnvDefaultFunc("DOCKER_REGISTRY_PASS", ""),
8585
Description: "Password for the registry",
8686
},
8787

8888
"config_file": {
8989
Type: schema.TypeString,
9090
Optional: true,
91-
ConflictsWith: []string{"registry_auth.username", "registry_auth.password"},
91+
ConflictsWith: []string{"registry_auth.username", "registry_auth.password", "registry_auth.config_file_content"},
9292
DefaultFunc: schema.EnvDefaultFunc("DOCKER_CONFIG", "~/.docker/config.json"),
9393
Description: "Path to docker json file for registry auth",
9494
},
95+
96+
"config_file_content": {
97+
Type: schema.TypeString,
98+
Optional: true,
99+
ConflictsWith: []string{"registry_auth.username", "registry_auth.password", "registry_auth.config_file"},
100+
Description: "Plain content of the docker json file for registry auth",
101+
},
95102
},
96103
},
97104
},
@@ -184,10 +191,43 @@ func providerSetToRegistryAuth(authSet *schema.Set) (*AuthConfigs, error) {
184191
// For each registry_auth block, generate an AuthConfiguration using either
185192
// username/password or the given config file
186193
if username, ok := auth["username"]; ok && username.(string) != "" {
194+
log.Println("[DEBUG] Using username for registry auths:", username)
187195
authConfig.Username = auth["username"].(string)
188196
authConfig.Password = auth["password"].(string)
197+
198+
// Note: check for config_file_content first because config_file has a default which would be used
199+
// neverthelesss config_file_content is set or not. The default has to be kept to check for the
200+
// environment variable and to be backwards compatible
201+
} else if configFileContent, ok := auth["config_file_content"]; ok && configFileContent.(string) != "" {
202+
log.Println("[DEBUG] Parsing file content for registry auths:", configFileContent.(string))
203+
r := strings.NewReader(configFileContent.(string))
204+
205+
// Parse and set the auth
206+
auths, err := newAuthConfigurations(r)
207+
if err != nil {
208+
return nil, fmt.Errorf("Error parsing docker registry config json: %v", err)
209+
}
210+
211+
foundRegistry := false
212+
for registry, authFileConfig := range auths.Configs {
213+
if authConfig.ServerAddress == normalizeRegistryAddress(registry) {
214+
authConfig.Username = authFileConfig.Username
215+
authConfig.Password = authFileConfig.Password
216+
foundRegistry = true
217+
}
218+
}
219+
220+
if !foundRegistry {
221+
return nil, fmt.Errorf("Couldn't find registry config for '%s' in file content", authConfig.ServerAddress)
222+
}
223+
224+
// As last step we check if a config file path is given
189225
} else if configFile, ok := auth["config_file"]; ok && configFile.(string) != "" {
190226
filePath := configFile.(string)
227+
log.Println("[DEBUG] Parsing file for registry auths:", filePath)
228+
229+
// We manually expand the path and do not use the 'pathexpand' interpolation function
230+
// because in the default of this varable we refer to '~/.docker/config.json'
191231
if strings.HasPrefix(filePath, "~/") {
192232
usr, err := user.Current()
193233
if err != nil {
@@ -201,6 +241,7 @@ func providerSetToRegistryAuth(authSet *schema.Set) (*AuthConfigs, error) {
201241
return nil, fmt.Errorf("Error opening docker registry config file: %v", err)
202242
}
203243

244+
// Parse and set the auth
204245
auths, err := newAuthConfigurations(r)
205246
if err != nil {
206247
return nil, fmt.Errorf("Error parsing docker registry config json: %v", err)

docker/resource_docker_image_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,28 @@ func TestAccDockerImage_data_private_config_file(t *testing.T) {
148148
})
149149
}
150150

151+
func TestAccDockerImage_data_private_config_file_content(t *testing.T) {
152+
registry := "127.0.0.1:15000"
153+
image := "127.0.0.1:15000/tftest-service:v1"
154+
wd, _ := os.Getwd()
155+
dockerConfig := wd + "/../scripts/testing/dockerconfig.json"
156+
157+
resource.Test(t, resource.TestCase{
158+
PreCheck: func() { testAccPreCheck(t) },
159+
Providers: testAccProviders,
160+
PreventPostDestroyRefresh: true,
161+
Steps: []resource.TestStep{
162+
{
163+
Config: fmt.Sprintf(testAccDockerImageFromDataPrivateConfigFileContent, registry, dockerConfig, image),
164+
Check: resource.ComposeTestCheckFunc(
165+
resource.TestMatchResourceAttr("docker_image.foo_private", "latest", contentDigestRegexp),
166+
),
167+
},
168+
},
169+
CheckDestroy: checkAndRemoveImages,
170+
})
171+
}
172+
151173
func TestAccDockerImage_sha265(t *testing.T) {
152174
resource.Test(t, resource.TestCase{
153175
PreCheck: func() { testAccPreCheck(t) },
@@ -251,6 +273,20 @@ resource "docker_image" "foo_private" {
251273
}
252274
`
253275

276+
const testAccDockerImageFromDataPrivateConfigFileContent = `
277+
provider "docker" {
278+
alias = "private"
279+
registry_auth {
280+
address = "%s"
281+
config_file_content = "${file("%s")}"
282+
}
283+
}
284+
resource "docker_image" "foo_private" {
285+
provider = "docker.private"
286+
name = "%s"
287+
}
288+
`
289+
254290
const testAddDockerImageWithSHA256RepoDigest = `
255291
resource "docker_image" "foobar" {
256292
name = "stocard/gotthard@sha256:ed752380c07940c651b46c97ca2101034b3be112f4d86198900aa6141f37fe7b"

website/docs/index.html.markdown

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ provider "docker" {
6262
config_file = "${pathexpand("~/.docker/config.json")}"
6363
}
6464
65+
registry_auth {
66+
address = "registry.my.company.com"
67+
config_file_content = "${var.plain_content_of_config_file}"
68+
}
69+
6570
registry_auth {
6671
address = "quay.io:8181"
6772
username = "someuser"
@@ -149,8 +154,11 @@ The following arguments are supported:
149154
will also be checked.
150155

151156
* `config_file` - (Optional) The path to a config file containing credentials for
152-
authenticating to the registry. Cannot be used with the `username`/`password` options.
157+
authenticating to the registry. Cannot be used with the `username`/`password` or `config_file_content` options.
153158
If this is blank, the `DOCKER_CONFIG` will also be checked.
159+
160+
* `config_file_content` - (Optional) The content of a config file as string containing credentials for
161+
authenticating to the registry. Cannot be used with the `username`/`password` or `config_file` options.
154162

155163

156164

0 commit comments

Comments
 (0)