feat: Implement auth_config for docker_registry_image (#701)

* feat: Implement auth_config for docker_registry_image

* fix: Formatting
This commit is contained in:
Martin 2025-04-18 17:16:05 +02:00 committed by GitHub
parent 1b354111b4
commit 98ccee6b68
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 115 additions and 8 deletions

View file

@ -37,6 +37,7 @@ resource "docker_image" "image" {
### Optional
- `auth_config` (Block List, Max: 1) Authentication configuration for the Docker registry. It is only used for this resource. (see [below for nested schema](#nestedblock--auth_config))
- `insecure_skip_verify` (Boolean) If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false`
- `keep_remotely` (Boolean) If true, then the Docker image won't be deleted on destroy operation. If this is false, it will delete the image from the docker registry on destroy operation. Defaults to `false`
- `triggers` (Map of String) A map of arbitrary strings that, when changed, will force the `docker_registry_image` resource to be replaced. This can be used to repush a local image
@ -44,4 +45,13 @@ resource "docker_image" "image" {
### Read-Only
- `id` (String) The ID of this resource.
- `sha256_digest` (String) The sha256 digest of the image.
- `sha256_digest` (String) The sha256 digest of the image.
<a id="nestedblock--auth_config"></a>
### Nested Schema for `auth_config`
Required:
- `address` (String) The address of the Docker registry.
- `password` (String, Sensitive) The password for the Docker registry.
- `username` (String) The username for the Docker registry.

View file

@ -47,6 +47,33 @@ func resourceDockerRegistryImage() *schema.Resource {
Description: "The sha256 digest of the image.",
Computed: true,
},
"auth_config": {
Type: schema.TypeList,
Description: "Authentication configuration for the Docker registry. It is only used for this resource.",
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"address": {
Type: schema.TypeString,
Description: "The address of the Docker registry.",
Required: true,
},
"username": {
Type: schema.TypeString,
Description: "The username for the Docker registry.",
Required: true,
},
"password": {
Type: schema.TypeString,
Description: "The password for the Docker registry.",
Required: true,
Sensitive: true,
},
},
},
},
},
}
}

View file

@ -22,6 +22,16 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func buildAuthConfigFromResource(v interface{}) registry.AuthConfig {
auth := v.([]interface{})[0].(map[string]interface{})
return registry.AuthConfig{
ServerAddress: normalizeRegistryAddress(auth["address"].(string)),
Username: auth["username"].(string),
Password: auth["password"].(string),
}
}
func resourceDockerRegistryImageCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*ProviderConfig).DockerClient
providerConfig := meta.(*ProviderConfig)
@ -30,10 +40,19 @@ func resourceDockerRegistryImageCreate(ctx context.Context, d *schema.ResourceDa
pushOpts := createPushImageOptions(name)
authConfig, err := getAuthConfigForRegistry(pushOpts.Registry, providerConfig)
if err != nil {
return diag.Errorf("resourceDockerRegistryImageCreate: Unable to get authConfig for registry: %s", err)
var authConfig registry.AuthConfig
if v, ok := d.GetOk("auth_config"); ok {
log.Printf("[INFO] Using auth config from resource: %s", v)
authConfig = buildAuthConfigFromResource(v)
} else {
log.Printf("[INFO] Using auth config from provider: %s", v)
var err error
authConfig, err = getAuthConfigForRegistry(pushOpts.Registry, providerConfig)
if err != nil {
return diag.Errorf("resourceDockerRegistryImageCreate: Unable to get authConfig for registry: %s", err)
}
}
if err := pushDockerRegistryImage(ctx, client, pushOpts, authConfig.Username, authConfig.Password); err != nil {
return diag.Errorf("Error pushing docker image: %s", err)
}
@ -41,7 +60,7 @@ func resourceDockerRegistryImageCreate(ctx context.Context, d *schema.ResourceDa
insecureSkipVerify := d.Get("insecure_skip_verify").(bool)
digest, err := getImageDigestWithFallback(pushOpts, authConfig.ServerAddress, authConfig.Username, authConfig.Password, insecureSkipVerify)
if err != nil {
return diag.Errorf("Unable to create image, image not found: %s", err)
return diag.Errorf("Got error getting registry image digest inside resourceDockerRegistryImageCreate: %s", err)
}
d.SetId(digest)
d.Set("sha256_digest", digest)
@ -52,9 +71,16 @@ func resourceDockerRegistryImageRead(ctx context.Context, d *schema.ResourceData
providerConfig := meta.(*ProviderConfig)
name := d.Get("name").(string)
pushOpts := createPushImageOptions(name)
authConfig, err := getAuthConfigForRegistry(pushOpts.Registry, providerConfig)
if err != nil {
return diag.Errorf("resourceDockerRegistryImageRead: Unable to get authConfig for registry: %s", err)
var authConfig registry.AuthConfig
if v, ok := d.GetOk("auth_config"); ok {
authConfig = buildAuthConfigFromResource(v)
} else {
var err error
authConfig, err = getAuthConfigForRegistry(pushOpts.Registry, providerConfig)
if err != nil {
return diag.Errorf("resourceDockerRegistryImageRead: Unable to get authConfig for registry: %s", err)
}
}
insecureSkipVerify := d.Get("insecure_skip_verify").(bool)

View file

@ -66,6 +66,26 @@ func TestAccDockerRegistryImageResource_pushMissingImage(t *testing.T) {
})
}
func TestAccDockerRegistryImageResource_withAuthConfig(t *testing.T) {
pushOptions := createPushImageOptions("127.0.0.1:15000/tftest-dockerregistryimage:1.0")
wd, _ := os.Getwd()
context := strings.ReplaceAll(filepath.Join(wd, "..", "..", "scripts", "testing", "docker_registry_image_context"), "\\", "\\\\")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(loadTestConfiguration(t, RESOURCE, "docker_registry_image", "testBuildDockerRegistryImageWithAuthConfig"), pushOptions.Name, context, pushOptions.Registry, "testuser", "testpwd"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("docker_registry_image.foo", "sha256_digest"),
),
},
},
CheckDestroy: testDockerRegistryImageInRegistry("testuser", "testpwd", pushOptions, true),
})
}
func testDockerRegistryImageNotInRegistry(pushOpts internalPushImageOptions) resource.TestCheckFunc {
return func(s *terraform.State) error {
providerConfig := testAccProvider.Meta().(*ProviderConfig)

View file

@ -0,0 +1,24 @@
provider "docker" {
alias = "private"
}
resource "docker_image" "foo_image" {
provider = docker.private
name = "%s"
build {
context = "%s"
}
}
resource "docker_registry_image" "foo" {
provider = docker.private
name = docker_image.foo_image.name
insecure_skip_verify = true
keep_remotely = true
auth_config {
address = "%s"
username = "%s"
password = "%s"
}
}