Skip to content

Commit

Permalink
Add OCI registry block to provider configuration (#862)
Browse files Browse the repository at this point in the history
Co-authored-by: BBBmau <alvarez.mauriciotm@gmail.com>
Co-authored-by: John Houston <jhouston@hashicorp.com>
  • Loading branch information
3 people authored Dec 13, 2022
1 parent 74b48cb commit ccba08b
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 31 deletions.
3 changes: 3 additions & 0 deletions .changelog/862.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
Add support for configuring OCI registries inside provider block
```
2 changes: 1 addition & 1 deletion helm/data_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ func dataTemplateRead(ctx context.Context, d *schema.ResourceData, meta interfac
if err != nil {
return diag.FromErr(err)
}
err = OCIRegistryLogin(actionConfig, d)
err = OCIRegistryLogin(actionConfig, d, m)
if err != nil {
return diag.FromErr(err)
}
Expand Down
99 changes: 72 additions & 27 deletions helm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ import (

// Meta is the meta information structure for the provider
type Meta struct {
data *schema.ResourceData
Settings *cli.EnvSettings
HelmDriver string
data *schema.ResourceData
Settings *cli.EnvSettings
RegistryClient *registry.Client
HelmDriver string

// Used to lock some operations
sync.Mutex
Expand Down Expand Up @@ -107,6 +108,12 @@ func Provider() *schema.Provider {
Description: "Kubernetes configuration.",
Elem: kubernetesResource(),
},
"registry": {
Type: schema.TypeList,
Optional: true,
Description: "RegistryClient configuration.",
Elem: registryResource(),
},
"experiments": {
Type: schema.TypeList,
MaxItems: 1,
Expand Down Expand Up @@ -146,6 +153,28 @@ func Provider() *schema.Provider {
return p
}

func registryResource() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"url": {
Type: schema.TypeString,
Required: true,
Description: "OCI URL in form of oci://host:port or oci://host",
},
"username": {
Type: schema.TypeString,
Required: true,
Description: "The username to use for HTTP basic authentication when accessing the Kubernetes master endpoint.",
},
"password": {
Type: schema.TypeString,
Required: true,
Description: "The password to use for HTTP basic authentication when accessing the Kubernetes master endpoint.",
},
},
}
}

func kubernetesResource() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
Expand Down Expand Up @@ -311,6 +340,18 @@ func providerConfigure(d *schema.ResourceData, terraformVersion string) (interfa
m.HelmDriver = v.(string)
}

if registryClient, err := registry.NewClient(); err == nil {
m.RegistryClient = registryClient
for _, r := range d.Get("registry").([]interface{}) {
if v, ok := r.(map[string]interface{}); ok {
err := OCIRegistryPerformLogin(m.RegistryClient, v["url"].(string), v["username"].(string), v["password"].(string))
if err != nil {
return nil, diag.FromErr(err)
}
}
}
}

return m, nil
}

Expand Down Expand Up @@ -407,15 +448,12 @@ type dataGetter interface {
var loggedInOCIRegistries map[string]string = map[string]string{}
var OCILoginMutex sync.Mutex

// OCIRegistryLogin creates an OCI registry client and logs into the registry if needed
func OCIRegistryLogin(actionConfig *action.Configuration, d dataGetter) error {
registryClient, err := registry.NewClient()
if err != nil {
return fmt.Errorf("could not create OCI registry client: %v", err)
}
// OCIRegistryLogin logs into the registry if needed
func OCIRegistryLogin(actionConfig *action.Configuration, d dataGetter, m *Meta) error {
registryClient := m.RegistryClient
actionConfig.RegistryClient = registryClient

// log in to the registry if neccessary
// log in to the registry if necessary
repository := d.Get("repository").(string)
chartName := d.Get("chart").(string)
var ociURL string
Expand All @@ -431,25 +469,32 @@ func OCIRegistryLogin(actionConfig *action.Configuration, d dataGetter) error {
username := d.Get("repository_username").(string)
password := d.Get("repository_password").(string)
if username != "" && password != "" {
u, err := url.Parse(ociURL)
if err != nil {
return fmt.Errorf("could not parse OCI registry URL: %v", err)
}
return OCIRegistryPerformLogin(registryClient, ociURL, username, password)
}

OCILoginMutex.Lock()
defer OCILoginMutex.Unlock()
if _, ok := loggedInOCIRegistries[u.Host]; ok {
debug("[INFO] Already logged into OCI registry %q", u.Host)
return nil
}
err = registryClient.Login(u.Host,
registry.LoginOptBasicAuth(username, password))
if err != nil {
return fmt.Errorf("could not login to OCI registry %q: %v", u.Host, err)
}
loggedInOCIRegistries[u.Host] = ""
debug("[INFO] Logged into OCI registry")
return nil
}

// OCIRegistryPerformLogin creates an OCI registry client and logs into the registry if needed
func OCIRegistryPerformLogin(registryClient *registry.Client, ociURL string, username string, password string) error {
u, err := url.Parse(ociURL)
if err != nil {
return fmt.Errorf("could not parse OCI registry URL: %v", err)
}

OCILoginMutex.Lock()
defer OCILoginMutex.Unlock()
if _, ok := loggedInOCIRegistries[u.Host]; ok {
debug("[INFO] Already logged into OCI registry %q", u.Host)
return nil
}
err = registryClient.Login(u.Host,
registry.LoginOptBasicAuth(username, password))
if err != nil {
return fmt.Errorf("could not login to OCI registry %q: %v", u.Host, err)
}
loggedInOCIRegistries[u.Host] = ""
debug("[INFO] Logged into OCI registry")

return nil
}
Expand Down
6 changes: 3 additions & 3 deletions helm/resource_release.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ func resourceReleaseCreate(ctx context.Context, d *schema.ResourceData, meta int
if err != nil {
return diag.FromErr(err)
}
err = OCIRegistryLogin(actionConfig, d)
err = OCIRegistryLogin(actionConfig, d, m)
if err != nil {
return diag.FromErr(err)
}
Expand Down Expand Up @@ -622,7 +622,7 @@ func resourceReleaseUpdate(ctx context.Context, d *schema.ResourceData, meta int
d.Partial(true)
return diag.FromErr(err)
}
err = OCIRegistryLogin(actionConfig, d)
err = OCIRegistryLogin(actionConfig, d, m)
if err != nil {
d.Partial(true)
return diag.FromErr(err)
Expand Down Expand Up @@ -759,7 +759,7 @@ func resourceDiff(ctx context.Context, d *schema.ResourceDiff, meta interface{})
if err != nil {
return err
}
err = OCIRegistryLogin(actionConfig, d)
err = OCIRegistryLogin(actionConfig, d, m)
if err != nil {
return err
}
Expand Down
49 changes: 49 additions & 0 deletions helm/resource_release_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,55 @@ func TestAccResourceRelease_OCI_repository(t *testing.T) {
})
}

func TestAccResourceRelease_OCI_registry_login(t *testing.T) {
name := randName("oci")
namespace := createRandomNamespace(t)
defer deleteNamespace(t, namespace)

ociRegistryURL, shutdown := setupOCIRegistry(t, false)
defer shutdown()

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Providers: testAccProviders,
CheckDestroy: testAccCheckHelmReleaseDestroy(namespace),
Steps: []resource.TestStep{
{
Config: testAccHelmReleaseConfig_OCI_login_provider(os.Getenv("KUBE_CONFIG_PATH"), testResourceName, namespace, name, ociRegistryURL, "1.2.3", "hashicorp", "terraform"),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("helm_release.test", "metadata.0.name", name),
resource.TestCheckResourceAttr("helm_release.test", "metadata.0.namespace", namespace),
resource.TestCheckResourceAttr("helm_release.test", "metadata.0.version", "1.2.3"),
resource.TestCheckResourceAttr("helm_release.test", "status", release.StatusDeployed.String()),
),
},
},
})
}

func testAccHelmReleaseConfig_OCI_login_provider(kubeconfig, resource, ns, name, repo, version, username, password string) string {
return fmt.Sprintf(`
provider "helm" {
kubernetes {
config_path = %q
}
registry {
url = %q
username = %q
password = %q
}
}
resource "helm_release" "%s" {
name = "%s"
namespace = %q
version = %q
repository = %[2]q
chart = "test-chart"
}`, kubeconfig, repo, username, password, resource, name, ns, version)
}

func TestAccResourceRelease_OCI_login(t *testing.T) {
name := randName("oci")
namespace := createRandomNamespace(t)
Expand Down
21 changes: 21 additions & 0 deletions website/docs/index.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ provider "helm" {
kubernetes {
config_path = "~/.kube/config"
}
# localhost registry with password protection
registry {
url = "oci://localhost:5000"
username = "username"
password = "password"
}
# private registry
registry {
url = "oci://private.registry"
username = "username"
password = "password"
}
}
resource "helm_release" "nginx_ingress" {
Expand Down Expand Up @@ -137,6 +151,7 @@ The following arguments are supported:
* `helm_driver` - (Optional) "The backend storage driver. Valid values are: `configmap`, `secret`, `memory`, `sql`. Defaults to `secret`.
Note: Regarding the sql driver, as of helm v3.2.0 SQL support exists only for the postgres dialect. The connection string can be configured by setting the `HELM_DRIVER_SQL_CONNECTION_STRING` environment variable e.g. `HELM_DRIVER_SQL_CONNECTION_STRING=postgres://username:password@host/dbname` more info [here](https://pkg.go.dev/github.com/lib/pq).
* `kubernetes` - Kubernetes configuration block.
* `registry` - Private OCI registry configuration block. Can be specified multiple times.

The `kubernetes` block supports:

Expand All @@ -158,6 +173,12 @@ The `kubernetes` block supports:
* `args` - (Optional) List of arguments to pass when executing the plugin.
* `env` - (Optional) Map of environment variables to set when executing the plugin.

The `registry` block has options:

* `url` - (Required) url to the registry in format `oci://host:port`
* `username` - (Required) username to registry
* `password` - (Required) password to registry

## Experiments

The provider takes an `experiments` block that allows you enable experimental features by setting them to `true`.
Expand Down

0 comments on commit ccba08b

Please sign in to comment.