terraform-provider-docker/internal/provider/data_source_docker_registry_image.go

199 lines
6.1 KiB
Go
Raw Normal View History

package provider
import (
"context"
"crypto/sha256"
b64 "encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func dataSourceDockerRegistryImage() *schema.Resource {
return &schema.Resource{
feat/doc generation (#193) * chore: add tfplugindocs tool * feat: add tfplugin doc dependency and make target * chore: apply documentation generation * docs(contributing): update for documentation generation * fix: adapt website-lint target to new do folder * docs(network): update ds descriptions * docs: add template for index.md * docs: add network resource generation * chore(ci): updates paths for website checks * docs: add plugin data source generation * docs: add import cmd for network resource * docs: add plugin resource generation * feat: outlines remaining resources with example and import cmd * feat: add descriptions to docs * chore: add DevSkim ignores and fix capitalized errors * docs: complete ds registry image * docs: add container resource generation * docs: add lables description to missing resources * docs: remove computed:true from network data so the list is rendered in the description * Revert "docs: remove computed:true from network data" This reverts commit dce9b7a5a23dd8b4156bf6e33947225b5f719df2. * docs: add docker image descriptions to generate the docs * docs: add docker registry image descriptions to generate the docs * docs: add docker service descriptions to generate the docs * docs: add docker volume descriptions to generate the docs * docs(index): clarifies description so more docker resources are mentioned * docs(network): fixes required and read-only attributes so the ds can only be read by-name * docs(plugin): clarifies the ds docs attributes * docs: fix typo registry image ds * docs(config): clarifies attributes and enhances examples Provide a long example and import command * fix(config): make data non-sensitive Because only secrets data is * docs(containter): clarifies attributes and enhances examples with import * docs(config): fix typo * docs(image): clarifies attributes and remove import * docs(network): clarifies attributes and adapts import * docs(plugin): clarifies attributes and import * docs(registry_image): clarifies attributes and removes import * chore(secret): remove typo * docs(service): clarifies attributes and import * docs(volume): clarifies attributes and import * fix: correct md linter rules after doc gen * docs(volume): regenerated * docs: add config custom template * docs: add templates for all resources * docs(config): templates all sections and examples for better redability and structure * docs(config): fix md linter * docs(container): templates all sections and examples * docs(image): templates all sections and examples * docs(image): fix import resource by renaming * docs(network): templates all sections and examples * docs(service): templates all sections and examples * docs(volume): templates all sections and examples * fix(lint): replace website with doc directory * fix(ci): link check file extension check * fix: markdown links * chore: remove old website folder * chore: fix website-lint terrafmr dir and pattern * fix: lint fix target website folder * fix: website links * docs(provider): update examples with templates on auth and certs * docs(provider): add tf-plugin-docs line * docs(contributing): split doc generation section * docs: final brush up for readability and structure * chore(ci): add website-generation job to see if files changed and it should run locally again * chore(ci): remove explicit docker setup from website lint because it's installed by default
2021-05-21 08:30:56 -04:00
Description: "Reads the image metadata from a Docker Registry. Used in conjunction with the [docker_image](../resources/image.md) resource to keep an image up to date on the latest available version of the tag.",
ReadContext: dataSourceDockerRegistryImageRead,
Schema: map[string]*schema.Schema{
"name": {
feat/doc generation (#193) * chore: add tfplugindocs tool * feat: add tfplugin doc dependency and make target * chore: apply documentation generation * docs(contributing): update for documentation generation * fix: adapt website-lint target to new do folder * docs(network): update ds descriptions * docs: add template for index.md * docs: add network resource generation * chore(ci): updates paths for website checks * docs: add plugin data source generation * docs: add import cmd for network resource * docs: add plugin resource generation * feat: outlines remaining resources with example and import cmd * feat: add descriptions to docs * chore: add DevSkim ignores and fix capitalized errors * docs: complete ds registry image * docs: add container resource generation * docs: add lables description to missing resources * docs: remove computed:true from network data so the list is rendered in the description * Revert "docs: remove computed:true from network data" This reverts commit dce9b7a5a23dd8b4156bf6e33947225b5f719df2. * docs: add docker image descriptions to generate the docs * docs: add docker registry image descriptions to generate the docs * docs: add docker service descriptions to generate the docs * docs: add docker volume descriptions to generate the docs * docs(index): clarifies description so more docker resources are mentioned * docs(network): fixes required and read-only attributes so the ds can only be read by-name * docs(plugin): clarifies the ds docs attributes * docs: fix typo registry image ds * docs(config): clarifies attributes and enhances examples Provide a long example and import command * fix(config): make data non-sensitive Because only secrets data is * docs(containter): clarifies attributes and enhances examples with import * docs(config): fix typo * docs(image): clarifies attributes and remove import * docs(network): clarifies attributes and adapts import * docs(plugin): clarifies attributes and import * docs(registry_image): clarifies attributes and removes import * chore(secret): remove typo * docs(service): clarifies attributes and import * docs(volume): clarifies attributes and import * fix: correct md linter rules after doc gen * docs(volume): regenerated * docs: add config custom template * docs: add templates for all resources * docs(config): templates all sections and examples for better redability and structure * docs(config): fix md linter * docs(container): templates all sections and examples * docs(image): templates all sections and examples * docs(image): fix import resource by renaming * docs(network): templates all sections and examples * docs(service): templates all sections and examples * docs(volume): templates all sections and examples * fix(lint): replace website with doc directory * fix(ci): link check file extension check * fix: markdown links * chore: remove old website folder * chore: fix website-lint terrafmr dir and pattern * fix: lint fix target website folder * fix: website links * docs(provider): update examples with templates on auth and certs * docs(provider): add tf-plugin-docs line * docs(contributing): split doc generation section * docs: final brush up for readability and structure * chore(ci): add website-generation job to see if files changed and it should run locally again * chore(ci): remove explicit docker setup from website lint because it's installed by default
2021-05-21 08:30:56 -04:00
Type: schema.TypeString,
Description: "The name of the Docker image, including any tags. e.g. `alpine:latest`",
Required: true,
},
"sha256_digest": {
feat/doc generation (#193) * chore: add tfplugindocs tool * feat: add tfplugin doc dependency and make target * chore: apply documentation generation * docs(contributing): update for documentation generation * fix: adapt website-lint target to new do folder * docs(network): update ds descriptions * docs: add template for index.md * docs: add network resource generation * chore(ci): updates paths for website checks * docs: add plugin data source generation * docs: add import cmd for network resource * docs: add plugin resource generation * feat: outlines remaining resources with example and import cmd * feat: add descriptions to docs * chore: add DevSkim ignores and fix capitalized errors * docs: complete ds registry image * docs: add container resource generation * docs: add lables description to missing resources * docs: remove computed:true from network data so the list is rendered in the description * Revert "docs: remove computed:true from network data" This reverts commit dce9b7a5a23dd8b4156bf6e33947225b5f719df2. * docs: add docker image descriptions to generate the docs * docs: add docker registry image descriptions to generate the docs * docs: add docker service descriptions to generate the docs * docs: add docker volume descriptions to generate the docs * docs(index): clarifies description so more docker resources are mentioned * docs(network): fixes required and read-only attributes so the ds can only be read by-name * docs(plugin): clarifies the ds docs attributes * docs: fix typo registry image ds * docs(config): clarifies attributes and enhances examples Provide a long example and import command * fix(config): make data non-sensitive Because only secrets data is * docs(containter): clarifies attributes and enhances examples with import * docs(config): fix typo * docs(image): clarifies attributes and remove import * docs(network): clarifies attributes and adapts import * docs(plugin): clarifies attributes and import * docs(registry_image): clarifies attributes and removes import * chore(secret): remove typo * docs(service): clarifies attributes and import * docs(volume): clarifies attributes and import * fix: correct md linter rules after doc gen * docs(volume): regenerated * docs: add config custom template * docs: add templates for all resources * docs(config): templates all sections and examples for better redability and structure * docs(config): fix md linter * docs(container): templates all sections and examples * docs(image): templates all sections and examples * docs(image): fix import resource by renaming * docs(network): templates all sections and examples * docs(service): templates all sections and examples * docs(volume): templates all sections and examples * fix(lint): replace website with doc directory * fix(ci): link check file extension check * fix: markdown links * chore: remove old website folder * chore: fix website-lint terrafmr dir and pattern * fix: lint fix target website folder * fix: website links * docs(provider): update examples with templates on auth and certs * docs(provider): add tf-plugin-docs line * docs(contributing): split doc generation section * docs: final brush up for readability and structure * chore(ci): add website-generation job to see if files changed and it should run locally again * chore(ci): remove explicit docker setup from website lint because it's installed by default
2021-05-21 08:30:56 -04:00
Type: schema.TypeString,
Description: "The content digest of the image, as stored in the registry.",
Computed: true,
},
chore/refactor tests (#201) * chore: format test configs for datasources * chore: outlines load test config helper and structure * docs(contributing): add command for resouce tests to have an example of the regex * refactor: move container test configs into separate files * fix: add insecure_skip_verify for image pulls to fix the local test setup with invalid certs * chore(ci): remove insecure registry adaption * chore: regenerate website * chore: update gitignore for scipts/testing dir * fix: replace nodejs services with go versions * fix: move testing program versions in separate files * test: reactivate flaky test from travis * chore: fix linter on all go files * fix(linter): testing go servers * chore(ci): add env for go version * chore(ci): name workflow steps also moves description of available docker versions in to acc dockerfile * Revert "test: reactivate flaky test from travis" This reverts commit b02654acc4d6b7d02c8f3ba090e6a3f248741b10. * docs: fix provider-ssh example * chore: use alpine als final image for tests * refactor: move test configs from folder into testname.tf files * refactor: image delete log is now debug and indented * refactor: image test config into seprate files * refactor: move network test config into seperate files * refactor: move plugin test config into seperate files * chore: rename registry image test file * refactor: move registry_image test config into seperate files * chore: format secret test configs * refactor: inline volume test configs * fix: remove unused volume label test function * refactor: move service test configs into seperate files * test: reactivate and fix service test * chore: simplify insecure skip verify add to http client * chore(ci): debug into service test * chore(ci): add testacc setup * chore: format tf config for provider test * chore(ci): add debug output for config.json * fix: check service auth for emptyness * fix: remove re-read of provider auth config because the bug occured only in CI as the meta object might be GCd * test: pass auth to service instead of provider * chore: reactivate all acc tests * test: outlines service inspect json check for full spec * test: add service inspect json checks * test: finish service inspect json checks * chore(service): move test helper to end to of the file * chore: move mapEquals to test helpers * test: add json inspect for config * chore: add debug inspect log for plugin, secret and volume * test: add json inspect for secret * test: add json inspect for image * test: add json inspect for network * test: add json inspect for plugin * test: add json inspect for volume * test: inline ds plugin test configs * test: inline network configs * test: move ds reg image configs into separate files * test: reactivates container upload checks * chore: adapt issues ref from old to new xw repo * fix: reactivate network ingress test and provide helpers for removing the default ingress network and leaving the swamr * docs: rerun website gen * test: fix reg image build and keep test * chore: add name to todo * chore: move ds network and plugin specs to file * chore: format provider test spec * chore: use simpler error message for empty strings
2021-05-31 03:11:49 -04:00
"insecure_skip_verify": {
Type: schema.TypeBool,
Description: "If `true`, the verification of TLS certificates of the server/registry is disabled. Defaults to `false`",
Optional: true,
Default: false,
},
},
}
}
func dataSourceDockerRegistryImageRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
pullOpts := parseImageOptions(d.Get("name").(string))
authConfig, err := getAuthConfigForRegistry(pullOpts.Registry, meta.(*ProviderConfig))
if err != nil {
// The user did not provide a credential for this registry.
// But there are many registries where you can pull without a credential.
// We are setting default values for the authConfig here.
authConfig.Username = ""
authConfig.Password = ""
authConfig.ServerAddress = "https://" + pullOpts.Registry
}
chore/refactor tests (#201) * chore: format test configs for datasources * chore: outlines load test config helper and structure * docs(contributing): add command for resouce tests to have an example of the regex * refactor: move container test configs into separate files * fix: add insecure_skip_verify for image pulls to fix the local test setup with invalid certs * chore(ci): remove insecure registry adaption * chore: regenerate website * chore: update gitignore for scipts/testing dir * fix: replace nodejs services with go versions * fix: move testing program versions in separate files * test: reactivate flaky test from travis * chore: fix linter on all go files * fix(linter): testing go servers * chore(ci): add env for go version * chore(ci): name workflow steps also moves description of available docker versions in to acc dockerfile * Revert "test: reactivate flaky test from travis" This reverts commit b02654acc4d6b7d02c8f3ba090e6a3f248741b10. * docs: fix provider-ssh example * chore: use alpine als final image for tests * refactor: move test configs from folder into testname.tf files * refactor: image delete log is now debug and indented * refactor: image test config into seprate files * refactor: move network test config into seperate files * refactor: move plugin test config into seperate files * chore: rename registry image test file * refactor: move registry_image test config into seperate files * chore: format secret test configs * refactor: inline volume test configs * fix: remove unused volume label test function * refactor: move service test configs into seperate files * test: reactivate and fix service test * chore: simplify insecure skip verify add to http client * chore(ci): debug into service test * chore(ci): add testacc setup * chore: format tf config for provider test * chore(ci): add debug output for config.json * fix: check service auth for emptyness * fix: remove re-read of provider auth config because the bug occured only in CI as the meta object might be GCd * test: pass auth to service instead of provider * chore: reactivate all acc tests * test: outlines service inspect json check for full spec * test: add service inspect json checks * test: finish service inspect json checks * chore(service): move test helper to end to of the file * chore: move mapEquals to test helpers * test: add json inspect for config * chore: add debug inspect log for plugin, secret and volume * test: add json inspect for secret * test: add json inspect for image * test: add json inspect for network * test: add json inspect for plugin * test: add json inspect for volume * test: inline ds plugin test configs * test: inline network configs * test: move ds reg image configs into separate files * test: reactivates container upload checks * chore: adapt issues ref from old to new xw repo * fix: reactivate network ingress test and provide helpers for removing the default ingress network and leaving the swamr * docs: rerun website gen * test: fix reg image build and keep test * chore: add name to todo * chore: move ds network and plugin specs to file * chore: format provider test spec * chore: use simpler error message for empty strings
2021-05-31 03:11:49 -04:00
insecureSkipVerify := d.Get("insecure_skip_verify").(bool)
digest, err := getImageDigest(pullOpts.Registry, authConfig.ServerAddress, pullOpts.Repository, pullOpts.Tag, authConfig.Username, authConfig.Password, insecureSkipVerify, false)
if err != nil {
digest, err = getImageDigest(pullOpts.Registry, authConfig.ServerAddress, pullOpts.Repository, pullOpts.Tag, authConfig.Username, authConfig.Password, insecureSkipVerify, true)
if err != nil {
return diag.Errorf("Got error when attempting to fetch image version %s:%s from registry: %s", pullOpts.Repository, pullOpts.Tag, err)
}
}
d.SetId(digest)
d.Set("sha256_digest", digest)
return nil
}
func getImageDigest(registry string, registryWithProtocol string, image, tag, username, password string, insecureSkipVerify, fallback bool) (string, error) {
client := buildHttpClientForRegistry(registryWithProtocol, insecureSkipVerify)
req, err := http.NewRequest("GET", registryWithProtocol+"/v2/"+image+"/manifests/"+tag, nil)
if err != nil {
return "", fmt.Errorf("Error creating registry request: %s", err)
}
if username != "" {
if registry != "ghcr.io" && !isECRRepositoryURL(registry) && registry != "gcr.io" {
req.SetBasicAuth(username, password)
} else {
if isECRRepositoryURL(registry) {
password = normalizeECRPasswordForHTTPUsage(password)
req.Header.Add("Authorization", "Basic "+password)
} else {
req.Header.Add("Authorization", "Bearer "+b64.StdEncoding.EncodeToString([]byte(password)))
}
}
}
setupHTTPHeadersForRegistryRequests(req, fallback)
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("Error during registry request: %s", err)
}
switch resp.StatusCode {
// Basic auth was valid or not needed
case http.StatusOK:
return getDigestFromResponse(resp)
// Either OAuth is required or the basic auth creds were invalid
case http.StatusUnauthorized:
if strings.HasPrefix(resp.Header.Get("www-authenticate"), "Bearer") {
auth := parseAuthHeader(resp.Header.Get("www-authenticate"))
params := url.Values{}
params.Set("service", auth["service"])
params.Set("scope", auth["scope"])
tokenRequest, err := http.NewRequest("GET", auth["realm"]+"?"+params.Encode(), nil)
if err != nil {
return "", fmt.Errorf("Error creating registry request: %s", err)
}
if username != "" {
tokenRequest.SetBasicAuth(username, password)
}
tokenResponse, err := client.Do(tokenRequest)
if err != nil {
return "", fmt.Errorf("Error during registry request: %s", err)
}
if tokenResponse.StatusCode != http.StatusOK {
return "", fmt.Errorf("Got bad response from registry: " + tokenResponse.Status)
}
body, err := ioutil.ReadAll(tokenResponse.Body)
if err != nil {
return "", fmt.Errorf("Error reading response body: %s", err)
}
token := &TokenResponse{}
err = json.Unmarshal(body, token)
if err != nil {
return "", fmt.Errorf("Error parsing OAuth token response: %s", err)
}
req.Header.Set("Authorization", "Bearer "+token.Token)
digestResponse, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("Error during registry request: %s", err)
}
if digestResponse.StatusCode != http.StatusOK {
return "", fmt.Errorf("Got bad response from registry: " + digestResponse.Status)
}
return getDigestFromResponse(digestResponse)
}
return "", fmt.Errorf("Bad credentials: " + resp.Status)
// Some unexpected status was given, return an error
default:
return "", fmt.Errorf("Got bad response from registry: " + resp.Status)
}
}
type TokenResponse struct {
Token string
}
// Parses key/value pairs from a WWW-Authenticate header
func parseAuthHeader(header string) map[string]string {
parts := strings.SplitN(header, " ", 2)
parts = strings.Split(parts[1], ",")
opts := make(map[string]string)
for _, part := range parts {
vals := strings.SplitN(part, "=", 2)
key := vals[0]
val := strings.Trim(vals[1], "\", ")
opts[key] = val
}
return opts
}
func getDigestFromResponse(response *http.Response) (string, error) {
header := response.Header.Get("Docker-Content-Digest")
if header == "" {
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return "", fmt.Errorf("Error reading registry response body: %s", err)
}
return fmt.Sprintf("sha256:%x", sha256.Sum256(body)), nil
}
return header, nil
}