Compare commits

..

4 Commits

Author SHA1 Message Date
Jeff McCune
6090ab224e (#14) Validate secrets fetched from provisioner cluster
This patch validates secrets are synced from the provisioner cluster to
a workload cluster.  This verifies the eso-creds-refresher job, external
secrets operator, etc...

Refer to
0ae58858f5
for the corresponding commit on the k2 cluster.
2024-02-27 15:55:17 -08:00
Jeff McCune
10e140258d (#15) Report multiple cue errors
This patch prints out the cue file and line numbers when a cue error
contains multiple go errors to unwrap.

For example:

```
❯ holos render --cluster-name=k2 ~/workspace/holos-run/holos/docs/examples/platforms/reference/clusters/workload/...
3:31PM ERR could not execute version=0.46.0 err="could not decode: content: error in call to encoding/yaml.MarshalStream: incomplete value string (and 1 more errors)" loc=builder.go:212
content: error in call to encoding/yaml.MarshalStream: incomplete value string:
    /home/jeff/workspace/holos-run/holos/docs/examples/schema.cue:199:11
    /home/jeff/workspace/holos-run/holos/docs/examples/cue.mod/gen/external-secrets.io/externalsecret/v1beta1/types_gen.cue:83:14
```
2024-02-27 15:32:11 -08:00
Jeff McCune
40ac705f0d (#16) Add create secret --append-hash=false
So we can easily create secrets for use with ExternalSecret resources.
2024-02-27 12:04:00 -08:00
Jeff McCune
b4ad6425e5 (#14) Validate SecretStore works
This patch validates a SecretStore in the holos-system namespace works
after provisioner credentials are refreshed.
2024-02-27 11:25:00 -08:00
16 changed files with 112 additions and 60 deletions

View File

@@ -0,0 +1,15 @@
# Want cue errors to show files and lines
! exec holos build .
stderr 'could not decode: content: cannot convert non-concrete value string'
stderr '/component.cue:6:1$'
-- cue.mod --
package holos
-- component.cue --
package holos
apiVersion: "holos.run/v1alpha1"
kind: "KubernetesObjects"
cluster: string @tag(cluster, string)
content: foo
foo: string

View File

@@ -922,7 +922,7 @@ import (
kubernetes?: {
// Auth configures how secret-manager authenticates with a
// Kubernetes instance.
auth: struct.MaxFields(1) & {
auth: {
// has both clientCert and clientKey as secretKeySelector
cert?: {
// A reference to a specific 'key' within a Secret resource,

View File

@@ -15,6 +15,10 @@ objects: #CredsRefresherService.objects
#TargetNamespace: #CredsRefresher.namespace
#Kustomization: spec: {
dependsOn: [{name: #InstancePrefix + "-namespaces"}]
}
let NAME = #CredsRefresher.name
let AUD = "//iam.googleapis.com/projects/\(#InputKeys.gcpProjectNumber)/locations/global/workloadIdentityPools/holos/providers/k8s-\(#InputKeys.cluster)"
let MOUNT = "/var/run/service-account"

View File

@@ -1,5 +1,16 @@
package holos
// Manages the External Secrets Operator from the official upstream Helm chart.
#TargetNamespace: "external-secrets"
#InputKeys: component: "eso"
#InputKeys: {
project: "secrets"
service: "eso"
}
#Kustomization: spec: {
dependsOn: [{name: #InstancePrefix + "-namespaces"}]
targetNamespace: #TargetNamespace

View File

@@ -1,10 +0,0 @@
package holos
#TargetNamespace: "external-secrets"
#InputKeys: component: "eso"
#InputKeys: {
project: "secrets"
service: "eso"
}

View File

@@ -0,0 +1,19 @@
package holos
// Validate ESO by syncing a secret with a SecretStore.
#TargetNamespace: "holos-system"
#InputKeys: {
project: "secrets"
component: "validate"
}
#Kustomization: spec: dependsOn: [{name: #InstancePrefix + "-eso"}]
objects: [
#SecretStore,
#ExternalSecret & { _name: "validate" },
]
{} & #KubernetesObjects

View File

@@ -1,13 +0,0 @@
package holos
#Kustomization: spec: dependsOn: [{name: #InstancePrefix + "-eso"}]
objects: [
#SecretStore,
#ExternalSecret & {
_name: "validate"
spec: dataFrom: [{extract: key: "ns/" + #TargetNamespace + "/test"}]
},
]
{} & #KubernetesObjects

View File

@@ -1,8 +0,0 @@
package holos
#TargetNamespace: "default"
#InputKeys: {
project: "secrets"
component: "validate"
}

View File

@@ -79,10 +79,10 @@ _apiVersion: "holos.run/v1alpha1"
kind: string | *"GitRepository"
name: string | *"flux-system"
}
suspend?: bool
suspend?: bool
targetNamespace?: string
timeout: string | *"3m0s"
wait: bool | *true
timeout: string | *"3m0s"
wait: bool | *true
}
}
@@ -90,7 +90,7 @@ _apiVersion: "holos.run/v1alpha1"
#ExternalSecret: #NamespaceObject & es.#ExternalSecret & {
_name: string
metadata: {
namespace: string | *"default"
namespace: #TargetNamespace
name: _name
}
spec: {
@@ -102,24 +102,29 @@ _apiVersion: "holos.run/v1alpha1"
target: {
creationPolicy: string | *"Owner"
}
data: [{
remoteRef: key: _name
secretKey: _name
}]
}
}
#SecretStore: #NamespaceObject & ss.#SecretStore & {
metadata: {
name: string | *"default"
namespace: string | *#TargetNamespace
namespace: #TargetNamespace
}
spec: provider: {
vault: {
auth: kubernetes: {
mountPath: #InputKeys.cluster
role: string | *"default"
serviceAccountRef: name: string | *"default"
kubernetes: {
remoteNamespace: #TargetNamespace
auth: token: bearerToken: {
name: string | *"eso-reader"
key: string | *"token"
}
server: {
caBundle: #InputKeys.provisionerCABundle
url: #InputKeys.provisionerURL
}
path: string | *"kv/k8s"
server: "https://vault.core." + #Platform.org.domain
version: string | *"v2"
}
}
}
@@ -140,6 +145,11 @@ _apiVersion: "holos.run/v1alpha1"
// GCP Project Info used for the Provisioner Cluster
gcpProjectID: string @tag(gcpProjectID, type=string)
gcpProjectNumber: int @tag(gcpProjectNumber, type=int)
// Same as cluster certificate-authority-data field in ~/.holos/kubeconfig.provisioner
provisionerCABundle: string @tag(provisionerCABundle, type=string)
// Same as the cluster server field in ~/.holos/kubeconfig.provisioner
provisionerURL: string @tag(provisionerURL, type=string)
}
// #Platform defines the primary lookup table for the platform. Lookup keys should be limited to those defined in #KeyTags.

View File

@@ -2,7 +2,8 @@ package cli
import (
"context"
"errors"
"cuelang.org/go/cue/errors"
"fmt"
"github.com/holos-run/holos/pkg/holos"
"github.com/holos-run/holos/pkg/wrapper"
"log/slog"
@@ -24,12 +25,18 @@ func MakeMain(options ...holos.Option) func() int {
// handleError is the top level error handler that unwraps and logs errors.
func handleError(ctx context.Context, err error, hc *holos.Config) (exitCode int) {
log := hc.NewTopLevelLogger()
var cueErr errors.Error
var errAt *wrapper.ErrorAt
const msg = "could not execute"
if ok := errors.As(err, &errAt); ok {
if errors.As(err, &errAt) {
log.ErrorContext(ctx, msg, "err", errAt.Unwrap(), "loc", errAt.Source.Loc())
} else {
log.ErrorContext(ctx, msg, "err", err)
}
// cue errors are bundled up as a list and refer to multiple files / lines.
if errors.As(err, &cueErr) {
msg := errors.Details(cueErr, nil)
_, _ = fmt.Fprint(hc.Stderr(), msg)
}
return 1
}

View File

@@ -26,6 +26,7 @@ func NewCreateCmd(hc *holos.Config) *cobra.Command {
cfg, flagSet := newConfig()
flagSet.Var(&cfg.files, "from-file", "store files as keys in the secret")
cfg.dryRun = flagSet.Bool("dry-run", false, "dry run")
cfg.appendHash = flagSet.Bool("append-hash", true, "append hash to kubernetes secret name")
cmd.Flags().SortFlags = false
cmd.Flags().AddGoFlagSet(flagSet)
@@ -72,10 +73,12 @@ func makeCreateRunFunc(hc *holos.Config, cfg *config) command.RunFunc {
secret.Labels[ClusterLabel] = *cfg.cluster
}
if secretHash, err := hash.SecretHash(secret); err != nil {
return wrapper.Wrap(err)
} else {
secret.Name = fmt.Sprintf("%s-%s", secret.Name, secretHash)
if *cfg.appendHash {
if secretHash, err := hash.SecretHash(secret); err != nil {
return wrapper.Wrap(err)
} else {
secret.Name = fmt.Sprintf("%s-%s", secret.Name, secretHash)
}
}
if *cfg.dryRun {

View File

@@ -12,13 +12,14 @@ const ClusterLabel = "holos.run/cluster.name"
type secretData map[string][]byte
type config struct {
files holos.StringSlice
printFile *string
extract *bool
dryRun *bool
cluster *string
namespace *string
extractTo *string
files holos.StringSlice
printFile *string
extract *bool
dryRun *bool
appendHash *bool
cluster *string
namespace *string
extractTo *string
}
func newConfig() (*config, *flag.FlagSet) {

View File

@@ -0,0 +1,7 @@
# Want no hash appended
holos create secret test --namespace holos-system --from-file $WORK/test --append-hash=false
stderr ' created: test '
stderr ' secret=test '
-- test --
sekret

View File

@@ -0,0 +1,6 @@
# Want no hash appended
holos create secret test --namespace holos-system --from-file $WORK/test --append-hash=false --dry-run
stdout 'name: test$'
-- test --
sekret

View File

@@ -1 +1 @@
45
46

View File

@@ -1 +1 @@
3
2