mirror of
https://github.com/holos-run/holos.git
synced 2026-03-13 08:19:05 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
340715f76c | ||
|
|
64ffacfc7a | ||
|
|
54acea42cb | ||
|
|
5ef8e75194 | ||
|
|
cb2b5c0f49 | ||
|
|
fd5a2fdbc1 | ||
|
|
eb3e272612 | ||
|
|
9f2a51bde8 | ||
|
|
2b3b5a4887 | ||
|
|
7426e8f867 | ||
|
|
cf0c455aa2 | ||
|
|
752a3f912d | ||
|
|
7d5852d675 | ||
|
|
66b4ca0e6c | ||
|
|
b3f682453d | ||
|
|
0c3181ae05 |
5
.github/workflows/lint.yaml
vendored
5
.github/workflows/lint.yaml
vendored
@@ -1,6 +1,7 @@
|
||||
---
|
||||
# https://github.com/golangci/golangci-lint-action?tab=readme-ov-file#how-to-use
|
||||
name: Lint
|
||||
on:
|
||||
"on":
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
@@ -22,6 +23,6 @@ jobs:
|
||||
go-version: stable
|
||||
cache: false
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
uses: golangci/golangci-lint-action@v4
|
||||
with:
|
||||
version: latest
|
||||
|
||||
5
.github/workflows/test.yaml
vendored
5
.github/workflows/test.yaml
vendored
@@ -23,5 +23,10 @@ jobs:
|
||||
with:
|
||||
go-version: stable
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@v4.1.0
|
||||
with:
|
||||
version: 'latest'
|
||||
|
||||
- name: Test
|
||||
run: ./scripts/test
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
_#_BuiltinPluginLoadingOptions_name: "BploUndefinedBploUseStaticallyLinkedBploLoadFromFileSys"
|
||||
@@ -0,0 +1,10 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// ConfigMapArgs contains the metadata of how to generate a configmap.
|
||||
#ConfigMapArgs: {
|
||||
#GeneratorArgs
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
// Package types holds the definition of the kustomization struct and
|
||||
// supporting structs. It's the k8s API conformant object that describes
|
||||
// a set of generation and transformation operations to create and/or
|
||||
// modify k8s resources.
|
||||
// A kustomization file is a serialization of this struct.
|
||||
package types
|
||||
@@ -0,0 +1,29 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// FieldSpec completely specifies a kustomizable field in a k8s API object.
|
||||
// It helps define the operands of transformations.
|
||||
//
|
||||
// For example, a directive to add a common label to objects
|
||||
// will need to know that a 'Deployment' object (in API group
|
||||
// 'apps', any version) can have labels at field path
|
||||
// 'spec/template/metadata/labels', and further that it is OK
|
||||
// (or not OK) to add that field path to the object if the
|
||||
// field path doesn't exist already.
|
||||
//
|
||||
// This would look like
|
||||
// {
|
||||
// group: apps
|
||||
// kind: Deployment
|
||||
// path: spec/template/metadata/labels
|
||||
// create: true
|
||||
// }
|
||||
#FieldSpec: {
|
||||
path?: string @go(Path)
|
||||
create?: bool @go(CreateIfNotPresent)
|
||||
}
|
||||
|
||||
#FsSlice: [...#FieldSpec]
|
||||
@@ -0,0 +1,33 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// GenerationBehavior specifies generation behavior of configmaps, secrets and maybe other resources.
|
||||
#GenerationBehavior: int // #enumGenerationBehavior
|
||||
|
||||
#enumGenerationBehavior:
|
||||
#BehaviorUnspecified |
|
||||
#BehaviorCreate |
|
||||
#BehaviorReplace |
|
||||
#BehaviorMerge
|
||||
|
||||
#values_GenerationBehavior: {
|
||||
BehaviorUnspecified: #BehaviorUnspecified
|
||||
BehaviorCreate: #BehaviorCreate
|
||||
BehaviorReplace: #BehaviorReplace
|
||||
BehaviorMerge: #BehaviorMerge
|
||||
}
|
||||
|
||||
// BehaviorUnspecified is an Unspecified behavior; typically treated as a Create.
|
||||
#BehaviorUnspecified: #GenerationBehavior & 0
|
||||
|
||||
// BehaviorCreate makes a new resource.
|
||||
#BehaviorCreate: #GenerationBehavior & 1
|
||||
|
||||
// BehaviorReplace replaces a resource.
|
||||
#BehaviorReplace: #GenerationBehavior & 2
|
||||
|
||||
// BehaviorMerge attempts to merge a new resource with an existing resource.
|
||||
#BehaviorMerge: #GenerationBehavior & 3
|
||||
@@ -0,0 +1,27 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// GeneratorArgs contains arguments common to ConfigMap and Secret generators.
|
||||
#GeneratorArgs: {
|
||||
// Namespace for the resource, optional
|
||||
namespace?: string @go(Namespace)
|
||||
|
||||
// Name - actually the partial name - of the generated resource.
|
||||
// The full name ends up being something like
|
||||
// NamePrefix + this.Name + hash(content of generated resource).
|
||||
name?: string @go(Name)
|
||||
|
||||
// Behavior of generated resource, must be one of:
|
||||
// 'create': create a new one
|
||||
// 'replace': replace the existing one
|
||||
// 'merge': merge with the existing one
|
||||
behavior?: string @go(Behavior)
|
||||
|
||||
#KvPairSources
|
||||
|
||||
// Local overrides to global generatorOptions field.
|
||||
options?: null | #GeneratorOptions @go(Options,*GeneratorOptions)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// GeneratorOptions modify behavior of all ConfigMap and Secret generators.
|
||||
#GeneratorOptions: {
|
||||
// Labels to add to all generated resources.
|
||||
labels?: {[string]: string} @go(Labels,map[string]string)
|
||||
|
||||
// Annotations to add to all generated resources.
|
||||
annotations?: {[string]: string} @go(Annotations,map[string]string)
|
||||
|
||||
// DisableNameSuffixHash if true disables the default behavior of adding a
|
||||
// suffix to the names of generated resources that is a hash of the
|
||||
// resource contents.
|
||||
disableNameSuffixHash?: bool @go(DisableNameSuffixHash)
|
||||
|
||||
// Immutable if true add to all generated resources.
|
||||
immutable?: bool @go(Immutable)
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
#HelmDefaultHome: "charts"
|
||||
|
||||
#HelmGlobals: {
|
||||
// ChartHome is a file path, relative to the kustomization root,
|
||||
// to a directory containing a subdirectory for each chart to be
|
||||
// included in the kustomization.
|
||||
// The default value of this field is "charts".
|
||||
// So, for example, kustomize looks for the minecraft chart
|
||||
// at {kustomizationRoot}/{ChartHome}/minecraft.
|
||||
// If the chart is there at build time, kustomize will use it as found,
|
||||
// and not check version numbers or dates.
|
||||
// If the chart is not there, kustomize will attempt to pull it
|
||||
// using the version number specified in the kustomization file,
|
||||
// and put it there. To suppress the pull attempt, simply assure
|
||||
// that the chart is already there.
|
||||
chartHome?: string @go(ChartHome)
|
||||
|
||||
// ConfigHome defines a value that kustomize should pass to helm via
|
||||
// the HELM_CONFIG_HOME environment variable. kustomize doesn't attempt
|
||||
// to read or write this directory.
|
||||
// If omitted, {tmpDir}/helm is used, where {tmpDir} is some temporary
|
||||
// directory created by kustomize for the benefit of helm.
|
||||
// Likewise, kustomize sets
|
||||
// HELM_CACHE_HOME={ConfigHome}/.cache
|
||||
// HELM_DATA_HOME={ConfigHome}/.data
|
||||
// for the helm subprocess.
|
||||
configHome?: string @go(ConfigHome)
|
||||
}
|
||||
|
||||
#HelmChart: {
|
||||
// Name is the name of the chart, e.g. 'minecraft'.
|
||||
name?: string @go(Name)
|
||||
|
||||
// Version is the version of the chart, e.g. '3.1.3'
|
||||
version?: string @go(Version)
|
||||
|
||||
// Repo is a URL locating the chart on the internet.
|
||||
// This is the argument to helm's `--repo` flag, e.g.
|
||||
// `https://itzg.github.io/minecraft-server-charts`.
|
||||
repo?: string @go(Repo)
|
||||
|
||||
// ReleaseName replaces RELEASE-NAME in chart template output,
|
||||
// making a particular inflation of a chart unique with respect to
|
||||
// other inflations of the same chart in a cluster. It's the first
|
||||
// argument to the helm `install` and `template` commands, i.e.
|
||||
// helm install {RELEASE-NAME} {chartName}
|
||||
// helm template {RELEASE-NAME} {chartName}
|
||||
// If omitted, the flag --generate-name is passed to 'helm template'.
|
||||
releaseName?: string @go(ReleaseName)
|
||||
|
||||
// Namespace set the target namespace for a release. It is .Release.Namespace
|
||||
// in the helm template
|
||||
namespace?: string @go(Namespace)
|
||||
|
||||
// AdditionalValuesFiles are local file paths to values files to be used in
|
||||
// addition to either the default values file or the values specified in ValuesFile.
|
||||
additionalValuesFiles?: [...string] @go(AdditionalValuesFiles,[]string)
|
||||
|
||||
// ValuesFile is a local file path to a values file to use _instead of_
|
||||
// the default values that accompanied the chart.
|
||||
// The default values are in '{ChartHome}/{Name}/values.yaml'.
|
||||
valuesFile?: string @go(ValuesFile)
|
||||
|
||||
// ValuesInline holds value mappings specified directly,
|
||||
// rather than in a separate file.
|
||||
valuesInline?: {...} @go(ValuesInline,map[string]interface{})
|
||||
|
||||
// ValuesMerge specifies how to treat ValuesInline with respect to Values.
|
||||
// Legal values: 'merge', 'override', 'replace'.
|
||||
// Defaults to 'override'.
|
||||
valuesMerge?: string @go(ValuesMerge)
|
||||
|
||||
// IncludeCRDs specifies if Helm should also generate CustomResourceDefinitions.
|
||||
// Defaults to 'false'.
|
||||
includeCRDs?: bool @go(IncludeCRDs)
|
||||
|
||||
// SkipHooks sets the --no-hooks flag when calling helm template. This prevents
|
||||
// helm from erroneously rendering test templates.
|
||||
skipHooks?: bool @go(SkipHooks)
|
||||
|
||||
// ApiVersions is the kubernetes apiversions used for Capabilities.APIVersions
|
||||
apiVersions?: [...string] @go(ApiVersions,[]string)
|
||||
|
||||
// KubeVersion is the kubernetes version used by Helm for Capabilities.KubeVersion"
|
||||
kubeVersion?: string @go(KubeVersion)
|
||||
|
||||
// NameTemplate is for specifying the name template used to name the release.
|
||||
nameTemplate?: string @go(NameTemplate)
|
||||
|
||||
// SkipTests skips tests from templated output.
|
||||
skipTests?: bool @go(SkipTests)
|
||||
}
|
||||
|
||||
// HelmChartArgs contains arguments to helm.
|
||||
// Deprecated. Use HelmGlobals and HelmChart instead.
|
||||
#HelmChartArgs: {
|
||||
chartName?: string @go(ChartName)
|
||||
chartVersion?: string @go(ChartVersion)
|
||||
chartRepoUrl?: string @go(ChartRepoURL)
|
||||
chartHome?: string @go(ChartHome)
|
||||
chartRepoName?: string @go(ChartRepoName)
|
||||
helmBin?: string @go(HelmBin)
|
||||
helmHome?: string @go(HelmHome)
|
||||
values?: string @go(Values)
|
||||
valuesLocal?: {...} @go(ValuesLocal,map[string]interface{})
|
||||
valuesMerge?: string @go(ValuesMerge)
|
||||
releaseName?: string @go(ReleaseName)
|
||||
releaseNamespace?: string @go(ReleaseNamespace)
|
||||
extraArgs?: [...string] @go(ExtraArgs,[]string)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
#Cloud: string // #enumCloud
|
||||
|
||||
#enumCloud:
|
||||
#GKE
|
||||
|
||||
#GKE: #Cloud & "gke"
|
||||
|
||||
// IAMPolicyGeneratorArgs contains arguments to generate a GKE service account resource.
|
||||
#IAMPolicyGeneratorArgs: {
|
||||
// which cloud provider to generate for (e.g. "gke")
|
||||
cloud: #Cloud @go(Cloud)
|
||||
|
||||
// information about the kubernetes cluster for this object
|
||||
kubernetesService: #KubernetesService @go(KubernetesService)
|
||||
|
||||
// information about the service account and project
|
||||
serviceAccount: #ServiceAccount @go(ServiceAccount)
|
||||
}
|
||||
|
||||
#KubernetesService: {
|
||||
// the name used for the Kubernetes service account
|
||||
name: string @go(Name)
|
||||
|
||||
// the name of the Kubernetes namespace for this object
|
||||
namespace?: string @go(Namespace)
|
||||
}
|
||||
|
||||
#ServiceAccount: {
|
||||
// the name of the new cloud provider service account
|
||||
name: string @go(Name)
|
||||
|
||||
// The ID of the project
|
||||
projectId: string @go(ProjectId)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// Image contains an image name, a new name, a new tag or digest,
|
||||
// which will replace the original name and tag.
|
||||
#Image: {
|
||||
// Name is a tag-less image name.
|
||||
name?: string @go(Name)
|
||||
|
||||
// NewName is the value used to replace the original name.
|
||||
newName?: string @go(NewName)
|
||||
|
||||
// TagSuffix is the value used to suffix the original tag
|
||||
// If Digest and NewTag is present an error is thrown
|
||||
tagSuffix?: string @go(TagSuffix)
|
||||
|
||||
// NewTag is the value used to replace the original tag.
|
||||
newTag?: string @go(NewTag)
|
||||
|
||||
// Digest is the value used to replace the original image tag.
|
||||
// If digest is present NewTag value is ignored.
|
||||
digest?: string @go(Digest)
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
#KustomizationVersion: "kustomize.config.k8s.io/v1beta1"
|
||||
#KustomizationKind: "Kustomization"
|
||||
#ComponentVersion: "kustomize.config.k8s.io/v1alpha1"
|
||||
#ComponentKind: "Component"
|
||||
#MetadataNamespacePath: "metadata/namespace"
|
||||
#MetadataNamespaceApiVersion: "v1"
|
||||
#MetadataNamePath: "metadata/name"
|
||||
#OriginAnnotations: "originAnnotations"
|
||||
#TransformerAnnotations: "transformerAnnotations"
|
||||
#ManagedByLabelOption: "managedByLabel"
|
||||
|
||||
// Kustomization holds the information needed to generate customized k8s api resources.
|
||||
#Kustomization: {
|
||||
#TypeMeta
|
||||
|
||||
// MetaData is a pointer to avoid marshalling empty struct
|
||||
metadata?: null | #ObjectMeta @go(MetaData,*ObjectMeta)
|
||||
|
||||
// OpenAPI contains information about what kubernetes schema to use.
|
||||
openapi?: {[string]: string} @go(OpenAPI,map[string]string)
|
||||
|
||||
// NamePrefix will prefix the names of all resources mentioned in the kustomization
|
||||
// file including generated configmaps and secrets.
|
||||
namePrefix?: string @go(NamePrefix)
|
||||
|
||||
// NameSuffix will suffix the names of all resources mentioned in the kustomization
|
||||
// file including generated configmaps and secrets.
|
||||
nameSuffix?: string @go(NameSuffix)
|
||||
|
||||
// Namespace to add to all objects.
|
||||
namespace?: string @go(Namespace)
|
||||
|
||||
// CommonLabels to add to all objects and selectors.
|
||||
commonLabels?: {[string]: string} @go(CommonLabels,map[string]string)
|
||||
|
||||
// Labels to add to all objects but not selectors.
|
||||
labels?: [...#Label] @go(Labels,[]Label)
|
||||
|
||||
// CommonAnnotations to add to all objects.
|
||||
commonAnnotations?: {[string]: string} @go(CommonAnnotations,map[string]string)
|
||||
|
||||
// Deprecated: Use the Patches field instead, which provides a superset of the functionality of PatchesStrategicMerge.
|
||||
// PatchesStrategicMerge specifies the relative path to a file
|
||||
// containing a strategic merge patch. Format documented at
|
||||
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md
|
||||
// URLs and globs are not supported.
|
||||
patchesStrategicMerge?: [...#PatchStrategicMerge] @go(PatchesStrategicMerge,[]PatchStrategicMerge)
|
||||
|
||||
// Deprecated: Use the Patches field instead, which provides a superset of the functionality of JSONPatches.
|
||||
// JSONPatches is a list of JSONPatch for applying JSON patch.
|
||||
// Format documented at https://tools.ietf.org/html/rfc6902
|
||||
// and http://jsonpatch.com
|
||||
patchesJson6902?: [...#Patch] @go(PatchesJson6902,[]Patch)
|
||||
|
||||
// Patches is a list of patches, where each one can be either a
|
||||
// Strategic Merge Patch or a JSON patch.
|
||||
// Each patch can be applied to multiple target objects.
|
||||
patches?: [...#Patch] @go(Patches,[]Patch)
|
||||
|
||||
// Images is a list of (image name, new name, new tag or digest)
|
||||
// for changing image names, tags or digests. This can also be achieved with a
|
||||
// patch, but this operator is simpler to specify.
|
||||
images?: [...#Image] @go(Images,[]Image)
|
||||
|
||||
// Deprecated: Use the Images field instead.
|
||||
imageTags?: [...#Image] @go(ImageTags,[]Image)
|
||||
|
||||
// Replacements is a list of replacements, which will copy nodes from a
|
||||
// specified source to N specified targets.
|
||||
replacements?: [...#ReplacementField] @go(Replacements,[]ReplacementField)
|
||||
|
||||
// Replicas is a list of {resourcename, count} that allows for simpler replica
|
||||
// specification. This can also be done with a patch.
|
||||
replicas?: [...#Replica] @go(Replicas,[]Replica)
|
||||
|
||||
// Deprecated: Vars will be removed in future release. Migrate to Replacements instead.
|
||||
// Vars allow things modified by kustomize to be injected into a
|
||||
// kubernetes object specification. A var is a name (e.g. FOO) associated
|
||||
// with a field in a specific resource instance. The field must
|
||||
// contain a value of type string/bool/int/float, and defaults to the name field
|
||||
// of the instance. Any appearance of "$(FOO)" in the object
|
||||
// spec will be replaced at kustomize build time, after the final
|
||||
// value of the specified field has been determined.
|
||||
vars?: [...#Var] @go(Vars,[]Var)
|
||||
|
||||
// SortOptions change the order that kustomize outputs resources.
|
||||
sortOptions?: null | #SortOptions @go(SortOptions,*SortOptions)
|
||||
|
||||
// Resources specifies relative paths to files holding YAML representations
|
||||
// of kubernetes API objects, or specifications of other kustomizations
|
||||
// via relative paths, absolute paths, or URLs.
|
||||
resources?: [...string] @go(Resources,[]string)
|
||||
|
||||
// Components specifies relative paths to specifications of other Components
|
||||
// via relative paths, absolute paths, or URLs.
|
||||
components?: [...string] @go(Components,[]string)
|
||||
|
||||
// Crds specifies relative paths to Custom Resource Definition files.
|
||||
// This allows custom resources to be recognized as operands, making
|
||||
// it possible to add them to the Resources list.
|
||||
// CRDs themselves are not modified.
|
||||
crds?: [...string] @go(Crds,[]string)
|
||||
|
||||
// Deprecated: Anything that would have been specified here should be specified in the Resources field instead.
|
||||
bases?: [...string] @go(Bases,[]string)
|
||||
|
||||
// ConfigMapGenerator is a list of configmaps to generate from
|
||||
// local data (one configMap per list item).
|
||||
// The resulting resource is a normal operand, subject to
|
||||
// name prefixing, patching, etc. By default, the name of
|
||||
// the map will have a suffix hash generated from its contents.
|
||||
configMapGenerator?: [...#ConfigMapArgs] @go(ConfigMapGenerator,[]ConfigMapArgs)
|
||||
|
||||
// SecretGenerator is a list of secrets to generate from
|
||||
// local data (one secret per list item).
|
||||
// The resulting resource is a normal operand, subject to
|
||||
// name prefixing, patching, etc. By default, the name of
|
||||
// the map will have a suffix hash generated from its contents.
|
||||
secretGenerator?: [...#SecretArgs] @go(SecretGenerator,[]SecretArgs)
|
||||
|
||||
// HelmGlobals contains helm configuration that isn't chart specific.
|
||||
helmGlobals?: null | #HelmGlobals @go(HelmGlobals,*HelmGlobals)
|
||||
|
||||
// HelmCharts is a list of helm chart configuration instances.
|
||||
helmCharts?: [...#HelmChart] @go(HelmCharts,[]HelmChart)
|
||||
|
||||
// HelmChartInflationGenerator is a list of helm chart configurations.
|
||||
// Deprecated. Auto-converted to HelmGlobals and HelmCharts.
|
||||
helmChartInflationGenerator?: [...#HelmChartArgs] @go(HelmChartInflationGenerator,[]HelmChartArgs)
|
||||
|
||||
// GeneratorOptions modify behavior of all ConfigMap and Secret generators.
|
||||
generatorOptions?: null | #GeneratorOptions @go(GeneratorOptions,*GeneratorOptions)
|
||||
|
||||
// Configurations is a list of transformer configuration files
|
||||
configurations?: [...string] @go(Configurations,[]string)
|
||||
|
||||
// Generators is a list of files containing custom generators
|
||||
generators?: [...string] @go(Generators,[]string)
|
||||
|
||||
// Transformers is a list of files containing transformers
|
||||
transformers?: [...string] @go(Transformers,[]string)
|
||||
|
||||
// Validators is a list of files containing validators
|
||||
validators?: [...string] @go(Validators,[]string)
|
||||
|
||||
// BuildMetadata is a list of strings used to toggle different build options
|
||||
buildMetadata?: [...string] @go(BuildMetadata,[]string)
|
||||
}
|
||||
|
||||
_#deprecatedWarningToRunEditFix: "Run 'kustomize edit fix' to update your Kustomization automatically."
|
||||
_#deprecatedWarningToRunEditFixExperimential: "[EXPERIMENTAL] Run 'kustomize edit fix' to update your Kustomization automatically."
|
||||
_#deprecatedBaseWarningMessage: "# Warning: 'bases' is deprecated. Please use 'resources' instead. Run 'kustomize edit fix' to update your Kustomization automatically."
|
||||
_#deprecatedImageTagsWarningMessage: "# Warning: 'imageTags' is deprecated. Please use 'images' instead. Run 'kustomize edit fix' to update your Kustomization automatically."
|
||||
_#deprecatedPatchesJson6902Message: "# Warning: 'patchesJson6902' is deprecated. Please use 'patches' instead. Run 'kustomize edit fix' to update your Kustomization automatically."
|
||||
_#deprecatedPatchesStrategicMergeMessage: "# Warning: 'patchesStrategicMerge' is deprecated. Please use 'patches' instead. Run 'kustomize edit fix' to update your Kustomization automatically."
|
||||
_#deprecatedVarsMessage: "# Warning: 'vars' is deprecated. Please use 'replacements' instead. [EXPERIMENTAL] Run 'kustomize edit fix' to update your Kustomization automatically."
|
||||
_#deprecatedCommonLabelsWarningMessage: "# Warning: 'commonLabels' is deprecated. Please use 'labels' instead. Run 'kustomize edit fix' to update your Kustomization automatically."
|
||||
@@ -0,0 +1,37 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// KvPairSources defines places to obtain key value pairs.
|
||||
#KvPairSources: {
|
||||
// LiteralSources is a list of literal
|
||||
// pair sources. Each literal source should
|
||||
// be a key and literal value, e.g. `key=value`
|
||||
literals?: [...string] @go(LiteralSources,[]string)
|
||||
|
||||
// FileSources is a list of file "sources" to
|
||||
// use in creating a list of key, value pairs.
|
||||
// A source takes the form: [{key}=]{path}
|
||||
// If the "key=" part is missing, the key is the
|
||||
// path's basename. If they "key=" part is present,
|
||||
// it becomes the key (replacing the basename).
|
||||
// In either case, the value is the file contents.
|
||||
// Specifying a directory will iterate each named
|
||||
// file in the directory whose basename is a
|
||||
// valid configmap key.
|
||||
files?: [...string] @go(FileSources,[]string)
|
||||
|
||||
// EnvSources is a list of file paths.
|
||||
// The contents of each file should be one
|
||||
// key=value pair per line, e.g. a Docker
|
||||
// or npm ".env" file or a ".ini" file
|
||||
// (wikipedia.org/wiki/INI_file)
|
||||
envs?: [...string] @go(EnvSources,[]string)
|
||||
|
||||
// Older, singular form of EnvSources.
|
||||
// On edits (e.g. `kustomize fix`) this is merged into the plural form
|
||||
// for consistency with LiteralSources and FileSources.
|
||||
env?: string @go(EnvSource)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
#Label: {
|
||||
// Pairs contains the key-value pairs for labels to add
|
||||
pairs?: {[string]: string} @go(Pairs,map[string]string)
|
||||
|
||||
// IncludeSelectors inidicates should transformer include the
|
||||
// fieldSpecs for selectors. Custom fieldSpecs specified by
|
||||
// FieldSpecs will be merged with builtin fieldSpecs if this
|
||||
// is true.
|
||||
includeSelectors?: bool @go(IncludeSelectors)
|
||||
|
||||
// IncludeTemplates inidicates should transformer include the
|
||||
// spec/template/metadata fieldSpec. Custom fieldSpecs specified by
|
||||
// FieldSpecs will be merged with spec/template/metadata fieldSpec if this
|
||||
// is true. If IncludeSelectors is true, IncludeTemplates is not needed.
|
||||
includeTemplates?: bool @go(IncludeTemplates)
|
||||
fields?: [...#FieldSpec] @go(FieldSpecs,[]FieldSpec)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// Restrictions on what things can be referred to
|
||||
// in a kustomization file.
|
||||
//
|
||||
//go:generate stringer -type=LoadRestrictions
|
||||
#LoadRestrictions: int // #enumLoadRestrictions
|
||||
|
||||
#enumLoadRestrictions:
|
||||
#LoadRestrictionsUnknown |
|
||||
#LoadRestrictionsRootOnly |
|
||||
#LoadRestrictionsNone
|
||||
|
||||
#values_LoadRestrictions: {
|
||||
LoadRestrictionsUnknown: #LoadRestrictionsUnknown
|
||||
LoadRestrictionsRootOnly: #LoadRestrictionsRootOnly
|
||||
LoadRestrictionsNone: #LoadRestrictionsNone
|
||||
}
|
||||
|
||||
#LoadRestrictionsUnknown: #LoadRestrictions & 0
|
||||
|
||||
// Files referenced by a kustomization file must be in
|
||||
// or under the directory holding the kustomization
|
||||
// file itself.
|
||||
#LoadRestrictionsRootOnly: #LoadRestrictions & 1
|
||||
|
||||
// The kustomization file may specify absolute or
|
||||
// relative paths to patch or resources files outside
|
||||
// its own tree.
|
||||
#LoadRestrictionsNone: #LoadRestrictions & 2
|
||||
@@ -0,0 +1,7 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
_#_LoadRestrictions_name: "LoadRestrictionsUnknownLoadRestrictionsRootOnlyLoadRestrictionsNone"
|
||||
@@ -0,0 +1,14 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// ObjectMeta partially copies apimachinery/pkg/apis/meta/v1.ObjectMeta
|
||||
// No need for a direct dependence; the fields are stable.
|
||||
#ObjectMeta: {
|
||||
name?: string @go(Name)
|
||||
namespace?: string @go(Namespace)
|
||||
labels?: {[string]: string} @go(Labels,map[string]string)
|
||||
annotations?: {[string]: string} @go(Annotations,map[string]string)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// Pair is a key value pair.
|
||||
#Pair: {
|
||||
Key: string
|
||||
Value: string
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// Patch represent either a Strategic Merge Patch or a JSON patch
|
||||
// and its targets.
|
||||
// The content of the patch can either be from a file
|
||||
// or from an inline string.
|
||||
#Patch: {
|
||||
// Path is a relative file path to the patch file.
|
||||
path?: string @go(Path)
|
||||
|
||||
// Patch is the content of a patch.
|
||||
patch?: string @go(Patch)
|
||||
|
||||
// Target points to the resources that the patch is applied to
|
||||
target?: #Target | #Selector @go(Target,*Selector)
|
||||
|
||||
// Options is a list of options for the patch
|
||||
options?: {[string]: bool} @go(Options,map[string]bool)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// PatchStrategicMerge represents a relative path to a
|
||||
// stategic merge patch with the format
|
||||
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md
|
||||
#PatchStrategicMerge: string
|
||||
@@ -0,0 +1,27 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
#HelmConfig: {
|
||||
Enabled: bool
|
||||
Command: string
|
||||
ApiVersions: [...string] @go(,[]string)
|
||||
KubeVersion: string
|
||||
}
|
||||
|
||||
// PluginConfig holds plugin configuration.
|
||||
#PluginConfig: {
|
||||
// PluginRestrictions distinguishes plugin restrictions.
|
||||
PluginRestrictions: #PluginRestrictions
|
||||
|
||||
// BpLoadingOptions distinguishes builtin plugin behaviors.
|
||||
BpLoadingOptions: #BuiltinPluginLoadingOptions
|
||||
|
||||
// FnpLoadingOptions sets the way function-based plugin behaviors.
|
||||
FnpLoadingOptions: #FnPluginLoadingOptions
|
||||
|
||||
// HelmConfig contains metadata needed for allowing and running helm.
|
||||
HelmConfig: #HelmConfig
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// Some plugin classes
|
||||
// - builtin: plugins defined in the kustomize repo.
|
||||
// May be freely used and re-configured.
|
||||
// - local: plugins that aren't builtin but are
|
||||
// locally defined (presumably by the user), meaning
|
||||
// the kustomization refers to them via a relative
|
||||
// file path, not a URL.
|
||||
// - remote: require a build-time download to obtain.
|
||||
// Unadvised, unless one controls the
|
||||
// serving site.
|
||||
//
|
||||
//go:generate stringer -type=PluginRestrictions
|
||||
#PluginRestrictions: int // #enumPluginRestrictions
|
||||
|
||||
#enumPluginRestrictions:
|
||||
#PluginRestrictionsUnknown |
|
||||
#PluginRestrictionsBuiltinsOnly |
|
||||
#PluginRestrictionsNone
|
||||
|
||||
#values_PluginRestrictions: {
|
||||
PluginRestrictionsUnknown: #PluginRestrictionsUnknown
|
||||
PluginRestrictionsBuiltinsOnly: #PluginRestrictionsBuiltinsOnly
|
||||
PluginRestrictionsNone: #PluginRestrictionsNone
|
||||
}
|
||||
|
||||
#PluginRestrictionsUnknown: #PluginRestrictions & 0
|
||||
|
||||
// Non-builtin plugins completely disabled.
|
||||
#PluginRestrictionsBuiltinsOnly: #PluginRestrictions & 1
|
||||
|
||||
// No restrictions, do whatever you want.
|
||||
#PluginRestrictionsNone: #PluginRestrictions & 2
|
||||
|
||||
// BuiltinPluginLoadingOptions distinguish ways in which builtin plugins are used.
|
||||
//go:generate stringer -type=BuiltinPluginLoadingOptions
|
||||
#BuiltinPluginLoadingOptions: int // #enumBuiltinPluginLoadingOptions
|
||||
|
||||
#enumBuiltinPluginLoadingOptions:
|
||||
#BploUndefined |
|
||||
#BploUseStaticallyLinked |
|
||||
#BploLoadFromFileSys
|
||||
|
||||
#values_BuiltinPluginLoadingOptions: {
|
||||
BploUndefined: #BploUndefined
|
||||
BploUseStaticallyLinked: #BploUseStaticallyLinked
|
||||
BploLoadFromFileSys: #BploLoadFromFileSys
|
||||
}
|
||||
|
||||
#BploUndefined: #BuiltinPluginLoadingOptions & 0
|
||||
|
||||
// Desired in production use for performance.
|
||||
#BploUseStaticallyLinked: #BuiltinPluginLoadingOptions & 1
|
||||
|
||||
// Desired in testing and development cycles where it's undesirable
|
||||
// to generate static code.
|
||||
#BploLoadFromFileSys: #BuiltinPluginLoadingOptions & 2
|
||||
|
||||
// FnPluginLoadingOptions set way functions-based plugins are restricted
|
||||
#FnPluginLoadingOptions: {
|
||||
// Allow to run executables
|
||||
EnableExec: bool
|
||||
|
||||
// Allow to run starlark
|
||||
EnableStar: bool
|
||||
|
||||
// Allow container access to network
|
||||
Network: bool
|
||||
NetworkName: string
|
||||
|
||||
// list of mounts
|
||||
Mounts: [...string] @go(,[]string)
|
||||
|
||||
// list of env variables to pass to fn
|
||||
Env: [...string] @go(,[]string)
|
||||
|
||||
// Run as uid and gid of the command executor
|
||||
AsCurrentUser: bool
|
||||
|
||||
// Run in this working directory
|
||||
WorkingDir: string
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
_#_PluginRestrictions_name: "PluginRestrictionsUnknownPluginRestrictionsBuiltinsOnlyPluginRestrictionsNone"
|
||||
@@ -0,0 +1,57 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
#DefaultReplacementFieldPath: "metadata.name"
|
||||
|
||||
// Replacement defines how to perform a substitution
|
||||
// where it is from and where it is to.
|
||||
#Replacement: {
|
||||
// The source of the value.
|
||||
source?: null | #SourceSelector @go(Source,*SourceSelector)
|
||||
|
||||
// The N fields to write the value to.
|
||||
targets?: [...null | #TargetSelector] @go(Targets,[]*TargetSelector)
|
||||
}
|
||||
|
||||
// SourceSelector is the source of the replacement transformer.
|
||||
#SourceSelector: {
|
||||
// Structured field path expected in the allowed object.
|
||||
fieldPath?: string @go(FieldPath)
|
||||
|
||||
// Used to refine the interpretation of the field.
|
||||
options?: null | #FieldOptions @go(Options,*FieldOptions)
|
||||
}
|
||||
|
||||
// TargetSelector specifies fields in one or more objects.
|
||||
#TargetSelector: {
|
||||
// Include objects that match this.
|
||||
select?: null | #Selector @go(Select,*Selector)
|
||||
|
||||
// From the allowed set, remove objects that match this.
|
||||
reject?: [...null | #Selector] @go(Reject,[]*Selector)
|
||||
|
||||
// Structured field paths expected in each allowed object.
|
||||
fieldPaths?: [...string] @go(FieldPaths,[]string)
|
||||
|
||||
// Used to refine the interpretation of the field.
|
||||
options?: null | #FieldOptions @go(Options,*FieldOptions)
|
||||
}
|
||||
|
||||
// FieldOptions refine the interpretation of FieldPaths.
|
||||
#FieldOptions: {
|
||||
// Used to split/join the field.
|
||||
delimiter?: string @go(Delimiter)
|
||||
|
||||
// Which position in the split to consider.
|
||||
index?: int @go(Index)
|
||||
|
||||
// TODO (#3492): Implement use of this option
|
||||
// None, Base64, URL, Hex, etc
|
||||
encoding?: string @go(Encoding)
|
||||
|
||||
// If field missing, add it.
|
||||
create?: bool @go(Create)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
#ReplacementField: {
|
||||
#Replacement
|
||||
path?: string @go(Path)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// Replica specifies a modification to a replica config.
|
||||
// The number of replicas of a resource whose name matches will be set to count.
|
||||
// This struct is used by the ReplicaCountTransform, and is meant to supplement
|
||||
// the existing patch functionality with a simpler syntax for replica configuration.
|
||||
#Replica: {
|
||||
// The name of the resource to change the replica count
|
||||
name?: string @go(Name)
|
||||
|
||||
// The number of replicas required.
|
||||
count: int64 @go(Count)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// SecretArgs contains the metadata of how to generate a secret.
|
||||
#SecretArgs: {
|
||||
#GeneratorArgs
|
||||
|
||||
// Type of the secret.
|
||||
//
|
||||
// This is the same field as the secret type field in v1/Secret:
|
||||
// It can be "Opaque" (default), or "kubernetes.io/tls".
|
||||
//
|
||||
// If type is "kubernetes.io/tls", then "literals" or "files" must have exactly two
|
||||
// keys: "tls.key" and "tls.crt"
|
||||
type?: string @go(Type)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// Selector specifies a set of resources.
|
||||
// Any resource that matches intersection of all conditions
|
||||
// is included in this set.
|
||||
#Selector: {
|
||||
// AnnotationSelector is a string that follows the label selection expression
|
||||
// https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api
|
||||
// It matches with the resource annotations.
|
||||
annotationSelector?: string @go(AnnotationSelector)
|
||||
|
||||
// LabelSelector is a string that follows the label selection expression
|
||||
// https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api
|
||||
// It matches with the resource labels.
|
||||
labelSelector?: string @go(LabelSelector)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// SortOptions defines the order that kustomize outputs resources.
|
||||
#SortOptions: {
|
||||
// Order selects the ordering strategy.
|
||||
order?: #SortOrder @go(Order)
|
||||
|
||||
// LegacySortOptions tweaks the sorting for the "legacy" sort ordering
|
||||
// strategy.
|
||||
legacySortOptions?: null | #LegacySortOptions @go(LegacySortOptions,*LegacySortOptions)
|
||||
}
|
||||
|
||||
// SortOrder defines different ordering strategies.
|
||||
#SortOrder: string // #enumSortOrder
|
||||
|
||||
#enumSortOrder:
|
||||
#LegacySortOrder |
|
||||
#FIFOSortOrder
|
||||
|
||||
#LegacySortOrder: #SortOrder & "legacy"
|
||||
|
||||
#FIFOSortOrder: #SortOrder & "fifo"
|
||||
|
||||
// LegacySortOptions define various options for tweaking the "legacy" ordering
|
||||
// strategy.
|
||||
#LegacySortOptions: {
|
||||
// OrderFirst selects the resource kinds to order first.
|
||||
orderFirst: [...string] @go(OrderFirst,[]string)
|
||||
|
||||
// OrderLast selects the resource kinds to order last.
|
||||
orderLast: [...string] @go(OrderLast,[]string)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// TypeMeta partially copies apimachinery/pkg/apis/meta/v1.TypeMeta
|
||||
// No need for a direct dependence; the fields are stable.
|
||||
#TypeMeta: {
|
||||
kind?: string @go(Kind)
|
||||
apiVersion?: string @go(APIVersion)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Code generated by cue get go. DO NOT EDIT.
|
||||
|
||||
//cue:generate cue get go sigs.k8s.io/kustomize/api/types
|
||||
|
||||
package types
|
||||
|
||||
// Var represents a variable whose value will be sourced
|
||||
// from a field in a Kubernetes object.
|
||||
#Var: {
|
||||
// Value of identifier name e.g. FOO used in container args, annotations
|
||||
// Appears in pod template as $(FOO)
|
||||
name: string @go(Name)
|
||||
|
||||
// ObjRef must refer to a Kubernetes resource under the
|
||||
// purview of this kustomization. ObjRef should use the
|
||||
// raw name of the object (the name specified in its YAML,
|
||||
// before addition of a namePrefix and a nameSuffix).
|
||||
objref: #Target @go(ObjRef)
|
||||
|
||||
// FieldRef refers to the field of the object referred to by
|
||||
// ObjRef whose value will be extracted for use in
|
||||
// replacing $(FOO).
|
||||
// If unspecified, this defaults to fieldPath: $defaultFieldPath
|
||||
fieldref?: #FieldSelector @go(FieldRef)
|
||||
}
|
||||
|
||||
// Target refers to a kubernetes object by Group, Version, Kind and Name
|
||||
// gvk.Gvk contains Group, Version and Kind
|
||||
// APIVersion is added to keep the backward compatibility of using ObjectReference
|
||||
// for Var.ObjRef
|
||||
#Target: {
|
||||
apiVersion?: string @go(APIVersion)
|
||||
name: string @go(Name)
|
||||
namespace?: string @go(Namespace)
|
||||
}
|
||||
|
||||
// FieldSelector contains the fieldPath to an object field.
|
||||
// This struct is added to keep the backward compatibility of using ObjectFieldSelector
|
||||
// for Var.FieldRef
|
||||
#FieldSelector: {
|
||||
fieldPath?: string @go(FieldPath)
|
||||
}
|
||||
|
||||
// byName is a sort interface which sorts Vars by name alphabetically
|
||||
_#byName: [...#Var]
|
||||
@@ -1,6 +1,6 @@
|
||||
package v1
|
||||
|
||||
#Deployment: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package v1
|
||||
|
||||
#CronJob: {
|
||||
apiVersion: "batch/v1"
|
||||
kind: "CronJob"
|
||||
apiVersion: "batch/v1"
|
||||
kind: "CronJob"
|
||||
}
|
||||
|
||||
#Job: {
|
||||
apiVersion: "batch/v1"
|
||||
kind: "Job"
|
||||
apiVersion: "batch/v1"
|
||||
kind: "Job"
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
package v1
|
||||
|
||||
#Namespace: {
|
||||
apiVersion: "v1"
|
||||
kind: "Namespace"
|
||||
apiVersion: "v1"
|
||||
kind: "Namespace"
|
||||
}
|
||||
|
||||
#ConfigMap: {
|
||||
apiVersion: "v1"
|
||||
kind: "ConfigMap"
|
||||
apiVersion: "v1"
|
||||
kind: "ConfigMap"
|
||||
}
|
||||
|
||||
#ServiceAccount: {
|
||||
apiVersion: "v1"
|
||||
kind: "ServiceAccount"
|
||||
apiVersion: "v1"
|
||||
kind: "ServiceAccount"
|
||||
}
|
||||
|
||||
#Pod: {
|
||||
apiVersion: "v1"
|
||||
kind: "Pod"
|
||||
apiVersion: "v1"
|
||||
kind: "Pod"
|
||||
}
|
||||
|
||||
#Service: {
|
||||
apiVersion: "v1"
|
||||
kind: "Service"
|
||||
apiVersion: "v1"
|
||||
kind: "Service"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package types
|
||||
|
||||
#Patch: {
|
||||
// Path is a relative file path to the patch file.
|
||||
path?: string @go(Path)
|
||||
|
||||
// Patch is the content of a patch.
|
||||
patch?: string @go(Patch)
|
||||
|
||||
// Target points to the resources that the patch is applied to
|
||||
target?: #Target | #Selector @go(Target,*Selector)
|
||||
|
||||
// Options is a list of options for the patch
|
||||
options?: {[string]: bool} @go(Options,map[string]bool)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package types
|
||||
|
||||
#Target: {
|
||||
group?: string @go(Group)
|
||||
version?: string @go(Version)
|
||||
kind?: string @go(Kind)
|
||||
}
|
||||
61
docs/examples/platforms/reference/certificates.cue
Normal file
61
docs/examples/platforms/reference/certificates.cue
Normal file
@@ -0,0 +1,61 @@
|
||||
package holos
|
||||
|
||||
#PlatformCerts: {
|
||||
// Globally scoped platform services are defined here.
|
||||
login: #PlatformCert & {
|
||||
_name: "login"
|
||||
_wildcard: true
|
||||
_description: "Cert for Zitadel oidc identity provider for iam services"
|
||||
}
|
||||
|
||||
// Cluster scoped services are defined here.
|
||||
for cluster in #Platform.clusters {
|
||||
"\(cluster.name)-httpbin": #ClusterCert & {
|
||||
_name: "httpbin"
|
||||
_cluster: cluster.name
|
||||
_description: "Test endpoint to verify the service mesh ingress gateway"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #PlatformCert provisions a cert in the provisioner cluster.
|
||||
// Workload clusters use ExternalSecret resources to fetch the Secret tls key and cert from the provisioner cluster.
|
||||
#PlatformCert: #Certificate & {
|
||||
_name: string
|
||||
_wildcard: true | *false
|
||||
metadata: name: string | *_name
|
||||
metadata: namespace: string | *"istio-ingress"
|
||||
spec: {
|
||||
commonName: string | *"\(_name).\(#Platform.org.domain)"
|
||||
if _wildcard {
|
||||
dnsNames: [commonName, "*.\(commonName)"]
|
||||
}
|
||||
if !_wildcard {
|
||||
dnsNames: [commonName]
|
||||
}
|
||||
secretName: metadata.name
|
||||
issuerRef: kind: "ClusterIssuer"
|
||||
issuerRef: name: string | *"letsencrypt"
|
||||
}
|
||||
}
|
||||
|
||||
// #ClusterCert provisions a cluster specific certificate.
|
||||
#ClusterCert: #Certificate & {
|
||||
_name: string
|
||||
_cluster: string
|
||||
_wildcard: true | *false
|
||||
metadata: name: string | *"\(_cluster)-\(_name)"
|
||||
metadata: namespace: string | *"istio-ingress"
|
||||
spec: {
|
||||
commonName: string | *"\(_name).\(_cluster).\(#Platform.org.domain)"
|
||||
if _wildcard {
|
||||
dnsNames: [commonName, "*.\(commonName)"]
|
||||
}
|
||||
if !_wildcard {
|
||||
dnsNames: [commonName]
|
||||
}
|
||||
secretName: metadata.name
|
||||
issuerRef: kind: "ClusterIssuer"
|
||||
issuerRef: name: string | *"letsencrypt"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package holos
|
||||
|
||||
// Components under this directory are part of this collection
|
||||
#InputKeys: project: "iam"
|
||||
|
||||
// Shared dependencies for all components in this collection.
|
||||
#DependsOn: _Namespaces
|
||||
|
||||
// Common Dependencies
|
||||
_Namespaces: Namespaces: name: "\(#StageName)-secrets-namespaces"
|
||||
@@ -0,0 +1,108 @@
|
||||
package holos
|
||||
|
||||
// Manage an Issuer for cockroachdb for zitadel.
|
||||
// For the iam login service, zitadel connects to cockroach db using tls certs for authz.
|
||||
// Upstream: "The recommended approach is to use cert-manager for certificate management. For details, refer to Deploy cert-manager for mTLS."
|
||||
// Refer to https://www.cockroachlabs.com/docs/stable/secure-cockroachdb-kubernetes#deploy-cert-manager-for-mtls
|
||||
|
||||
#InputKeys: component: "crdb"
|
||||
|
||||
#KubernetesObjects & {
|
||||
apiObjects: {
|
||||
Issuer: {
|
||||
// https://github.com/cockroachdb/helm-charts/blob/3dcf96726ebcfe3784afb526ddcf4095a1684aea/README.md?plain=1#L196-L201
|
||||
crdb: #Issuer & {
|
||||
_description: "Issues the self signed root ca cert for cockroach db"
|
||||
metadata: name: #ComponentName
|
||||
metadata: namespace: #TargetNamespace
|
||||
spec: selfSigned: {}
|
||||
}
|
||||
"crdb-ca-issuer": #Issuer & {
|
||||
_description: "Issues mtls certs for cockroach db"
|
||||
metadata: name: "crdb-ca-issuer"
|
||||
metadata: namespace: #TargetNamespace
|
||||
spec: ca: secretName: "cockroach-ca"
|
||||
}
|
||||
}
|
||||
Certificate: {
|
||||
"crdb-ca-cert": #Certificate & {
|
||||
_description: "Root CA cert for cockroach db"
|
||||
metadata: name: "crdb-ca-cert"
|
||||
metadata: namespace: #TargetNamespace
|
||||
spec: {
|
||||
commonName: "root"
|
||||
isCA: true
|
||||
issuerRef: group: "cert-manager.io"
|
||||
issuerRef: kind: "Issuer"
|
||||
issuerRef: name: "crdb"
|
||||
privateKey: algorithm: "ECDSA"
|
||||
privateKey: size: 256
|
||||
secretName: "cockroach-ca"
|
||||
subject: organizations: ["Cockroach"]
|
||||
}
|
||||
}
|
||||
"crdb-node": #Certificate & {
|
||||
metadata: name: "crdb-node"
|
||||
metadata: namespace: #TargetNamespace
|
||||
spec: {
|
||||
commonName: "node"
|
||||
dnsNames: [
|
||||
"localhost",
|
||||
"127.0.0.1",
|
||||
"crdb-public",
|
||||
"crdb-public.\(#TargetNamespace)",
|
||||
"crdb-public.\(#TargetNamespace).svc.cluster.local",
|
||||
"*.crdb",
|
||||
"*.crdb.\(#TargetNamespace)",
|
||||
"*.crdb.\(#TargetNamespace).svc.cluster.local",
|
||||
]
|
||||
duration: "876h"
|
||||
issuerRef: group: "cert-manager.io"
|
||||
issuerRef: kind: "Issuer"
|
||||
issuerRef: name: "crdb-ca-issuer"
|
||||
privateKey: algorithm: "RSA"
|
||||
privateKey: size: 2048
|
||||
renewBefore: "168h"
|
||||
secretName: "cockroachdb-node"
|
||||
subject: organizations: ["Cockroach"]
|
||||
usages: ["digital signature", "key encipherment", "server auth", "client auth"]
|
||||
}
|
||||
}
|
||||
"crdb-root-client": #Certificate & {
|
||||
metadata: name: "crdb-root-client"
|
||||
metadata: namespace: #TargetNamespace
|
||||
spec: {
|
||||
commonName: "root"
|
||||
duration: "672h"
|
||||
issuerRef: group: "cert-manager.io"
|
||||
issuerRef: kind: "Issuer"
|
||||
issuerRef: name: "crdb-ca-issuer"
|
||||
privateKey: algorithm: "RSA"
|
||||
privateKey: size: 2048
|
||||
renewBefore: "48h"
|
||||
secretName: "cockroachdb-root"
|
||||
subject: organizations: ["Cockroach"]
|
||||
usages: ["digital signature", "key encipherment", "client auth"]
|
||||
}
|
||||
}
|
||||
}
|
||||
Certificate: zitadel: #Certificate & {
|
||||
metadata: name: "crdb-zitadel-client"
|
||||
metadata: namespace: #TargetNamespace
|
||||
spec: {
|
||||
commonName: "zitadel"
|
||||
issuerRef: {
|
||||
group: "cert-manager.io"
|
||||
kind: "Issuer"
|
||||
name: "crdb-ca-issuer"
|
||||
}
|
||||
privateKey: algorithm: "RSA"
|
||||
privateKey: size: 2048
|
||||
renewBefore: "48h0m0s"
|
||||
secretName: "cockroachdb-zitadel"
|
||||
subject: organizations: ["Cockroach"]
|
||||
usages: ["digital signature", "key encipherment", "client auth"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package holos
|
||||
|
||||
#TargetNamespace: #InstancePrefix + "-zitadel"
|
||||
|
||||
#DB: {
|
||||
Host: "crdb-public"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package holos
|
||||
|
||||
// Provision all platform certificates.
|
||||
#InputKeys: component: "certificates"
|
||||
|
||||
// Certificates usually go into the istio-system namespace, but they may go anywhere.
|
||||
#TargetNamespace: "default"
|
||||
|
||||
// Depends on issuers
|
||||
#DependsOn: _LetsEncrypt
|
||||
|
||||
#KubernetesObjects & {
|
||||
apiObjects: {
|
||||
for k, obj in #PlatformCerts {
|
||||
"\(obj.kind)": {
|
||||
"\(obj.metadata.namespace)/\(obj.metadata.name)": obj
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package holos
|
||||
|
||||
// https://cert-manager.io/docs/
|
||||
|
||||
#TargetNamespace: "cert-manager"
|
||||
|
||||
#InputKeys: {
|
||||
component: "certmanager"
|
||||
service: "cert-manager"
|
||||
}
|
||||
|
||||
#HelmChart & {
|
||||
values: #Values & {
|
||||
installCRDs: true
|
||||
startupapicheck: enabled: false
|
||||
// Must not use kube-system on gke autopilot. GKE Warden authz blocks access.
|
||||
global: leaderElection: namespace: #TargetNamespace
|
||||
}
|
||||
namespace: #TargetNamespace
|
||||
chart: {
|
||||
name: "cert-manager"
|
||||
version: "1.14.3"
|
||||
repository: {
|
||||
name: "jetstack"
|
||||
url: "https://charts.jetstack.io"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://cloud.google.com/kubernetes-engine/docs/concepts/autopilot-resource-requests#min-max-requests
|
||||
#PodResources: {
|
||||
requests: {
|
||||
cpu: string | *"250m"
|
||||
memory: string | *"512Mi"
|
||||
"ephemeral-storage": string | *"100Mi"
|
||||
}
|
||||
}
|
||||
|
||||
// https://cloud.google.com/kubernetes-engine/docs/how-to/autopilot-spot-pods
|
||||
#NodeSelector: {
|
||||
"kubernetes.io/os": "linux"
|
||||
"cloud.google.com/gke-spot": "true"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package holos
|
||||
|
||||
#UpstreamValues: {
|
||||
#Values: {
|
||||
|
||||
// +docs:section=Global
|
||||
// Default values for cert-manager.
|
||||
@@ -51,7 +51,7 @@ package holos
|
||||
|
||||
leaderElection: {
|
||||
// Override the namespace used for the leader election lease
|
||||
namespace: "kube-system"
|
||||
namespace: string | *"kube-system"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ package holos
|
||||
// memory: 32Mi
|
||||
//
|
||||
// ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
|
||||
resources: {}
|
||||
resources: #PodResources
|
||||
|
||||
// Pod Security Context
|
||||
// ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
|
||||
@@ -310,9 +310,7 @@ package holos
|
||||
// This default ensures that Pods are only scheduled to Linux nodes.
|
||||
// It prevents Pods being scheduled to Windows nodes in a mixed OS cluster.
|
||||
// +docs:property
|
||||
nodeSelector: {
|
||||
"kubernetes.io/os": "linux"
|
||||
}
|
||||
nodeSelector: #NodeSelector
|
||||
|
||||
// +docs:ignore
|
||||
ingressShim: {}
|
||||
@@ -408,7 +406,7 @@ package holos
|
||||
enabled: true
|
||||
servicemonitor: {
|
||||
// Create a ServiceMonitor to add cert-manager to Prometheus
|
||||
enabled: false
|
||||
enabled: true | *false
|
||||
|
||||
// Specifies the `prometheus` label on the created ServiceMonitor, this is
|
||||
// used when different Prometheus instances have label selectors matching
|
||||
@@ -652,7 +650,7 @@ package holos
|
||||
// memory: 32Mi
|
||||
//
|
||||
// ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
|
||||
resources: {}
|
||||
resources: #PodResources
|
||||
|
||||
// Liveness probe values
|
||||
// ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes
|
||||
@@ -685,9 +683,7 @@ package holos
|
||||
// This default ensures that Pods are only scheduled to Linux nodes.
|
||||
// It prevents Pods being scheduled to Windows nodes in a mixed OS cluster.
|
||||
// +docs:property
|
||||
nodeSelector: {
|
||||
"kubernetes.io/os": "linux"
|
||||
}
|
||||
nodeSelector: #NodeSelector
|
||||
|
||||
// A Kubernetes Affinity, if required; see https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core
|
||||
//
|
||||
@@ -959,7 +955,7 @@ package holos
|
||||
// memory: 32Mi
|
||||
//
|
||||
// ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
|
||||
resources: {}
|
||||
resources: #PodResources
|
||||
|
||||
// The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with
|
||||
// matching labels.
|
||||
@@ -968,9 +964,7 @@ package holos
|
||||
// This default ensures that Pods are only scheduled to Linux nodes.
|
||||
// It prevents Pods being scheduled to Windows nodes in a mixed OS cluster.
|
||||
// +docs:property
|
||||
nodeSelector: {
|
||||
"kubernetes.io/os": "linux"
|
||||
}
|
||||
nodeSelector: #NodeSelector
|
||||
|
||||
// A Kubernetes Affinity, if required; see https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core
|
||||
//
|
||||
@@ -1098,7 +1092,7 @@ package holos
|
||||
|
||||
startupapicheck: {
|
||||
// Enables the startup api check
|
||||
enabled: true
|
||||
enabled: *true | false
|
||||
|
||||
// Pod Security Context to be set on the startupapicheck component Pod
|
||||
// ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
|
||||
@@ -1151,7 +1145,7 @@ package holos
|
||||
// memory: 32Mi
|
||||
//
|
||||
// ref: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
|
||||
resources: {}
|
||||
resources: #PodResources
|
||||
|
||||
// The nodeSelector on Pods tells Kubernetes to schedule Pods on the nodes with
|
||||
// matching labels.
|
||||
@@ -1160,9 +1154,7 @@ package holos
|
||||
// This default ensures that Pods are only scheduled to Linux nodes.
|
||||
// It prevents Pods being scheduled to Windows nodes in a mixed OS cluster.
|
||||
// +docs:property
|
||||
nodeSelector: {
|
||||
"kubernetes.io/os": "linux"
|
||||
}
|
||||
nodeSelector: #NodeSelector
|
||||
|
||||
// A Kubernetes Affinity, if required; see https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#affinity-v1-core
|
||||
//
|
||||
@@ -0,0 +1,78 @@
|
||||
package holos
|
||||
|
||||
// Lets Encrypt certificate issuers for public tls certs
|
||||
#InputKeys: component: "letsencrypt"
|
||||
#TargetNamespace: "cert-manager"
|
||||
|
||||
let Name = "letsencrypt"
|
||||
|
||||
// The cloudflare api token is platform scoped, not cluster scoped.
|
||||
#SecretName: "cloudflare-api-token-secret"
|
||||
|
||||
// Depends on cert manager
|
||||
#DependsOn: _CertManager
|
||||
|
||||
#KubernetesObjects & {
|
||||
apiObjects: {
|
||||
ClusterIssuer: {
|
||||
letsencrypt: #ClusterIssuer & {
|
||||
metadata: name: Name
|
||||
spec: {
|
||||
acme: {
|
||||
email: #Platform.org.contact.email
|
||||
server: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
privateKeySecretRef: name: Name
|
||||
solvers: [{
|
||||
dns01: cloudflare: {
|
||||
email: #Platform.org.cloudflare.email
|
||||
apiTokenSecretRef: name: #SecretName
|
||||
apiTokenSecretRef: key: "api_token"
|
||||
}}]
|
||||
}
|
||||
}
|
||||
}
|
||||
letsencryptStaging: #ClusterIssuer & {
|
||||
metadata: name: Name + "-staging"
|
||||
spec: {
|
||||
acme: {
|
||||
email: #Platform.org.contact.email
|
||||
server: "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
privateKeySecretRef: name: Name + "-staging"
|
||||
solvers: [{
|
||||
dns01: cloudflare: {
|
||||
email: #Platform.org.cloudflare.email
|
||||
apiTokenSecretRef: name: #SecretName
|
||||
apiTokenSecretRef: key: "api_token"
|
||||
}}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _HTTPSolvers are disabled in the provisioner cluster, dns is the method supported by holos.
|
||||
_HTTPSolvers: {
|
||||
letsencryptHTTP: #ClusterIssuer & {
|
||||
metadata: name: Name + "-http"
|
||||
spec: {
|
||||
acme: {
|
||||
email: #Platform.org.contact.email
|
||||
server: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
privateKeySecretRef: name: Name
|
||||
solvers: [{http01: ingress: class: "istio"}]
|
||||
}
|
||||
}
|
||||
}
|
||||
letsencryptHTTPStaging: #ClusterIssuer & {
|
||||
metadata: name: Name + "-http-staging"
|
||||
spec: {
|
||||
acme: {
|
||||
email: #Platform.org.contact.email
|
||||
server: "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
privateKeySecretRef: name: Name + "-staging"
|
||||
solvers: [{http01: ingress: class: "istio"}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package holos
|
||||
|
||||
// Components under this directory are part of this collection
|
||||
#InputKeys: project: "mesh"
|
||||
|
||||
// Shared dependencies for all components in this collection.
|
||||
#DependsOn: _Namespaces
|
||||
|
||||
// Common Dependencies
|
||||
_Namespaces: Namespaces: name: "\(#StageName)-secrets-namespaces"
|
||||
_CertManager: CertManager: name: "\(#InstancePrefix)-certmanager"
|
||||
_LetsEncrypt: LetsEncrypt: name: "\(#InstancePrefix)-letsencrypt"
|
||||
_Certificates: Certificates: name: "\(#InstancePrefix)-certificates"
|
||||
@@ -14,13 +14,7 @@ package holos
|
||||
}
|
||||
values: #Values
|
||||
apiObjects: {
|
||||
Issuer: {
|
||||
// https://github.com/cockroachdb/helm-charts/blob/3dcf96726ebcfe3784afb526ddcf4095a1684aea/README.md?plain=1#L196-L201
|
||||
cockroachdb: #Issuer & {
|
||||
metadata: name: #ComponentName
|
||||
metadata: namespace: #TargetNamespace
|
||||
spec: selfSigned: {}
|
||||
}
|
||||
}
|
||||
ExternalSecret: node: #ExternalSecret & {_name: "cockroachdb-node"}
|
||||
ExternalSecret: root: #ExternalSecret & {_name: "cockroachdb-root"}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,7 +478,7 @@ package holos
|
||||
copyCerts: image: "busybox"
|
||||
certs: {
|
||||
// Bring your own certs scenario. If provided, tls.init section will be ignored.
|
||||
provided: false
|
||||
provided: true | *false
|
||||
// Secret name for the client root cert.
|
||||
clientRootSecret: "cockroachdb-root"
|
||||
// Secret name for node cert.
|
||||
@@ -487,7 +487,7 @@ package holos
|
||||
caSecret: "cockroach-ca"
|
||||
// Enable if the secret is a dedicated TLS.
|
||||
// TLS secrets are created by cert-mananger, for example.
|
||||
tlsSecret: false
|
||||
tlsSecret: true | *false
|
||||
// Enable if the you want cockroach db to create its own certificates
|
||||
selfSigner: {
|
||||
// If set, the cockroach db will generate its own certificates
|
||||
|
||||
@@ -10,11 +10,9 @@ package holos
|
||||
certs: {
|
||||
// https://github.com/cockroachdb/helm-charts/blob/3dcf96726ebcfe3784afb526ddcf4095a1684aea/README.md?plain=1#L204-L215
|
||||
selfSigner: enabled: false
|
||||
certManager: true
|
||||
certManagerIssuer: {
|
||||
kind: "Issuer"
|
||||
name: #ComponentName
|
||||
}
|
||||
certManager: false
|
||||
provided: true
|
||||
tlsSecret: true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package holos
|
||||
#TargetNamespace: #InstancePrefix + "-zitadel"
|
||||
|
||||
#DB: {
|
||||
Host: "crdb-public"
|
||||
Host: "crdb-public"
|
||||
}
|
||||
|
||||
// The canonical login domain for the entire platform. Zitadel will be active on a singlec cluster at a time, but always accessible from this hostname.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package holos
|
||||
|
||||
#InputKeys: component: "zitadel"
|
||||
import "encoding/yaml"
|
||||
|
||||
let Name = "zitadel"
|
||||
#InputKeys: component: Name
|
||||
|
||||
// Upstream helm chart doesn't specify the namespace field for all resources.
|
||||
#Kustomization: spec: targetNamespace: #TargetNamespace
|
||||
@@ -8,10 +11,10 @@ package holos
|
||||
#HelmChart & {
|
||||
namespace: #TargetNamespace
|
||||
chart: {
|
||||
name: "zitadel"
|
||||
name: Name
|
||||
version: "7.9.0"
|
||||
repository: {
|
||||
name: "zitadel"
|
||||
name: Name
|
||||
url: "https://charts.zitadel.com"
|
||||
}
|
||||
}
|
||||
@@ -21,23 +24,32 @@ package holos
|
||||
ExternalSecret: masterkey: #ExternalSecret & {
|
||||
_name: "zitadel-masterkey"
|
||||
}
|
||||
Certificate: zitadel: #Certificate & {
|
||||
metadata: name: "crdb-zitadel-client"
|
||||
ExternalSecret: zitadel: #ExternalSecret & {
|
||||
_name: "cockroachdb-zitadel"
|
||||
}
|
||||
VirtualService: zitadel: #VirtualService & {
|
||||
metadata: name: Name
|
||||
metadata: namespace: #TargetNamespace
|
||||
spec: {
|
||||
commonName: "zitadel"
|
||||
issuerRef: {
|
||||
group: "cert-manager.io"
|
||||
kind: "Issuer"
|
||||
name: "crdb-ca-issuer"
|
||||
}
|
||||
privateKey: algorithm: "RSA"
|
||||
privateKey: size: 2048
|
||||
renewBefore: "48h0m0s"
|
||||
secretName: "cockroachdb-zitadel"
|
||||
subject: organizations: ["Cockroach"]
|
||||
usages: ["digital signature", "key encipherment", "client auth"]
|
||||
}
|
||||
spec: hosts: ["login.\(#Platform.org.domain)"]
|
||||
spec: gateways: ["istio-ingress/default"]
|
||||
spec: http: [{route: [{destination: host: Name}]}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Generalize this common pattern of injecting the istio sidecar into a Deployment
|
||||
let Patch = [{op: "add", path: "/spec/template/metadata/labels/sidecar.istio.io~1inject", value: "true"}]
|
||||
|
||||
#Kustomize: {
|
||||
patches: [
|
||||
{
|
||||
target: {
|
||||
group: "apps"
|
||||
version: "v1"
|
||||
kind: "Deployment"
|
||||
name: Name
|
||||
}
|
||||
patch: yaml.Marshal(Patch)
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
package holos
|
||||
|
||||
// Lets Encrypt certificate issuers for public tls certs
|
||||
#InputKeys: component: "certissuers"
|
||||
#TargetNamespace: "cert-manager"
|
||||
|
||||
let Name = "letsencrypt"
|
||||
|
||||
// The cloudflare api token is platform scoped, not cluster scoped.
|
||||
#SecretName: "cloudflare-api-token-secret"
|
||||
|
||||
// Depends on cert manager
|
||||
#DependsOn: _CertManager
|
||||
|
||||
#KubernetesObjects & {
|
||||
apiObjects: {
|
||||
ClusterIssuer: {
|
||||
letsencrypt: #ClusterIssuer & {
|
||||
metadata: name: Name
|
||||
spec: {
|
||||
acme: {
|
||||
email: #Platform.org.contact.email
|
||||
server: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
privateKeySecretRef: name: Name + "-istio"
|
||||
solvers: [{http01: ingress: class: "istio"}]
|
||||
}
|
||||
}
|
||||
}
|
||||
letsencryptStaging: #ClusterIssuer & {
|
||||
metadata: name: Name + "-staging"
|
||||
spec: {
|
||||
acme: {
|
||||
email: #Platform.org.contact.email
|
||||
server: "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
privateKeySecretRef: name: Name + "-staging-istio"
|
||||
solvers: [{http01: ingress: class: "istio"}]
|
||||
}
|
||||
}
|
||||
}
|
||||
letsencryptDns: #ClusterIssuer & {
|
||||
metadata: name: Name + "-dns"
|
||||
spec: {
|
||||
acme: {
|
||||
email: #Platform.org.contact.email
|
||||
server: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
privateKeySecretRef: name: Name + "-istio"
|
||||
solvers: [{
|
||||
dns01: cloudflare: {
|
||||
email: #Platform.org.cloudflare.email
|
||||
apiTokenSecretRef: name: #SecretName
|
||||
apiTokenSecretRef: key: "api_token"
|
||||
}}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ExternalSecret: "\(#SecretName)": #ExternalSecret & {
|
||||
_name: #SecretName
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package holos
|
||||
|
||||
// https://cert-manager.io/docs/
|
||||
|
||||
#TargetNamespace: "cert-manager"
|
||||
|
||||
#InputKeys: {
|
||||
component: "certmanager"
|
||||
service: "cert-manager"
|
||||
}
|
||||
|
||||
#HelmChart & {
|
||||
values: #UpstreamValues & {
|
||||
installCRDs: true
|
||||
}
|
||||
namespace: #TargetNamespace
|
||||
chart: {
|
||||
name: "cert-manager"
|
||||
version: "1.14.3"
|
||||
repository: {
|
||||
name: "jetstack"
|
||||
url: "https://charts.jetstack.io"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,32 +9,21 @@ let Name = "gateway"
|
||||
#TargetNamespace: "istio-ingress"
|
||||
#DependsOn: _IngressGateway
|
||||
|
||||
// TODO: We need to generalize this for multiple services hanging off the default gateway.
|
||||
let LoginCert = #Certificate & {
|
||||
metadata: {
|
||||
name: "login"
|
||||
namespace: #TargetNamespace
|
||||
}
|
||||
spec: {
|
||||
commonName: "login.\(#Platform.org.domain)"
|
||||
dnsNames: [commonName]
|
||||
secretName: metadata.name
|
||||
issuerRef: kind: "ClusterIssuer"
|
||||
issuerRef: name: "letsencrypt"
|
||||
}
|
||||
}
|
||||
let LoginCert = #PlatformCerts.login
|
||||
|
||||
#KubernetesObjects & {
|
||||
apiObjects: {
|
||||
Certificate: login: LoginCert
|
||||
ExternalSecret: login: #ExternalSecret & {
|
||||
_name: "login"
|
||||
}
|
||||
Gateway: default: #Gateway & {
|
||||
metadata: name: "default"
|
||||
metadata: name: "default"
|
||||
metadata: namespace: #TargetNamespace
|
||||
spec: selector: istio: "ingressgateway"
|
||||
spec: servers: [
|
||||
{
|
||||
hosts: ["prod-iam-zitadel/\(LoginCert.spec.commonName)"]
|
||||
port: name: "https-prod-iam-zitadel"
|
||||
hosts: [for dnsName in LoginCert.spec.dnsNames {"prod-iam-zitadel/\(dnsName)"}]
|
||||
port: name: "https-prod-iam-login"
|
||||
port: number: 443
|
||||
port: protocol: "HTTPS"
|
||||
tls: credentialName: LoginCert.spec.secretName
|
||||
|
||||
@@ -14,14 +14,13 @@ let Metadata = {
|
||||
#TargetNamespace: "istio-ingress"
|
||||
#DependsOn: _IngressGateway
|
||||
|
||||
let Cert = #HTTP01Cert & {
|
||||
_name: Name
|
||||
_secret: SecretName
|
||||
}
|
||||
let Cert = #PlatformCerts[SecretName]
|
||||
|
||||
#KubernetesObjects & {
|
||||
apiObjects: {
|
||||
Certificate: httpbin: Cert.object
|
||||
ExternalSecret: httpbin: #ExternalSecret & {
|
||||
_name: Cert.spec.secretName
|
||||
}
|
||||
Deployment: httpbin: #Deployment & {
|
||||
metadata: Metadata
|
||||
spec: selector: matchLabels: MatchLabels
|
||||
@@ -56,18 +55,18 @@ let Cert = #HTTP01Cert & {
|
||||
spec: selector: istio: "ingressgateway"
|
||||
spec: servers: [
|
||||
{
|
||||
hosts: ["\(#TargetNamespace)/\(Cert.Host)"]
|
||||
hosts: [for host in Cert.spec.dnsNames {"\(#TargetNamespace)/\(host)"}]
|
||||
port: name: "https-\(#InstanceName)"
|
||||
port: number: 443
|
||||
port: protocol: "HTTPS"
|
||||
tls: credentialName: Cert.SecretName
|
||||
tls: credentialName: Cert.spec.secretName
|
||||
tls: mode: "SIMPLE"
|
||||
},
|
||||
]
|
||||
}
|
||||
VirtualService: httpbin: #VirtualService & {
|
||||
metadata: Metadata
|
||||
spec: hosts: [Cert.Host]
|
||||
spec: hosts: [for host in Cert.spec.dnsNames {host}]
|
||||
spec: gateways: ["\(#TargetNamespace)/\(Name)"]
|
||||
spec: http: [{route: [{destination: host: Name}]}]
|
||||
}
|
||||
|
||||
@@ -14,9 +14,12 @@ import (
|
||||
crt "cert-manager.io/certificate/v1"
|
||||
gw "networking.istio.io/gateway/v1beta1"
|
||||
vs "networking.istio.io/virtualservice/v1beta1"
|
||||
kc "sigs.k8s.io/kustomize/api/types"
|
||||
"encoding/yaml"
|
||||
)
|
||||
|
||||
let ResourcesFile = "resources.yaml"
|
||||
|
||||
// _apiVersion is the version of this schema. Defines the interface between CUE output and the holos cli.
|
||||
_apiVersion: "holos.run/v1alpha1"
|
||||
|
||||
@@ -260,7 +263,7 @@ _apiVersion: "holos.run/v1alpha1"
|
||||
}
|
||||
}
|
||||
|
||||
// apiObjectsContent holds the marshalled representation of apiObjects
|
||||
// apiObjectMap holds the marshalled representation of apiObjects
|
||||
apiObjectMap: {
|
||||
for kind, v in apiObjects {
|
||||
"\(kind)": {
|
||||
@@ -340,6 +343,10 @@ _apiVersion: "holos.run/v1alpha1"
|
||||
platform: #Platform
|
||||
// instance returns the key values of the holos component instance.
|
||||
instance: #InputKeys
|
||||
// resources is the intermediate file name for api objects.
|
||||
resourcesFile: ResourcesFile
|
||||
// kustomizeFiles represents the files in a kustomize directory tree.
|
||||
kustomizeFiles: #KustomizeFiles.Files
|
||||
}
|
||||
|
||||
// #PlatformSpec is the output schema of a platform specification.
|
||||
@@ -360,6 +367,28 @@ _apiVersion: "holos.run/v1alpha1"
|
||||
...
|
||||
}
|
||||
|
||||
// #KustomizeTree represents a kustomize build.
|
||||
#KustomizeFiles: {
|
||||
Objects: {
|
||||
"kustomization.yaml": #Kustomize
|
||||
}
|
||||
// Files holds the marshaled output holos writes to the filesystem
|
||||
Files: {
|
||||
for filename, obj in Objects {
|
||||
"\(filename)": yaml.Marshal(obj)
|
||||
}
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
// kustomization.yaml
|
||||
#Kustomize: kc.#Kustomization & {
|
||||
apiVersion: "kustomize.config.k8s.io/v1beta1"
|
||||
kind: "Kustomization"
|
||||
resources: [ResourcesFile]
|
||||
...
|
||||
}
|
||||
|
||||
// By default, render kind: Skipped so holos knows to skip over intermediate cue files.
|
||||
// This enables the use of holos render ./foo/bar/baz/... when bar contains intermediary constraints which are not complete components.
|
||||
// Holos skips over these intermediary cue instances.
|
||||
|
||||
10
go.mod
10
go.mod
@@ -5,10 +5,14 @@ go 1.21.5
|
||||
require (
|
||||
cuelang.org/go v0.7.0
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/rogpeppe/go-internal v1.12.0
|
||||
github.com/spf13/cobra v1.7.0
|
||||
golang.org/x/tools v0.18.0
|
||||
k8s.io/api v0.29.2
|
||||
k8s.io/apimachinery v0.29.2
|
||||
k8s.io/client-go v0.29.2
|
||||
k8s.io/kubectl v0.29.2
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -27,7 +31,7 @@ require (
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/imdario/mergo v0.3.6 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
@@ -41,7 +45,6 @@ require (
|
||||
github.com/opencontainers/image-spec v1.1.0-rc4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/oauth2 v0.10.0 // indirect
|
||||
@@ -54,12 +57,9 @@ require (
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.29.2 // indirect
|
||||
k8s.io/klog/v2 v2.110.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
|
||||
k8s.io/kubectl v0.29.2 // indirect
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
11
go.sum
11
go.sum
@@ -46,8 +46,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJY
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
|
||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
@@ -96,8 +96,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 h1:sadMIsgmHpEOGbUs6VtHBXRR1OHevnj7hLx9ZcdNGW4=
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c=
|
||||
github.com/rogpeppe/go-internal v1.11.1-0.20231026093722-fa6a31e0812c h1:fPpdjePK1atuOg28PXfNSqgwf9I/qD1Hlo39JFwKBXk=
|
||||
github.com/rogpeppe/go-internal v1.11.1-0.20231026093722-fa6a31e0812c/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@@ -177,6 +175,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A=
|
||||
@@ -197,5 +196,5 @@ sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMm
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
||||
@@ -23,7 +23,7 @@ func makeBuildRunFunc(cfg *holos.Config) command.RunFunc {
|
||||
if result.Skip {
|
||||
continue
|
||||
}
|
||||
outs = append(outs, result.FinalOutput())
|
||||
outs = append(outs, result.AccumulatedOutput())
|
||||
}
|
||||
out := strings.Join(outs, "---\n")
|
||||
if _, err := fmt.Fprintln(cmd.OutOrStdout(), out); err != nil {
|
||||
|
||||
129
pkg/cli/preflight/gh.go
Normal file
129
pkg/cli/preflight/gh.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package preflight
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/holos-run/holos/pkg/logger"
|
||||
"github.com/holos-run/holos/pkg/util"
|
||||
"github.com/holos-run/holos/pkg/wrapper"
|
||||
)
|
||||
|
||||
type ghAuthStatusResponse string
|
||||
|
||||
// RunGhChecks runs all the preflight checks related to GitHub.
|
||||
func RunGhChecks(ctx context.Context, cfg *config) error {
|
||||
if err := cliIsInstalled(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := cliIsAuthed(ctx, cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// cliIsInstalled checks if the GitHub CLI is installed.
|
||||
func cliIsInstalled(ctx context.Context) error {
|
||||
log := logger.FromContext(ctx)
|
||||
|
||||
version, err := getGhVersion(ctx)
|
||||
if err != nil {
|
||||
log.WarnContext(ctx, "GitHub CLI (gh) not installed or not in PATH.")
|
||||
return guideToInstallGh(ctx)
|
||||
}
|
||||
|
||||
log.InfoContext(ctx, "GitHub CLI found", "gh_version", version)
|
||||
return nil
|
||||
}
|
||||
|
||||
// cliIsAuthed checks if 'gh' is authenticated. If not, 'gh auth login' is run then cliIsAuthed is called again.
|
||||
func cliIsAuthed(ctx context.Context, cfg *config) error {
|
||||
log := logger.FromContext(ctx)
|
||||
|
||||
status, err := ghAuthStatus(ctx, cfg)
|
||||
|
||||
if err != nil || !ghIsAuthenticated(status, cfg) {
|
||||
log.WarnContext(ctx, "GitHub CLI not authenticated to "+*cfg.githubInstance)
|
||||
if err := authenticateGh(ctx, cfg); err != nil {
|
||||
return wrapper.Wrap(fmt.Errorf("failed to authenticate the GitHub CLI to %v: %w", cfg.githubInstance, err))
|
||||
}
|
||||
|
||||
// Re-run this check now that gh should be authenticated.
|
||||
err := cliIsAuthed(ctx, cfg)
|
||||
return err
|
||||
}
|
||||
|
||||
log.InfoContext(ctx, "GitHub CLI is authenticated to "+*cfg.githubInstance)
|
||||
|
||||
if !ghTokenAllowsRepoCreation(status) {
|
||||
return wrapper.Wrap(fmt.Errorf("GitHub token does not have the necessary scopes to create a repository"))
|
||||
}
|
||||
|
||||
log.InfoContext(ctx, "GitHub token is able to create a repository")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ghAuthStatus runs 'gh auth status' and returns the result.
|
||||
func ghAuthStatus(ctx context.Context, cfg *config) (ghAuthStatusResponse, error) {
|
||||
log := logger.FromContext(ctx)
|
||||
out, err := util.RunCmd(ctx, "gh", "auth", "status", "--hostname="+*cfg.githubInstance)
|
||||
|
||||
var status ghAuthStatusResponse
|
||||
if err != nil {
|
||||
status = ghAuthStatusResponse(out.Stderr.String())
|
||||
} else {
|
||||
status = ghAuthStatusResponse(out.Stdout.String())
|
||||
}
|
||||
log.DebugContext(ctx, "gh auth status", "gh_auth_status", status)
|
||||
|
||||
return status, err
|
||||
}
|
||||
|
||||
// getGhVersion retrieves the version of 'gh'.
|
||||
func getGhVersion(ctx context.Context) (string, error) {
|
||||
out, err := util.RunCmd(ctx, "gh", "--version")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.Split(out.Stdout.String(), "\n")[0], nil
|
||||
}
|
||||
|
||||
// guideToInstallGh guides the user towards installing the GitHub CLI.
|
||||
func guideToInstallGh(ctx context.Context) error {
|
||||
log := logger.FromContext(ctx)
|
||||
log.WarnContext(ctx, "The GitHub CLI is required to set up Holos. To install it, follow the instructions at: https://github.com/cli/cli#installation")
|
||||
return wrapper.Wrap(fmt.Errorf("GitHub CLI is not installed"))
|
||||
}
|
||||
|
||||
// authenticateGh runs 'gh auth login' to authenticate the GitHub CLI.
|
||||
func authenticateGh(ctx context.Context, cfg *config) error {
|
||||
log := logger.FromContext(ctx)
|
||||
log.InfoContext(ctx, "Authenticating GitHub CLI with 'gh auth login --hostname="+*cfg.githubInstance+"'. Please follow the prompts.")
|
||||
|
||||
err := util.RunInteractiveCmd(ctx, "gh", "auth", "login", "--hostname="+*cfg.githubInstance)
|
||||
if err != nil {
|
||||
log.ErrorContext(ctx, "Failed to authenticate GitHub CLI")
|
||||
return wrapper.Wrap(fmt.Errorf("failed to authenticate GitHub CLI: %w", err))
|
||||
}
|
||||
|
||||
log.InfoContext(ctx, "GitHub CLI has been authenticated.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ghIsAuthenticated checks if the GitHub CLI is authenticated and logged in to githubInstance.
|
||||
func ghIsAuthenticated(status ghAuthStatusResponse, cfg *config) bool {
|
||||
return strings.Contains(string(status), "Logged in to "+*cfg.githubInstance)
|
||||
}
|
||||
|
||||
// ghTokenAllowsRepoCreation validates that the GitHub CLI is authenticated
|
||||
// with a token that allows repository creation. This is a naive implementation
|
||||
// that just checks the output of 'gh auth status' for the presence of the
|
||||
// 'repo' scope. Note that the 'repo' scope is sufficient to create a secret in
|
||||
// a repository, so this check also covers that.
|
||||
// Example token scope line: "- Token scopes: 'admin:public_key', 'gist', 'read:org', 'repo'"
|
||||
func ghTokenAllowsRepoCreation(status ghAuthStatusResponse) bool {
|
||||
return strings.Contains(string(status), "'repo'")
|
||||
}
|
||||
36
pkg/cli/preflight/gh_test.go
Normal file
36
pkg/cli/preflight/gh_test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package preflight
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGhTokenAllowsRepoCreation(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
status ghAuthStatusResponse
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "token has necessary scopes",
|
||||
status: "- Token: gho_************************************\n- Token scopes: 'gist', 'read:org', 'repo'",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "token has necessary scopes",
|
||||
status: " - Token scopes: 'gist', 'read:org', 'repo'",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "token does not have necessary scopes",
|
||||
status: "- Token: gho_************************************\n- Token scopes: 'gist', 'read:org'",
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := ghTokenAllowsRepoCreation(tc.status)
|
||||
if result != tc.expected {
|
||||
t.Errorf("expected %v, got %v", tc.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
55
pkg/cli/preflight/preflight.go
Normal file
55
pkg/cli/preflight/preflight.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package preflight
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/holos-run/holos/pkg/cli/command"
|
||||
"github.com/holos-run/holos/pkg/holos"
|
||||
"github.com/holos-run/holos/pkg/logger"
|
||||
)
|
||||
|
||||
// Config holds configuration parameters for preflight checks.
|
||||
type config struct {
|
||||
githubInstance *string
|
||||
}
|
||||
|
||||
// Build the shared configuration and flagset for the preflight command.
|
||||
func newConfig() (*config, *flag.FlagSet) {
|
||||
cfg := &config{}
|
||||
flagSet := flag.NewFlagSet("", flag.ContinueOnError)
|
||||
cfg.githubInstance = flagSet.String("github-instance", "github.com", "Address of the GitHub instance you want to use")
|
||||
|
||||
return cfg, flagSet
|
||||
}
|
||||
|
||||
// New returns the preflight command for the root command.
|
||||
func New(hc *holos.Config) *cobra.Command {
|
||||
cfg, flagSet := newConfig()
|
||||
|
||||
cmd := command.New("preflight")
|
||||
cmd.Short = "Run preflight checks to ensure you're ready to use Holos"
|
||||
cmd.Flags().AddGoFlagSet(flagSet)
|
||||
cmd.RunE = makePreflightRunFunc(hc, cfg)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// makePreflightRunFunc returns the internal implementation of the preflight cli command.
|
||||
func makePreflightRunFunc(_ *holos.Config, cfg *config) command.RunFunc {
|
||||
return func(cmd *cobra.Command, _ []string) error {
|
||||
ctx := cmd.Context()
|
||||
log := logger.FromContext(ctx)
|
||||
log.Info("Starting preflight checks")
|
||||
|
||||
// GitHub checks
|
||||
if err := RunGhChecks(ctx, cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
// Other checks can be added here
|
||||
|
||||
log.Info("Preflight checks complete. Ready to use Holos 🚀")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ func makeRenderRunFunc(cfg *holos.Config) command.RunFunc {
|
||||
}
|
||||
// API Objects
|
||||
path := result.Filename(cfg.WriteTo(), cfg.ClusterName())
|
||||
if err := result.Save(ctx, path, result.FinalOutput()); err != nil {
|
||||
if err := result.Save(ctx, path, result.AccumulatedOutput()); err != nil {
|
||||
return wrapper.Wrap(err)
|
||||
}
|
||||
// Kustomization
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/holos-run/holos/pkg/cli/build"
|
||||
"github.com/holos-run/holos/pkg/cli/create"
|
||||
"github.com/holos-run/holos/pkg/cli/get"
|
||||
"github.com/holos-run/holos/pkg/cli/kv"
|
||||
"github.com/holos-run/holos/pkg/cli/preflight"
|
||||
"github.com/holos-run/holos/pkg/cli/render"
|
||||
"github.com/holos-run/holos/pkg/cli/txtar"
|
||||
"github.com/holos-run/holos/pkg/holos"
|
||||
"github.com/holos-run/holos/pkg/logger"
|
||||
"github.com/holos-run/holos/pkg/version"
|
||||
"github.com/spf13/cobra"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// New returns a new root *cobra.Command for command line execution.
|
||||
@@ -51,6 +54,7 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
rootCmd.AddCommand(render.New(cfg))
|
||||
rootCmd.AddCommand(get.New(cfg))
|
||||
rootCmd.AddCommand(create.New(cfg))
|
||||
rootCmd.AddCommand(preflight.New(cfg))
|
||||
|
||||
// Maybe not needed?
|
||||
rootCmd.AddCommand(txtar.New(cfg))
|
||||
|
||||
@@ -2,10 +2,12 @@ package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/holos-run/holos/pkg/cli/command"
|
||||
"github.com/holos-run/holos/pkg/holos"
|
||||
"github.com/holos-run/holos/pkg/logger"
|
||||
"github.com/holos-run/holos/pkg/util"
|
||||
"github.com/holos-run/holos/pkg/wrapper"
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -90,17 +92,24 @@ func makeGetRunFunc(hc *holos.Config, cfg *config) command.RunFunc {
|
||||
|
||||
printFile := *cfg.printFile
|
||||
if len(toExtract) == 0 {
|
||||
if printFile == "" {
|
||||
printFile = secretName
|
||||
}
|
||||
}
|
||||
|
||||
if printFile != "" {
|
||||
if data, found := secret.Data[printFile]; found {
|
||||
hc.Write(data)
|
||||
} else {
|
||||
err := fmt.Errorf("cannot print: want %s have %v: did you mean --extract-all or --%s=name", printFile, keys, printFlagName)
|
||||
return wrapper.Wrap(err)
|
||||
if printFile == "" { // print all data keys
|
||||
data := make(map[string]string)
|
||||
for k, v := range secret.Data {
|
||||
data[k] = string(v)
|
||||
}
|
||||
b, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return wrapper.Wrap(err)
|
||||
}
|
||||
b = util.EnsureNewline(b)
|
||||
hc.Write(b)
|
||||
} else { // print named data keys keys
|
||||
if data, found := secret.Data[printFile]; found {
|
||||
hc.Write(data)
|
||||
} else {
|
||||
err := fmt.Errorf("cannot print: want %s have %v: did you mean --extract-all or --%s=name", printFile, keys, printFlagName)
|
||||
return wrapper.Wrap(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,30 @@ var secret = v1.Secret{
|
||||
Type: "Opaque",
|
||||
}
|
||||
|
||||
var loginSecret = v1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Secret",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "zitadel-admin-d7fgbgbfbt",
|
||||
Namespace: "secrets",
|
||||
Labels: map[string]string{
|
||||
"holos.run/owner.name": "jeff",
|
||||
"holos.run/secret.name": "zitadel-admin",
|
||||
},
|
||||
CreationTimestamp: metav1.Time{
|
||||
Time: time.Date(2021, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"url": []byte("https://login.example.com"),
|
||||
"username": []byte("zitadel-admin@zitadel.login.example.com"),
|
||||
"password": []byte("Password1!"),
|
||||
},
|
||||
Type: "Opaque",
|
||||
}
|
||||
|
||||
// cmdHolos executes the holos root command with a kubernetes.Interface that
|
||||
// persists for the duration of the testscript. holos is NOT executed in a
|
||||
// subprocess, the current working directory is not and should not be changed.
|
||||
@@ -72,7 +96,7 @@ func TestSecrets(t *testing.T) {
|
||||
testscript.Run(t, testscript.Params{
|
||||
Dir: "testdata",
|
||||
Setup: func(env *testscript.Env) error {
|
||||
env.Values[clientsetKey] = fake.NewSimpleClientset(&secret)
|
||||
env.Values[clientsetKey] = fake.NewSimpleClientset(&secret, &loginSecret)
|
||||
return nil
|
||||
},
|
||||
Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){
|
||||
|
||||
7
pkg/cli/secret/testdata/issue34_print_data.txt
vendored
Normal file
7
pkg/cli/secret/testdata/issue34_print_data.txt
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Print the data key by default
|
||||
holos get secret zitadel-admin
|
||||
stdout '^{$'
|
||||
stdout '^ "url": "https://login.example.com"'
|
||||
stdout '^ "username": "zitadel-admin@zitadel.login.example.com"'
|
||||
stdout '^ "password": "Password1!"'
|
||||
stdout '^}$'
|
||||
@@ -4,23 +4,22 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"cuelang.org/go/cue/build"
|
||||
"fmt"
|
||||
"github.com/holos-run/holos"
|
||||
"github.com/holos-run/holos/pkg/logger"
|
||||
"github.com/holos-run/holos/pkg/util"
|
||||
"github.com/holos-run/holos/pkg/wrapper"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"cuelang.org/go/cue/build"
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"cuelang.org/go/cue/load"
|
||||
|
||||
"github.com/holos-run/holos"
|
||||
"github.com/holos-run/holos/pkg/logger"
|
||||
"github.com/holos-run/holos/pkg/util"
|
||||
"github.com/holos-run/holos/pkg/wrapper"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -82,13 +81,21 @@ type Metadata struct {
|
||||
// holos cli. A map is used to improve the clarity of error messages from cue.
|
||||
type apiObjectMap map[string]map[string]string
|
||||
|
||||
// fileContentMap is a map of file names to file contents.
|
||||
type fileContentMap map[string]string
|
||||
|
||||
// Result is the build result for display or writing.
|
||||
type Result struct {
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
KsContent string `json:"ksContent,omitempty"`
|
||||
APIObjectMap apiObjectMap `json:"apiObjectMap,omitempty"`
|
||||
finalOutput string
|
||||
Skip bool
|
||||
Metadata Metadata `json:"metadata,omitempty"`
|
||||
KsContent string `json:"ksContent,omitempty"`
|
||||
// APIObjectMap holds the marshalled representation of api objects.
|
||||
APIObjectMap apiObjectMap `json:"apiObjectMap,omitempty"`
|
||||
accumulatedOutput string
|
||||
Skip bool
|
||||
// KustomizeFiles holds the files for a kustomize kustomization directory.
|
||||
KustomizeFiles fileContentMap `json:"kustomizeFiles"`
|
||||
// ResourcesFile is the file name used for api objects in kustomization.yaml
|
||||
ResourcesFile string `json:"resourcesFile,omitempty"`
|
||||
}
|
||||
|
||||
type Repository struct {
|
||||
@@ -104,14 +111,15 @@ type Chart struct {
|
||||
|
||||
// A HelmChart represents a helm command to provide chart values in order to render kubernetes api objects.
|
||||
type HelmChart struct {
|
||||
APIVersion string `json:"apiVersion"`
|
||||
Kind string `json:"kind"`
|
||||
Metadata Metadata `json:"metadata"`
|
||||
KsContent string `json:"ksContent"`
|
||||
Namespace string `json:"namespace"`
|
||||
Chart Chart `json:"chart"`
|
||||
ValuesContent string `json:"valuesContent"`
|
||||
APIObjectMap apiObjectMap `json:"APIObjectMap"`
|
||||
APIVersion string `json:"apiVersion"`
|
||||
Kind string `json:"kind"`
|
||||
Metadata Metadata `json:"metadata"`
|
||||
KsContent string `json:"ksContent"`
|
||||
Namespace string `json:"namespace"`
|
||||
Chart Chart `json:"chart"`
|
||||
ValuesContent string `json:"valuesContent"`
|
||||
// APIObjectMap holds the marshalled representation of api objects.
|
||||
APIObjectMap apiObjectMap `json:"APIObjectMap"`
|
||||
}
|
||||
|
||||
// Name returns the metadata name of the result. Equivalent to the
|
||||
@@ -128,14 +136,14 @@ func (r *Result) KustomizationFilename(writeTo string, cluster string) string {
|
||||
return filepath.Join(writeTo, "clusters", cluster, "holos", "components", r.Name()+"-kustomization.gen.yaml")
|
||||
}
|
||||
|
||||
// FinalOutput returns the final rendered output.
|
||||
func (r *Result) FinalOutput() string {
|
||||
return r.finalOutput
|
||||
// AccumulatedOutput returns the accumulated rendered output.
|
||||
func (r *Result) AccumulatedOutput() string {
|
||||
return r.accumulatedOutput
|
||||
}
|
||||
|
||||
// addAPIObjects adds the overlay api objects to finalOutput.
|
||||
// addAPIObjects adds the overlay api objects to accumulatedOutput.
|
||||
func (r *Result) addOverlayObjects(log *slog.Logger) {
|
||||
b := []byte(r.FinalOutput())
|
||||
b := []byte(r.AccumulatedOutput())
|
||||
kinds := make([]string, 0, len(r.APIObjectMap))
|
||||
// Sort the keys
|
||||
for kind := range r.APIObjectMap {
|
||||
@@ -155,13 +163,64 @@ func (r *Result) addOverlayObjects(log *slog.Logger) {
|
||||
for _, name := range names {
|
||||
yamlString := v[name]
|
||||
log.Debug(fmt.Sprintf("%s/%s", kind, name), "kind", kind, "name", name)
|
||||
util.EnsureNewline(b)
|
||||
b = util.EnsureNewline(b)
|
||||
header := fmt.Sprintf("---\n# Source: CUE apiObjects.%s.%s\n", kind, name)
|
||||
b = append(b, []byte(header+yamlString)...)
|
||||
util.EnsureNewline(b)
|
||||
b = util.EnsureNewline(b)
|
||||
}
|
||||
}
|
||||
r.finalOutput = string(b)
|
||||
r.accumulatedOutput = string(b)
|
||||
}
|
||||
|
||||
// kustomize replaces the final output with the output of kustomize build if the
|
||||
func (r *Result) kustomize(ctx context.Context) error {
|
||||
log := logger.FromContext(ctx)
|
||||
if r.ResourcesFile == "" {
|
||||
log.DebugContext(ctx, "skipping kustomize: no resourcesFile")
|
||||
return nil
|
||||
}
|
||||
if len(r.KustomizeFiles) < 1 {
|
||||
log.DebugContext(ctx, "skipping kustomize: no kustomizeFiles")
|
||||
return nil
|
||||
}
|
||||
tempDir, err := os.MkdirTemp("", "holos.kustomize")
|
||||
if err != nil {
|
||||
return wrapper.Wrap(err)
|
||||
}
|
||||
defer remove(ctx, tempDir)
|
||||
|
||||
// Write the main api object resources file for kustomize.
|
||||
target := filepath.Join(tempDir, r.ResourcesFile)
|
||||
b := []byte(r.AccumulatedOutput())
|
||||
b = util.EnsureNewline(b)
|
||||
if err := os.WriteFile(target, b, 0644); err != nil {
|
||||
return wrapper.Wrap(fmt.Errorf("could not write resources: %w", err))
|
||||
}
|
||||
log.DebugContext(ctx, "wrote: "+target, "op", "write", "path", target, "bytes", len(b))
|
||||
|
||||
// Write the kustomization tree, kustomization.yaml must be in this map for kustomize to work.
|
||||
for file, content := range r.KustomizeFiles {
|
||||
target := filepath.Join(tempDir, file)
|
||||
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
|
||||
return wrapper.Wrap(err)
|
||||
}
|
||||
b := []byte(content)
|
||||
b = util.EnsureNewline(b)
|
||||
if err := os.WriteFile(target, b, 0644); err != nil {
|
||||
return wrapper.Wrap(fmt.Errorf("could not write: %w", err))
|
||||
}
|
||||
log.DebugContext(ctx, "wrote: "+target, "op", "write", "path", target, "bytes", len(b))
|
||||
}
|
||||
|
||||
// Run kustomize.
|
||||
kOut, err := util.RunCmd(ctx, "kubectl", "kustomize", tempDir)
|
||||
if err != nil {
|
||||
log.ErrorContext(ctx, kOut.Stderr.String())
|
||||
return wrapper.Wrap(err)
|
||||
}
|
||||
// Replace the accumulated output
|
||||
r.accumulatedOutput = kOut.Stdout.String()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save writes the content to the filesystem for git ops.
|
||||
@@ -278,6 +337,9 @@ func (b *Builder) Run(ctx context.Context) (results []*Result, err error) {
|
||||
return nil, err
|
||||
}
|
||||
result.addOverlayObjects(log)
|
||||
if err := result.kustomize(ctx); err != nil {
|
||||
return nil, wrapper.Wrap(fmt.Errorf("could not kustomize: %w", err))
|
||||
}
|
||||
default:
|
||||
return nil, wrapper.Wrap(fmt.Errorf("build kind not implemented: %v", kind))
|
||||
}
|
||||
@@ -316,25 +378,6 @@ func (b *Builder) findCueMod() (dir holos.PathCueMod, err error) {
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
type runResult struct {
|
||||
stdout *bytes.Buffer
|
||||
stderr *bytes.Buffer
|
||||
}
|
||||
|
||||
func runCmd(ctx context.Context, name string, args ...string) (result runResult, err error) {
|
||||
result = runResult{
|
||||
stdout: new(bytes.Buffer),
|
||||
stderr: new(bytes.Buffer),
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, name, args...)
|
||||
cmd.Stdout = result.stdout
|
||||
cmd.Stderr = result.stderr
|
||||
log := logger.FromContext(ctx)
|
||||
log.DebugContext(ctx, "run: "+name, "name", name, "args", args)
|
||||
err = cmd.Run()
|
||||
return
|
||||
}
|
||||
|
||||
// runHelm provides the values produced by CUE to helm template and returns
|
||||
// the rendered kubernetes api objects in the result.
|
||||
func runHelm(ctx context.Context, hc *HelmChart, r *Result, path holos.PathComponent) error {
|
||||
@@ -348,15 +391,15 @@ func runHelm(ctx context.Context, hc *HelmChart, r *Result, path holos.PathCompo
|
||||
if isNotExist(cachedChartPath) {
|
||||
// Add repositories
|
||||
repo := hc.Chart.Repository
|
||||
out, err := runCmd(ctx, "helm", "repo", "add", repo.Name, repo.URL)
|
||||
out, err := util.RunCmd(ctx, "helm", "repo", "add", repo.Name, repo.URL)
|
||||
if err != nil {
|
||||
log.ErrorContext(ctx, "could not run helm", "stderr", out.stderr.String(), "stdout", out.stdout.String())
|
||||
log.ErrorContext(ctx, "could not run helm", "stderr", out.Stderr.String(), "stdout", out.Stdout.String())
|
||||
return wrapper.Wrap(fmt.Errorf("could not run helm repo add: %w", err))
|
||||
}
|
||||
// Update repository
|
||||
out, err = runCmd(ctx, "helm", "repo", "update", repo.Name)
|
||||
out, err = util.RunCmd(ctx, "helm", "repo", "update", repo.Name)
|
||||
if err != nil {
|
||||
log.ErrorContext(ctx, "could not run helm", "stderr", out.stderr.String(), "stdout", out.stdout.String())
|
||||
log.ErrorContext(ctx, "could not run helm", "stderr", out.Stderr.String(), "stdout", out.Stdout.String())
|
||||
return wrapper.Wrap(fmt.Errorf("could not run helm repo update: %w", err))
|
||||
}
|
||||
// Cache the chart
|
||||
@@ -380,9 +423,9 @@ func runHelm(ctx context.Context, hc *HelmChart, r *Result, path holos.PathCompo
|
||||
|
||||
// Run charts
|
||||
chart := hc.Chart
|
||||
helmOut, err := runCmd(ctx, "helm", "template", "--values", valuesPath, "--namespace", hc.Namespace, "--kubeconfig", "/dev/null", "--version", chart.Version, chart.Name, cachedChartPath)
|
||||
helmOut, err := util.RunCmd(ctx, "helm", "template", "--values", valuesPath, "--namespace", hc.Namespace, "--kubeconfig", "/dev/null", "--version", chart.Version, chart.Name, cachedChartPath)
|
||||
if err != nil {
|
||||
stderr := helmOut.stderr.String()
|
||||
stderr := helmOut.Stderr.String()
|
||||
lines := strings.Split(stderr, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "Error:") {
|
||||
@@ -392,7 +435,7 @@ func runHelm(ctx context.Context, hc *HelmChart, r *Result, path holos.PathCompo
|
||||
return wrapper.Wrap(fmt.Errorf("could not run helm template: %w", err))
|
||||
}
|
||||
|
||||
r.finalOutput = helmOut.stdout.String()
|
||||
r.accumulatedOutput = helmOut.Stdout.String()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -423,11 +466,11 @@ func cacheChart(ctx context.Context, path holos.PathComponent, chartDir string,
|
||||
defer remove(ctx, cacheTemp)
|
||||
|
||||
chartName := fmt.Sprintf("%s/%s", chart.Repository.Name, chart.Name)
|
||||
helmOut, err := runCmd(ctx, "helm", "pull", "--destination", cacheTemp, "--untar=true", "--version", chart.Version, chartName)
|
||||
helmOut, err := util.RunCmd(ctx, "helm", "pull", "--destination", cacheTemp, "--untar=true", "--version", chart.Version, chartName)
|
||||
if err != nil {
|
||||
return wrapper.Wrap(fmt.Errorf("could not run helm pull: %w", err))
|
||||
}
|
||||
log.Debug("helm pull", "stdout", helmOut.stdout, "stderr", helmOut.stderr)
|
||||
log.Debug("helm pull", "stdout", helmOut.Stdout, "stderr", helmOut.Stderr)
|
||||
|
||||
cachePath := filepath.Join(string(path), chartDir)
|
||||
if err := os.Rename(cacheTemp, cachePath); err != nil {
|
||||
|
||||
56
pkg/util/run.go
Normal file
56
pkg/util/run.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/holos-run/holos/pkg/logger"
|
||||
)
|
||||
|
||||
// runResult holds the stdout and stderr of a command.
|
||||
type RunResult struct {
|
||||
Stdout *bytes.Buffer
|
||||
Stderr *bytes.Buffer
|
||||
}
|
||||
|
||||
// RunCmd runs a command within a context, captures its output, provides debug
|
||||
// logging, and returns the result.
|
||||
// Example:
|
||||
//
|
||||
// result, err := RunCmd(ctx, "echo", "hello")
|
||||
// if err != nil {
|
||||
// return wrapper.Wrap(err)
|
||||
// }
|
||||
// fmt.Println(result.Stdout.String())
|
||||
//
|
||||
// Output:
|
||||
//
|
||||
// "hello\n"
|
||||
func RunCmd(ctx context.Context, name string, args ...string) (result RunResult, err error) {
|
||||
result = RunResult{
|
||||
Stdout: new(bytes.Buffer),
|
||||
Stderr: new(bytes.Buffer),
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, name, args...)
|
||||
cmd.Stdout = result.Stdout
|
||||
cmd.Stderr = result.Stderr
|
||||
log := logger.FromContext(ctx)
|
||||
log.DebugContext(ctx, "running: "+name, "name", name, "args", args)
|
||||
err = cmd.Run()
|
||||
return result, err
|
||||
}
|
||||
|
||||
// RunInteractiveCmd runs a command within a context but allows the command to
|
||||
// accept stdin interactively from the user. The caller is expected to handle
|
||||
// errors.
|
||||
func RunInteractiveCmd(ctx context.Context, name string, args ...string) error {
|
||||
cmd := exec.CommandContext(ctx, name, args...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
log := logger.FromContext(ctx)
|
||||
log.DebugContext(ctx, "running: "+name, "name", name, "args", args)
|
||||
return cmd.Run()
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
50
|
||||
53
|
||||
|
||||
Reference in New Issue
Block a user