Skip to content

Commit

Permalink
helm: support private helm repositories (#370)
Browse files Browse the repository at this point in the history
Previously holos unconditionally executed helm repo add which failed for
private repositories requiring basic authentication.

This patch addresses the problem by using the Helm SDK to pull and cache
charts without adding them as repositories.  New fields for the
core.Helm type allow basic auth credentials to be read from environment
variables.

Multiple repositories are supported by using different env vars for
different repositories.
  • Loading branch information
jeffmccune committed Dec 6, 2024
1 parent 4529673 commit f71d6d5
Show file tree
Hide file tree
Showing 14 changed files with 699 additions and 235 deletions.
2 changes: 2 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"certificaterequest",
"certificaterequests",
"certificatesigningrequests",
"chartmuseum",
"clientset",
"clsx",
"clusterexternalsecret",
Expand Down Expand Up @@ -188,6 +189,7 @@
"mutatingwebhookconfigurations",
"mvdan",
"mxcl",
"mychart",
"myhostname",
"myRegistrKeySecretName",
"mysecret",
Expand Down
17 changes: 17 additions & 0 deletions api/core/v1alpha5/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,26 @@ type Chart struct {
}

// Repository represents a [Helm] [Chart] repository.
//
// The Auth field is useful to configure http basic authentication to the Helm
// repository. Holos gets the username and password from the environment
// variables represented by the Auth field.
type Repository struct {
Name string `json:"name" yaml:"name"`
URL string `json:"url" yaml:"url"`
Auth Auth `json:"auth,omitempty" yaml:"auth,omitempty"`
}

// Auth represents environment variable names containing auth credentials.
type Auth struct {
Username AuthSource `json:"username" yaml:"username"`
Password AuthSource `json:"password" yaml:"password"`
}

// AuthSource represents a source for the value of an [Auth] field.
type AuthSource struct {
Value string `json:"value,omitempty" yaml:"value,omitempty"`
FromEnv string `json:"fromEnv,omitempty" yaml:"fromEnv,omitempty"`
}

// Transformer combines multiple inputs from prior [Generator] or [Transformer]
Expand Down
38 changes: 0 additions & 38 deletions cmd/holos/tests/v1alpha5/issues/helm-pull-errors.txt

This file was deleted.

29 changes: 29 additions & 0 deletions doc/md/api/core.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Package core contains schemas for a [Platform](<#Platform>) and [BuildPlan](<#Bu
## Index

- [type Artifact](<#Artifact>)
- [type Auth](<#Auth>)
- [type AuthSource](<#AuthSource>)
- [type BuildPlan](<#BuildPlan>)
- [type BuildPlanSpec](<#BuildPlanSpec>)
- [type Chart](<#Chart>)
Expand Down Expand Up @@ -65,6 +67,30 @@ type Artifact struct {
}
```

<a name="Auth"></a>
## type Auth {#Auth}

Auth represents environment variable names containing auth credentials.

```go
type Auth struct {
Username AuthSource `json:"username" yaml:"username"`
Password AuthSource `json:"password" yaml:"password"`
}
```

<a name="AuthSource"></a>
## type AuthSource {#AuthSource}

AuthSource represents a source for the value of an [Auth](<#Auth>) field.

```go
type AuthSource struct {
Value string `json:"value,omitempty" yaml:"value,omitempty"`
FromEnv string `json:"fromEnv,omitempty" yaml:"fromEnv,omitempty"`
}
```

<a name="BuildPlan"></a>
## type BuildPlan {#BuildPlan}

Expand Down Expand Up @@ -365,10 +391,13 @@ type PlatformSpec struct {

Repository represents a [Helm](<#Helm>) [Chart](<#Chart>) repository.

The Auth field is useful to configure http basic authentication to the Helm repository. Holos gets the username and password from the environment variables represented by the Auth field.

```go
type Repository struct {
Name string `json:"name" yaml:"name"`
URL string `json:"url" yaml:"url"`
Auth Auth `json:"auth,omitempty" yaml:"auth,omitempty"`
}
```

Expand Down
175 changes: 175 additions & 0 deletions doc/md/topics/private-helm.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
---
description: Private Helm Repositories
slug: private-helm
sidebar_position: 999
---

# Private Helm

## Configuration

Holos uses the Helm SDK and defers to it for authentication to private
repositories. Each Helm Generator supports providing http basic authentication
credentials from environment variables.

For example, the following BuildPlan causes `holos` to get the admin username
password from the `HOLOS_TEST_PASS` environment variable.

```bash
mkdir -p projects/holos/components/private-chart
cat <<EOF > projects/holos/components/private-chart/private-chart.cue
```
```cue showLineNumbers
package holos
holos: Component.BuildPlan
// Test holos can access a private repository with basic auth.
// https://github.com/holos-run/holos/issues/370
Component: #Helm & {
Chart: {
name: "mychart"
version: "0.1.0"
repository: {
name: "holos-test"
url: "https://charts.holos.localhost"
// auth: username: fromEnv: "HOLOS_TEST_USER"
auth: username: value: "admin"
auth: password: fromEnv: "HOLOS_TEST_PASS"
}
}
}
```
```bash
EOF
```

## Verification

Verify `holos` can access a private Helm repository by setting [ChartMuseum] up
on a [Local Cluster]. We'll use https with basic auth to authenticate to the
chart repository.

Using the [bank of holos] repository, deploy chart museum:

```bash
holos render platform -t ChartMuseum
```

Apply the manifests:

```bash
kubectl apply --server-side=true -f deploy/clusters/workload/projects/holos/components/chart-museum
kubectl apply --server-side=true -f deploy/clusters/workload/projects/network/components/httproutes
```

Get the admin password:

```bash
kubectl get secret -n holos chartmuseum-auth -o json \
| jq --exit-status -r '.data.password | @base64d'
```

Add a local repo:

```bash
helm repo add holos-test https://charts.holos.localhost --username admin
```
```txt
Password:
"holos-test" has been added to your repositories
```

:::note
Helm by default stores this password in `~/Library/Preferences/helm/repositories.yaml`
:::

Create a chart:

```bash
helm create mychart
```
```txt
Creating mychart
```

Package it up.

```bash
helm package mychart
```
```txt
Successfully packaged chart and saved it to: mychart-0.1.0.tgz
```

Publish it.

```bash
curl --user "admin:$(pbpaste)" --data-binary "@mychart-0.1.0.tgz" https://charts.holos.localhost/api/charts
```
```json
{"saved":true}
```

Remove all cached charts:

```bash
find . -name vendor | xargs rm -rf
```

Render the chart:

```bash
cat <<EOF > test-private-repo.cue
```
```cue showLineNumbers
@if(TestPrivateRepo)
package holos
// Test holos can access a private repository with basic auth.
// https://github.com/holos-run/holos/issues/370
Projects: holos: #ProjectBuilder & {
team: "holos-authors"
namespaces: holos: _
_components: "private-chart": _
}
```
```bash
EOF
```

```
time holos render platform -t TestPrivateRepo
```

Check the chart was pulled and cached:

```shell
tree ./projects/holos/components/private-chart/vendor
```
```txt
./projects/holos/components/private-chart/vendor
└── 0.1.0
├── mychart
│   ├── Chart.yaml
│   ├── mychart-0.1.0.tgz
│   ├── templates
│   │   ├── NOTES.txt
│   │   ├── _helpers.tpl
│   │   ├── deployment.yaml
│   │   ├── hpa.yaml
│   │   ├── ingress.yaml
│   │   ├── service.yaml
│   │   ├── serviceaccount.yaml
│   │   └── tests
│   │   └── test-connection.yaml
│   └── values.yaml
└── mychart-0.1.0.tgz
6 directories, 11 files
```

[Local Cluster]: ./local-cluster.mdx
[ChartMuseum]: https://chartmuseum.com/docs/
[bank of holos]: https://github.com/holos-run/bank-of-holos
Loading

0 comments on commit f71d6d5

Please sign in to comment.