зеркало из https://github.com/Azure/orkestra.git
Nitishm/rework/combine app into appgrp (#68)
* Application type and app reconciler deprecated BREAKING CHANGES: - No longer supporting Application type and application reconciler - Application spec has been merged into ApplicationGroup API - No changes to behavior other than revised spec and sample YAMLs Signed-off-by: nitishm <nitishm@microsoft.com> * Update Tiltfile to use appgroup only Signed-off-by: nitishm <nitishm@microsoft.com> * Rename DAG to Application Renamed DAG struct to Application Renamed DAGData struct to DAG Signed-off-by: nitishm <nitishm@microsoft.com> * Updated docs Signed-off-by: nitishm <nitishm@microsoft.com> * Fix lint error Signed-off-by: nitishm <nitishm@microsoft.com> Co-authored-by: nitishm <nitishm@microsoft.com>
This commit is contained in:
Родитель
2cbe7fcda9
Коммит
3d5603dddb
3
PROJECT
3
PROJECT
|
@ -1,9 +1,6 @@
|
|||
domain: azure.microsoft.com
|
||||
repo: github.com/Azure/Orkestra
|
||||
resources:
|
||||
- group: orkestra
|
||||
kind: Application
|
||||
version: v1alpha1
|
||||
- group: orkestra
|
||||
kind: ApplicationGroup
|
||||
version: v1alpha1
|
||||
|
|
10
README.md
10
README.md
|
@ -27,7 +27,7 @@ For getting started you will need,
|
|||
- `helm` - Helm client
|
||||
- (_optional_) `argo` - Argo workflow client (follow the instructions to install the binary at https://github.com/argoproj/argo/releases)
|
||||
|
||||
Install the `ApplicationGroup` and `Application` custom resource definitions (CRDs) using `make install`
|
||||
Install the `ApplicationGroup` and custom resource definitions (CRDs) using `make install`
|
||||
|
||||
```console
|
||||
/home/nitishm/go/bin/controller-gen "crd:trivialVersions=true" rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
|
||||
|
@ -112,9 +112,9 @@ To solve the complex application orchestration problem Orkestra builds a [Direct
|
|||
|
||||
<p align="center"><img src="./assets/orkestra-core.png" width="750x" /></p>
|
||||
|
||||
1. Submit `Application` and `ApplicationGroup` CRs
|
||||
2. For each `Application` in `ApplicationGroup` download Helm chart from “primary” Helm Registry
|
||||
3. (*optional) For each dependency in the `Application` chart, if dependency chart is embedded in `charts/` directory, push to ”staging” Helm Registry (Chart-museum).
|
||||
1. Submit `ApplicationGroup` CRs
|
||||
2. For each application in `ApplicationGroup` download Helm chart from “primary” Helm Registry
|
||||
3. (*optional) For each dependency in the Application chart, if dependency chart is embedded in `charts/` directory, push to ”staging” Helm Registry (Chart-museum).
|
||||
4. Generate and submit Argo Workflow DAG
|
||||
5. (Executor nodes only) Submit and probe deployment state of `HelmRelease` CR.
|
||||
6. Fetch and deploy Helm charts referred to by each `HelmRelease` CR to the Kubernetes cluster.
|
||||
|
@ -140,7 +140,7 @@ Try out the examples in [examples](./examples)
|
|||
|
||||
### Functional
|
||||
|
||||
- [ ] Handling of `Application`, `ApplicationGroup` UPDATE & DELETE reconcilation events : [#64](https://github.com/Azure/Orkestra/issues/64), [#59](https://github.com/Azure/Orkestra/issues/59)
|
||||
- [ ] Handling of `ApplicationGroup` UPDATE & DELETE reconcilation events : [#64](https://github.com/Azure/Orkestra/issues/64), [#59](https://github.com/Azure/Orkestra/issues/59)
|
||||
|
||||
### Features
|
||||
|
||||
|
|
2
Tiltfile
2
Tiltfile
|
@ -25,4 +25,4 @@ yaml = helm(
|
|||
|
||||
k8s_yaml(yaml,allow_duplicates=True)
|
||||
|
||||
k8s_yaml(['./config/samples/dev-applicationgroup.yaml', './config/samples/kafka-dev-application.yaml', './config/samples/redis-dev-application.yaml'],allow_duplicates=True)
|
||||
k8s_yaml(['./config/samples/dev-applicationgroup.yaml'],allow_duplicates=True)
|
|
@ -4,26 +4,72 @@
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
helmopv1 "github.com/fluxcd/helm-operator/pkg/apis/helm.fluxcd.io/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
// ApplicationSpec defines the desired state of Application
|
||||
type ApplicationSpec struct {
|
||||
// Namespace to which the HelmRelease object will be deployed
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
Subcharts []DAG `json:"subcharts,omitempty"`
|
||||
GroupID string `json:"groupID,omitempty"`
|
||||
// ChartRepoNickname is used to lookup the repository config in the registries config map
|
||||
ChartRepoNickname string `json:"repo,omitempty"`
|
||||
// XXX (nitishm) **IMPORTANT**: DO NOT USE HelmReleaseSpec.Values!!!
|
||||
// ApplicationSpec.Overlays field replaces HelmReleaseSpec.Values field.
|
||||
// Setting the HelmReleaseSpec.Values field will not reflect in the deployed Application object
|
||||
//
|
||||
// Explanation
|
||||
// ===========
|
||||
// HelmValues uses a map[string]interface{} structure for holding helm values Data.
|
||||
// kubebuilder prunes the field value when deploying the Application resource as it considers the field to be an
|
||||
// Unknown field. HelmOperator v1 being in maintenance mode, we do not expect them to merge PRs
|
||||
// to add the +kubebuilder:pruning:PreserveUnknownFields
|
||||
// https://github.com/fluxcd/helm-operator/issues/585
|
||||
|
||||
// +kubebuilder:pruning:PreserveUnknownFields
|
||||
// +kubebuilder:validation:XPreserveUnknownFields
|
||||
Overlays helmopv1.HelmValues `json:"overlays,omitempty"`
|
||||
|
||||
// RepoPath provides the subdir path to the actual chart artifact within a Helm Registry
|
||||
// Artifactory for instance utilizes folders to store charts
|
||||
RepoPath string `json:"repoPath,omitempty"`
|
||||
|
||||
helmopv1.HelmReleaseSpec `json:",inline"`
|
||||
}
|
||||
|
||||
// ChartStatus denotes the current status of the Application Reconciliation
|
||||
type ChartStatus struct {
|
||||
Error string `json:"error,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Staged bool `json:"staged,omitempty"`
|
||||
}
|
||||
|
||||
// ApplicationGroupSpec defines the desired state of ApplicationGroup
|
||||
type ApplicationGroupSpec struct {
|
||||
Applications []DAG `json:"applications,omitempty"`
|
||||
Applications []Application `json:"applications,omitempty"`
|
||||
}
|
||||
|
||||
type Application struct {
|
||||
DAG `json:",inline"`
|
||||
Spec ApplicationSpec `json:"spec,omitempty"`
|
||||
}
|
||||
type DAG struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Dependencies []string `json:"dependencies,omitempty"`
|
||||
}
|
||||
|
||||
type ApplicationStatus struct {
|
||||
Name string `json:"name"`
|
||||
ChartStatus `json:",inline"`
|
||||
Subcharts map[string]ChartStatus `json:"subcharts,omitempty"`
|
||||
}
|
||||
|
||||
// ApplicationGroupStatus defines the observed state of ApplicationGroup
|
||||
type ApplicationGroupStatus struct {
|
||||
Applications []ApplicationStatus `json:"status,omitempty"`
|
||||
Ready bool `json:"ready"`
|
||||
Ready bool `json:"ready,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
helmopv1 "github.com/fluxcd/helm-operator/pkg/apis/helm.fluxcd.io/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
|
||||
// ApplicationSpec defines the desired state of Application
|
||||
type ApplicationSpec struct {
|
||||
// Namespace to which the HelmRelease object will be deployed
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
Subcharts []DAG `json:"subcharts,omitempty"`
|
||||
GroupID string `json:"groupID,omitempty"`
|
||||
// ChartRepoNickname is used to lookup the repository config in the registries config map
|
||||
ChartRepoNickname string `json:"repo,omitempty"`
|
||||
// XXX (nitishm) **IMPORTANT**: DO NOT USE HelmReleaseSpec.Values!!!
|
||||
// ApplicationSpec.Overlays field replaces HelmReleaseSpec.Values field.
|
||||
// Setting the HelmReleaseSpec.Values field will not reflect in the deployed Application object
|
||||
//
|
||||
// Explanation
|
||||
// ===========
|
||||
// HelmValues uses a map[string]interface{} structure for holding helm values Data.
|
||||
// kubebuilder prunes the field value when deploying the Application resource as it considers the field to be an
|
||||
// Unknown field. HelmOperator v1 being in maintenance mode, we do not expect them to merge PRs
|
||||
// to add the +kubebuilder:pruning:PreserveUnknownFields
|
||||
// https://github.com/fluxcd/helm-operator/issues/585
|
||||
|
||||
// +kubebuilder:pruning:PreserveUnknownFields
|
||||
// +kubebuilder:validation:XPreserveUnknownFields
|
||||
Overlays helmopv1.HelmValues `json:"overlays,omitempty"`
|
||||
|
||||
// RepoPath provides the subdir path to the actual chart artifact within a Helm Registry
|
||||
// Artifactory for instance utilizes folders to store charts
|
||||
RepoPath string `json:"repoPath,omitempty"`
|
||||
|
||||
helmopv1.HelmReleaseSpec `json:",inline"`
|
||||
}
|
||||
|
||||
// ChartStatus denotes the current status of the Application Reconciliation
|
||||
type ChartStatus struct {
|
||||
Ready bool `json:"ready,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Staged bool `json:"staged,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// ApplicationStatus defines the observed state of Application
|
||||
type ApplicationStatus struct {
|
||||
Name string `json:"name"`
|
||||
Application ChartStatus `json:"application"`
|
||||
Subcharts map[string]ChartStatus `json:"subcharts"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:path=applications,scope=Cluster
|
||||
// +kubebuilder:subresource:status
|
||||
|
||||
// Application is the Schema for the applications API
|
||||
type Application struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ApplicationSpec `json:"spec,omitempty"`
|
||||
Status ApplicationStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// ApplicationList contains a list of Application
|
||||
type ApplicationList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []Application `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&Application{}, &ApplicationList{})
|
||||
}
|
|
@ -14,10 +14,8 @@ import (
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Application) DeepCopyInto(out *Application) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.DAG.DeepCopyInto(&out.DAG)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Application.
|
||||
|
@ -30,14 +28,6 @@ func (in *Application) DeepCopy() *Application {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Application) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ApplicationGroup) DeepCopyInto(out *ApplicationGroup) {
|
||||
*out = *in
|
||||
|
@ -102,7 +92,7 @@ func (in *ApplicationGroupSpec) DeepCopyInto(out *ApplicationGroupSpec) {
|
|||
*out = *in
|
||||
if in.Applications != nil {
|
||||
in, out := &in.Applications, &out.Applications
|
||||
*out = make([]DAG, len(*in))
|
||||
*out = make([]Application, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
|
@ -141,38 +131,6 @@ func (in *ApplicationGroupStatus) DeepCopy() *ApplicationGroupStatus {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ApplicationList) DeepCopyInto(out *ApplicationList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Application, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationList.
|
||||
func (in *ApplicationList) DeepCopy() *ApplicationList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ApplicationList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ApplicationList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ApplicationSpec) DeepCopyInto(out *ApplicationSpec) {
|
||||
*out = *in
|
||||
|
@ -200,7 +158,7 @@ func (in *ApplicationSpec) DeepCopy() *ApplicationSpec {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ApplicationStatus) DeepCopyInto(out *ApplicationStatus) {
|
||||
*out = *in
|
||||
out.Application = in.Application
|
||||
out.ChartStatus = in.ChartStatus
|
||||
if in.Subcharts != nil {
|
||||
in, out := &in.Subcharts, &out.Subcharts
|
||||
*out = make(map[string]ChartStatus, len(*in))
|
||||
|
|
Двоичные данные
assets/orkestra-core.png
Двоичные данные
assets/orkestra-core.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 282 KiB После Ширина: | Высота: | Размер: 276 KiB |
|
@ -45,6 +45,305 @@ spec:
|
|||
type: array
|
||||
name:
|
||||
type: string
|
||||
spec:
|
||||
description: ApplicationSpec defines the desired state of Application
|
||||
properties:
|
||||
chart:
|
||||
properties:
|
||||
chartPullSecret:
|
||||
description: ChartPullSecret holds the reference to
|
||||
the authentication secret for accessing the Helm repository
|
||||
using HTTPS basic auth. NOT IMPLEMENTED!
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
git:
|
||||
description: Git URL is the URL of the Git repository,
|
||||
e.g. `git@github.com:org/repo`, `http://github.com/org/repo`,
|
||||
or `ssh://git@example.com:2222/org/repo.git`.
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the Helm chart _without_
|
||||
an alias, e.g. redis (for `helm upgrade [flags] stable/redis`).
|
||||
type: string
|
||||
path:
|
||||
description: Path is the path to the chart relative
|
||||
to the repository root.
|
||||
type: string
|
||||
ref:
|
||||
description: Ref is the Git branch (or other reference)
|
||||
to use. Defaults to 'master', or the configured default
|
||||
Git ref.
|
||||
type: string
|
||||
repository:
|
||||
description: RepoURL is the URL of the Helm repository,
|
||||
e.g. `https://kubernetes-charts.storage.googleapis.com`
|
||||
or `https://charts.example.com`.
|
||||
type: string
|
||||
secretRef:
|
||||
description: SecretRef holds the authentication secret
|
||||
for accessing the Git repository (over HTTPS). The
|
||||
credentials will be added to an HTTPS GitURL before
|
||||
the mirror is started.
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
skipDepUpdate:
|
||||
description: SkipDepUpdate will tell the operator to
|
||||
skip running 'helm dep update' before installing or
|
||||
upgrading the chart, the chart dependencies _must_
|
||||
be present for this to succeed.
|
||||
type: boolean
|
||||
version:
|
||||
description: Version is the targeted Helm chart version,
|
||||
e.g. 7.0.1.
|
||||
type: string
|
||||
type: object
|
||||
disableOpenAPIValidation:
|
||||
description: DisableOpenAPIValidation controls whether OpenAPI
|
||||
validation is enforced.
|
||||
type: boolean
|
||||
forceUpgrade:
|
||||
description: Force will mark this Helm release to `--force`
|
||||
upgrades. This forces the resource updates through delete/recreate
|
||||
if needed.
|
||||
type: boolean
|
||||
groupID:
|
||||
type: string
|
||||
helmVersion:
|
||||
description: 'HelmVersion is the version of Helm to target.
|
||||
If not supplied, the lowest _enabled Helm version_ will
|
||||
be targeted. Valid HelmVersion values are: "v2", "v3"'
|
||||
enum:
|
||||
- v2
|
||||
- v3
|
||||
type: string
|
||||
maxHistory:
|
||||
description: MaxHistory is the maximum amount of revisions
|
||||
to keep for the Helm release. If not supplied, it defaults
|
||||
to 10.
|
||||
type: integer
|
||||
namespace:
|
||||
description: Namespace to which the HelmRelease object will
|
||||
be deployed
|
||||
type: string
|
||||
overlays:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
releaseName:
|
||||
description: ReleaseName is the name of the The Helm release.
|
||||
If not supplied, it will be generated by affixing the
|
||||
namespace to the resource name.
|
||||
type: string
|
||||
repo:
|
||||
description: ChartRepoNickname is used to lookup the repository
|
||||
config in the registries config map
|
||||
type: string
|
||||
repoPath:
|
||||
description: RepoPath provides the subdir path to the actual
|
||||
chart artifact within a Helm Registry Artifactory for
|
||||
instance utilizes folders to store charts
|
||||
type: string
|
||||
resetValues:
|
||||
description: ResetValues will mark this Helm release to
|
||||
reset the values to the defaults of the targeted chart
|
||||
before performing an upgrade. Not explicitly setting this
|
||||
to `false` equals to `true` due to the declarative nature
|
||||
of the operator.
|
||||
type: boolean
|
||||
rollback:
|
||||
description: The rollback settings for this Helm release.
|
||||
properties:
|
||||
disableHooks:
|
||||
description: DisableHooks will mark this Helm release
|
||||
to prevent hooks from running during the rollback.
|
||||
type: boolean
|
||||
enable:
|
||||
description: Enable will mark this Helm release for
|
||||
rollbacks.
|
||||
type: boolean
|
||||
force:
|
||||
description: Force will mark this Helm release to `--force`
|
||||
rollbacks. This forces the resource updates through
|
||||
delete/recreate if needed.
|
||||
type: boolean
|
||||
maxRetries:
|
||||
description: MaxRetries is the maximum amount of upgrade
|
||||
retries the operator should make before bailing.
|
||||
format: int64
|
||||
type: integer
|
||||
recreate:
|
||||
description: Recreate will mark this Helm release to
|
||||
`--recreate-pods` for if applicable. This performs
|
||||
pod restarts.
|
||||
type: boolean
|
||||
retry:
|
||||
description: Retry will mark this Helm release for upgrade
|
||||
retries after a rollback.
|
||||
type: boolean
|
||||
timeout:
|
||||
description: Timeout is the time to wait for any individual
|
||||
Kubernetes operation (like Jobs for hooks) during
|
||||
rollback.
|
||||
format: int64
|
||||
type: integer
|
||||
wait:
|
||||
description: Wait will mark this Helm release to wait
|
||||
until all Pods, PVCs, Services, and minimum number
|
||||
of Pods of a Deployment, StatefulSet, or ReplicaSet
|
||||
are in a ready state before marking the release as
|
||||
successful.
|
||||
type: boolean
|
||||
type: object
|
||||
skipCRDs:
|
||||
description: SkipCRDs will mark this Helm release to skip
|
||||
the creation of CRDs during a Helm 3 installation.
|
||||
type: boolean
|
||||
subcharts:
|
||||
items:
|
||||
properties:
|
||||
dependencies:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
targetNamespace:
|
||||
description: TargetNamespace overrides the targeted namespace
|
||||
for the Helm release. The default namespace equals to
|
||||
the namespace of the HelmRelease resource.
|
||||
type: string
|
||||
test:
|
||||
description: The test settings for this Helm release.
|
||||
properties:
|
||||
cleanup:
|
||||
description: Cleanup, when targeting Helm 2, determines
|
||||
whether to delete test pods between each test run
|
||||
initiated by the Helm Operator.
|
||||
type: boolean
|
||||
enable:
|
||||
description: Enable will mark this Helm release for
|
||||
tests.
|
||||
type: boolean
|
||||
ignoreFailures:
|
||||
description: IgnoreFailures will cause a Helm release
|
||||
to be rolled back if it fails otherwise it will be
|
||||
left in a released state
|
||||
type: boolean
|
||||
timeout:
|
||||
description: Timeout is the time to wait for any individual
|
||||
Kubernetes operation (like Jobs for hooks) during
|
||||
test.
|
||||
format: int64
|
||||
type: integer
|
||||
type: object
|
||||
timeout:
|
||||
description: Timeout is the time to wait for any individual
|
||||
Kubernetes operation (like Jobs for hooks) during installation
|
||||
and upgrade operations.
|
||||
format: int64
|
||||
type: integer
|
||||
valueFileSecrets:
|
||||
description: ValueFileSecrets holds the local name references
|
||||
to secrets. DEPRECATED, use ValuesFrom.secretKeyRef instead.
|
||||
items:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
values:
|
||||
description: Values holds the values for this Helm release.
|
||||
type: object
|
||||
valuesFrom:
|
||||
items:
|
||||
properties:
|
||||
chartFileRef:
|
||||
description: The reference to a local chart file with
|
||||
release values.
|
||||
properties:
|
||||
optional:
|
||||
description: Optional will mark this ChartFileSelector
|
||||
as optional. The result of this are that operations
|
||||
are permitted without the source, due to it
|
||||
e.g. being temporarily unavailable.
|
||||
type: boolean
|
||||
path:
|
||||
description: Path is the file path to the source
|
||||
relative to the chart root.
|
||||
type: string
|
||||
required:
|
||||
- path
|
||||
type: object
|
||||
configMapKeyRef:
|
||||
description: The reference to a config map with release
|
||||
values.
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
optional:
|
||||
type: boolean
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
externalSourceRef:
|
||||
description: The reference to an external source with
|
||||
release values.
|
||||
properties:
|
||||
optional:
|
||||
description: Optional will mark this ExternalSourceSelector
|
||||
as optional. The result of this are that operations
|
||||
are permitted without the source, due to it
|
||||
e.g. being temporarily unavailable.
|
||||
type: boolean
|
||||
url:
|
||||
description: URL is the URL of the external source.
|
||||
type: string
|
||||
required:
|
||||
- url
|
||||
type: object
|
||||
secretKeyRef:
|
||||
description: The reference to a secret with release
|
||||
values.
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
optional:
|
||||
type: boolean
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
wait:
|
||||
description: Wait will mark this Helm release to wait until
|
||||
all Pods, PVCs, Services, and minimum number of Pods of
|
||||
a Deployment, StatefulSet, or ReplicaSet are in a ready
|
||||
state before marking the release as successful.
|
||||
type: boolean
|
||||
required:
|
||||
- chart
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
|
@ -57,23 +356,13 @@ spec:
|
|||
type: boolean
|
||||
status:
|
||||
items:
|
||||
description: ApplicationStatus defines the observed state of Application
|
||||
properties:
|
||||
application:
|
||||
description: ChartStatus denotes the current status of the Application
|
||||
Reconciliation
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
ready:
|
||||
type: boolean
|
||||
staged:
|
||||
type: boolean
|
||||
version:
|
||||
type: string
|
||||
type: object
|
||||
error:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
staged:
|
||||
type: boolean
|
||||
subcharts:
|
||||
additionalProperties:
|
||||
description: ChartStatus denotes the current status of the
|
||||
|
@ -81,22 +370,18 @@ spec:
|
|||
properties:
|
||||
error:
|
||||
type: string
|
||||
ready:
|
||||
type: boolean
|
||||
staged:
|
||||
type: boolean
|
||||
version:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
version:
|
||||
type: string
|
||||
required:
|
||||
- application
|
||||
- name
|
||||
- subcharts
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- ready
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
|
|
|
@ -1,364 +0,0 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: (devel)
|
||||
creationTimestamp: null
|
||||
name: applications.orkestra.azure.microsoft.com
|
||||
spec:
|
||||
group: orkestra.azure.microsoft.com
|
||||
names:
|
||||
kind: Application
|
||||
listKind: ApplicationList
|
||||
plural: applications
|
||||
singular: application
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Application is the Schema for the applications API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: ApplicationSpec defines the desired state of Application
|
||||
properties:
|
||||
chart:
|
||||
properties:
|
||||
chartPullSecret:
|
||||
description: ChartPullSecret holds the reference to the authentication
|
||||
secret for accessing the Helm repository using HTTPS basic auth.
|
||||
NOT IMPLEMENTED!
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
git:
|
||||
description: Git URL is the URL of the Git repository, e.g. `git@github.com:org/repo`,
|
||||
`http://github.com/org/repo`, or `ssh://git@example.com:2222/org/repo.git`.
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the Helm chart _without_ an alias,
|
||||
e.g. redis (for `helm upgrade [flags] stable/redis`).
|
||||
type: string
|
||||
path:
|
||||
description: Path is the path to the chart relative to the repository
|
||||
root.
|
||||
type: string
|
||||
ref:
|
||||
description: Ref is the Git branch (or other reference) to use.
|
||||
Defaults to 'master', or the configured default Git ref.
|
||||
type: string
|
||||
repository:
|
||||
description: RepoURL is the URL of the Helm repository, e.g. `https://kubernetes-charts.storage.googleapis.com`
|
||||
or `https://charts.example.com`.
|
||||
type: string
|
||||
secretRef:
|
||||
description: SecretRef holds the authentication secret for accessing
|
||||
the Git repository (over HTTPS). The credentials will be added
|
||||
to an HTTPS GitURL before the mirror is started.
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
skipDepUpdate:
|
||||
description: SkipDepUpdate will tell the operator to skip running
|
||||
'helm dep update' before installing or upgrading the chart,
|
||||
the chart dependencies _must_ be present for this to succeed.
|
||||
type: boolean
|
||||
version:
|
||||
description: Version is the targeted Helm chart version, e.g.
|
||||
7.0.1.
|
||||
type: string
|
||||
type: object
|
||||
disableOpenAPIValidation:
|
||||
description: DisableOpenAPIValidation controls whether OpenAPI validation
|
||||
is enforced.
|
||||
type: boolean
|
||||
forceUpgrade:
|
||||
description: Force will mark this Helm release to `--force` upgrades.
|
||||
This forces the resource updates through delete/recreate if needed.
|
||||
type: boolean
|
||||
groupID:
|
||||
type: string
|
||||
helmVersion:
|
||||
description: 'HelmVersion is the version of Helm to target. If not
|
||||
supplied, the lowest _enabled Helm version_ will be targeted. Valid
|
||||
HelmVersion values are: "v2", "v3"'
|
||||
enum:
|
||||
- v2
|
||||
- v3
|
||||
type: string
|
||||
maxHistory:
|
||||
description: MaxHistory is the maximum amount of revisions to keep
|
||||
for the Helm release. If not supplied, it defaults to 10.
|
||||
type: integer
|
||||
namespace:
|
||||
description: Namespace to which the HelmRelease object will be deployed
|
||||
type: string
|
||||
overlays:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
releaseName:
|
||||
description: ReleaseName is the name of the The Helm release. If not
|
||||
supplied, it will be generated by affixing the namespace to the
|
||||
resource name.
|
||||
type: string
|
||||
repo:
|
||||
description: ChartRepoNickname is used to lookup the repository config
|
||||
in the registries config map
|
||||
type: string
|
||||
repoPath:
|
||||
description: RepoPath provides the subdir path to the actual chart
|
||||
artifact within a Helm Registry Artifactory for instance utilizes
|
||||
folders to store charts
|
||||
type: string
|
||||
resetValues:
|
||||
description: ResetValues will mark this Helm release to reset the
|
||||
values to the defaults of the targeted chart before performing an
|
||||
upgrade. Not explicitly setting this to `false` equals to `true`
|
||||
due to the declarative nature of the operator.
|
||||
type: boolean
|
||||
rollback:
|
||||
description: The rollback settings for this Helm release.
|
||||
properties:
|
||||
disableHooks:
|
||||
description: DisableHooks will mark this Helm release to prevent
|
||||
hooks from running during the rollback.
|
||||
type: boolean
|
||||
enable:
|
||||
description: Enable will mark this Helm release for rollbacks.
|
||||
type: boolean
|
||||
force:
|
||||
description: Force will mark this Helm release to `--force` rollbacks.
|
||||
This forces the resource updates through delete/recreate if
|
||||
needed.
|
||||
type: boolean
|
||||
maxRetries:
|
||||
description: MaxRetries is the maximum amount of upgrade retries
|
||||
the operator should make before bailing.
|
||||
format: int64
|
||||
type: integer
|
||||
recreate:
|
||||
description: Recreate will mark this Helm release to `--recreate-pods`
|
||||
for if applicable. This performs pod restarts.
|
||||
type: boolean
|
||||
retry:
|
||||
description: Retry will mark this Helm release for upgrade retries
|
||||
after a rollback.
|
||||
type: boolean
|
||||
timeout:
|
||||
description: Timeout is the time to wait for any individual Kubernetes
|
||||
operation (like Jobs for hooks) during rollback.
|
||||
format: int64
|
||||
type: integer
|
||||
wait:
|
||||
description: Wait will mark this Helm release to wait until all
|
||||
Pods, PVCs, Services, and minimum number of Pods of a Deployment,
|
||||
StatefulSet, or ReplicaSet are in a ready state before marking
|
||||
the release as successful.
|
||||
type: boolean
|
||||
type: object
|
||||
skipCRDs:
|
||||
description: SkipCRDs will mark this Helm release to skip the creation
|
||||
of CRDs during a Helm 3 installation.
|
||||
type: boolean
|
||||
subcharts:
|
||||
items:
|
||||
properties:
|
||||
dependencies:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
targetNamespace:
|
||||
description: TargetNamespace overrides the targeted namespace for
|
||||
the Helm release. The default namespace equals to the namespace
|
||||
of the HelmRelease resource.
|
||||
type: string
|
||||
test:
|
||||
description: The test settings for this Helm release.
|
||||
properties:
|
||||
cleanup:
|
||||
description: Cleanup, when targeting Helm 2, determines whether
|
||||
to delete test pods between each test run initiated by the Helm
|
||||
Operator.
|
||||
type: boolean
|
||||
enable:
|
||||
description: Enable will mark this Helm release for tests.
|
||||
type: boolean
|
||||
ignoreFailures:
|
||||
description: IgnoreFailures will cause a Helm release to be rolled
|
||||
back if it fails otherwise it will be left in a released state
|
||||
type: boolean
|
||||
timeout:
|
||||
description: Timeout is the time to wait for any individual Kubernetes
|
||||
operation (like Jobs for hooks) during test.
|
||||
format: int64
|
||||
type: integer
|
||||
type: object
|
||||
timeout:
|
||||
description: Timeout is the time to wait for any individual Kubernetes
|
||||
operation (like Jobs for hooks) during installation and upgrade
|
||||
operations.
|
||||
format: int64
|
||||
type: integer
|
||||
valueFileSecrets:
|
||||
description: ValueFileSecrets holds the local name references to secrets.
|
||||
DEPRECATED, use ValuesFrom.secretKeyRef instead.
|
||||
items:
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: array
|
||||
values:
|
||||
description: Values holds the values for this Helm release.
|
||||
type: object
|
||||
valuesFrom:
|
||||
items:
|
||||
properties:
|
||||
chartFileRef:
|
||||
description: The reference to a local chart file with release
|
||||
values.
|
||||
properties:
|
||||
optional:
|
||||
description: Optional will mark this ChartFileSelector as
|
||||
optional. The result of this are that operations are permitted
|
||||
without the source, due to it e.g. being temporarily unavailable.
|
||||
type: boolean
|
||||
path:
|
||||
description: Path is the file path to the source relative
|
||||
to the chart root.
|
||||
type: string
|
||||
required:
|
||||
- path
|
||||
type: object
|
||||
configMapKeyRef:
|
||||
description: The reference to a config map with release values.
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
optional:
|
||||
type: boolean
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
externalSourceRef:
|
||||
description: The reference to an external source with release
|
||||
values.
|
||||
properties:
|
||||
optional:
|
||||
description: Optional will mark this ExternalSourceSelector
|
||||
as optional. The result of this are that operations are
|
||||
permitted without the source, due to it e.g. being temporarily
|
||||
unavailable.
|
||||
type: boolean
|
||||
url:
|
||||
description: URL is the URL of the external source.
|
||||
type: string
|
||||
required:
|
||||
- url
|
||||
type: object
|
||||
secretKeyRef:
|
||||
description: The reference to a secret with release values.
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
optional:
|
||||
type: boolean
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
type: array
|
||||
wait:
|
||||
description: Wait will mark this Helm release to wait until all Pods,
|
||||
PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet,
|
||||
or ReplicaSet are in a ready state before marking the release as
|
||||
successful.
|
||||
type: boolean
|
||||
required:
|
||||
- chart
|
||||
type: object
|
||||
status:
|
||||
description: ApplicationStatus defines the observed state of Application
|
||||
properties:
|
||||
application:
|
||||
description: ChartStatus denotes the current status of the Application
|
||||
Reconciliation
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
ready:
|
||||
type: boolean
|
||||
staged:
|
||||
type: boolean
|
||||
version:
|
||||
type: string
|
||||
type: object
|
||||
name:
|
||||
type: string
|
||||
subcharts:
|
||||
additionalProperties:
|
||||
description: ChartStatus denotes the current status of the Application
|
||||
Reconciliation
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
ready:
|
||||
type: boolean
|
||||
staged:
|
||||
type: boolean
|
||||
version:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- application
|
||||
- name
|
||||
- subcharts
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
|
@ -2,20 +2,17 @@
|
|||
# since it depends on service name and namespace that are out of this kustomize package.
|
||||
# It should be run by config/default
|
||||
resources:
|
||||
- bases/orkestra.azure.microsoft.com_applications.yaml
|
||||
- bases/orkestra.azure.microsoft.com_applicationgroups.yaml
|
||||
# +kubebuilder:scaffold:crdkustomizeresource
|
||||
|
||||
patchesStrategicMerge:
|
||||
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
|
||||
# patches here are for enabling the conversion webhook for each CRD
|
||||
#- patches/webhook_in_applications.yaml
|
||||
#- patches/webhook_in_applicationgroups.yaml
|
||||
# +kubebuilder:scaffold:crdkustomizewebhookpatch
|
||||
|
||||
# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
|
||||
# patches here are for enabling the CA injection for each CRD
|
||||
#- patches/cainjection_in_applications.yaml
|
||||
#- patches/cainjection_in_applicationgroups.yaml
|
||||
# +kubebuilder:scaffold:crdkustomizecainjectionpatch
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
# The following patch adds a directive for certmanager to inject CA into the CRD
|
||||
# CRD conversion requires k8s 1.13 or later.
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
|
||||
name: applications.orkestra.azure.microsoft.com
|
|
@ -1,17 +0,0 @@
|
|||
# The following patch enables conversion webhook for CRD
|
||||
# CRD conversion requires k8s 1.13 or later.
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: applications.orkestra.azure.microsoft.com
|
||||
spec:
|
||||
conversion:
|
||||
strategy: Webhook
|
||||
webhookClientConfig:
|
||||
# this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,
|
||||
# but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)
|
||||
caBundle: Cg==
|
||||
service:
|
||||
namespace: system
|
||||
name: webhook-service
|
||||
path: /convert
|
|
@ -1,24 +0,0 @@
|
|||
# permissions for end users to edit applications.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: application-editor-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- orkestra.azure.microsoft.com
|
||||
resources:
|
||||
- applications
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- orkestra.azure.microsoft.com
|
||||
resources:
|
||||
- applications/status
|
||||
verbs:
|
||||
- get
|
|
@ -1,20 +0,0 @@
|
|||
# permissions for end users to view applications.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: application-viewer-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- orkestra.azure.microsoft.com
|
||||
resources:
|
||||
- applications
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- orkestra.azure.microsoft.com
|
||||
resources:
|
||||
- applications/status
|
||||
verbs:
|
||||
- get
|
|
@ -26,23 +26,3 @@ rules:
|
|||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- orkestra.azure.microsoft.com
|
||||
resources:
|
||||
- applications
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- orkestra.azure.microsoft.com
|
||||
resources:
|
||||
- applications/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
|
|
|
@ -7,7 +7,46 @@ spec:
|
|||
- name: kafka-dev
|
||||
dependencies:
|
||||
- redis-dev
|
||||
spec:
|
||||
namespace: "orkestra"
|
||||
repo: bitnami
|
||||
groupID: "dev"
|
||||
subcharts:
|
||||
# subchart ordering
|
||||
- name: zookeeper
|
||||
dependencies: []
|
||||
# HelmRelease spec fields
|
||||
# https://docs.fluxcd.io/projects/helm-operator/en/1.0.0-rc9/references/helmrelease-custom-resource.html#helmrelease-custom-resource
|
||||
chart:
|
||||
repository: "https://charts.bitnami.com/bitnami"
|
||||
name: kafka
|
||||
version: 12.4.1
|
||||
overlays:
|
||||
global:
|
||||
imagePullSecrets: []
|
||||
zookeerper:
|
||||
enabled: true
|
||||
targetNamespace: "kafka-dev-ns"
|
||||
- name: redis-dev
|
||||
spec:
|
||||
namespace: "orkestra"
|
||||
repo: bitnami
|
||||
groupID: "dev"
|
||||
subcharts: []
|
||||
# HelmRelease spec fields
|
||||
# https://docs.fluxcd.io/projects/helm-operator/en/1.0.0-rc9/references/helmrelease-custom-resource.html#helmrelease-custom-resource
|
||||
chart:
|
||||
repository: "https://charts.bitnami.com/bitnami"
|
||||
name: redis
|
||||
version: 12.2.3
|
||||
overlays:
|
||||
global:
|
||||
imagePullSecrets: []
|
||||
master:
|
||||
persistence:
|
||||
size: 4Gi
|
||||
targetNamespace: "redis-dev-ns"
|
||||
|
||||
# TODO (nitishm) - define these fields using Argo Workflow spec
|
||||
# strategy:
|
||||
# rollout:
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
# Example: Kafka with Zookeeper as a dependency
|
||||
apiVersion: orkestra.azure.microsoft.com/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: kafka-dev
|
||||
spec:
|
||||
# Namespace to which the HelmRelease object is deployed
|
||||
namespace: "orkestra"
|
||||
repo: bitnami
|
||||
groupID: "dev"
|
||||
subcharts:
|
||||
# subchart ordering
|
||||
- name: zookeeper
|
||||
dependencies: []
|
||||
# HelmRelease spec fields
|
||||
# https://docs.fluxcd.io/projects/helm-operator/en/1.0.0-rc9/references/helmrelease-custom-resource.html#helmrelease-custom-resource
|
||||
chart:
|
||||
repository: "https://charts.bitnami.com/bitnami"
|
||||
name: kafka
|
||||
version: 12.4.1
|
||||
overlays:
|
||||
global:
|
||||
imagePullSecrets: []
|
||||
zookeerper:
|
||||
enabled: true
|
||||
targetNamespace: "kafka-dev-ns"
|
||||
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
# Example: Kafka with Zookeeper as a dependency
|
||||
apiVersion: orkestra.azure.microsoft.com/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: redis-dev
|
||||
spec:
|
||||
namespace: "orkestra"
|
||||
repo: bitnami
|
||||
groupID: "dev"
|
||||
subcharts: []
|
||||
# HelmRelease spec fields
|
||||
# https://docs.fluxcd.io/projects/helm-operator/en/1.0.0-rc9/references/helmrelease-custom-resource.html#helmrelease-custom-resource
|
||||
chart:
|
||||
repository: "https://charts.bitnami.com/bitnami"
|
||||
name: redis
|
||||
version: 12.2.3
|
||||
overlays:
|
||||
global:
|
||||
imagePullSecrets: []
|
||||
master:
|
||||
persistence:
|
||||
size: 4Gi
|
||||
targetNamespace: "redis-dev-ns"
|
||||
|
||||
|
||||
|
|
@ -6,10 +6,13 @@ package controllers
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/Azure/Orkestra/pkg/configurer"
|
||||
"github.com/Azure/Orkestra/pkg/registry"
|
||||
"github.com/Azure/Orkestra/pkg/workflow"
|
||||
"github.com/go-logr/logr"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/tools/record"
|
||||
|
@ -33,9 +36,18 @@ type ApplicationGroupReconciler struct {
|
|||
Cfg *configurer.Controller
|
||||
Engine workflow.Engine
|
||||
|
||||
// RegistryClient interacts with the helm registries to pull and push charts
|
||||
RegistryClient *registry.Client
|
||||
|
||||
// WorkflowNS is the namespace to which (generated) Argo Workflow object is deployed
|
||||
WorkflowNS string
|
||||
|
||||
// StagingRepoName is the nickname for the repository used for staging artifacts before being deployed using the HelmRelease object
|
||||
StagingRepoName string
|
||||
|
||||
// TargetDir to stage the charts before pushing
|
||||
TargetDir string
|
||||
|
||||
// Recorder generates kubernetes events
|
||||
Recorder record.EventRecorder
|
||||
}
|
||||
|
@ -72,6 +84,19 @@ func (r *ApplicationGroupReconciler) Reconcile(req ctrl.Request) (ctrl.Result, e
|
|||
logr.V(3).Info("reconciling AppGroup instance previously in error state")
|
||||
}
|
||||
|
||||
// Initialize the Status fields if not already setup
|
||||
if len(appGroup.Status.Applications) == 0 {
|
||||
appGroup.Status.Applications = make([]orkestrav1alpha1.ApplicationStatus, 0, len(appGroup.Spec.Applications))
|
||||
for _, app := range appGroup.Spec.Applications {
|
||||
status := orkestrav1alpha1.ApplicationStatus{
|
||||
Name: app.Name,
|
||||
ChartStatus: orkestrav1alpha1.ChartStatus{Version: app.Spec.Version},
|
||||
Subcharts: make(map[string]orkestrav1alpha1.ChartStatus),
|
||||
}
|
||||
appGroup.Status.Applications = append(appGroup.Status.Applications, status)
|
||||
}
|
||||
}
|
||||
|
||||
requeue, err = r.reconcile(ctx, logr, r.WorkflowNS, &appGroup)
|
||||
defer r.updateStatusAndEvent(ctx, appGroup, requeue, err)
|
||||
if err != nil {
|
||||
|
@ -104,3 +129,19 @@ func (r *ApplicationGroupReconciler) updateStatusAndEvent(ctx context.Context, g
|
|||
r.Recorder.Event(&grp, "Normal", "ReconcileSuccess", fmt.Sprintf("Successfully reconciled ApplicationGroup %s", grp.Name))
|
||||
}
|
||||
}
|
||||
|
||||
func isDependenciesEmbedded(ch *chart.Chart) bool {
|
||||
isURI := false
|
||||
for _, d := range ch.Metadata.Dependencies {
|
||||
if _, err := url.ParseRequestURI(d.Repository); err == nil {
|
||||
isURI = true
|
||||
}
|
||||
}
|
||||
|
||||
if !isURI {
|
||||
if len(ch.Dependencies()) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -6,11 +6,10 @@ import (
|
|||
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/Azure/Orkestra/api/v1alpha1"
|
||||
orkestrav1alpha1 "github.com/Azure/Orkestra/api/v1alpha1"
|
||||
"github.com/Azure/Orkestra/pkg/registry"
|
||||
"github.com/go-logr/logr"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -25,92 +24,154 @@ func (r *ApplicationGroupReconciler) reconcile(ctx context.Context, l logr.Logge
|
|||
|
||||
if len(appGroup.Spec.Applications) == 0 {
|
||||
l.Error(ErrInvalidSpec, "ApplicationGroup must list atleast one Application")
|
||||
return false, fmt.Errorf("application group must list atleast one Application : %w", ErrInvalidSpec)
|
||||
err := fmt.Errorf("application group must list atleast one Application : %w", ErrInvalidSpec)
|
||||
appGroup.Status.Error = err.Error()
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Readiness matrix stores the status of the Application object
|
||||
// It assists in accounting for the status of all Applications in the app group
|
||||
appReadinessMatrix := populateReadinessMatrix(appGroup.Spec.Applications)
|
||||
// Cache the application objects to pass into the generate function for workflow gen
|
||||
appObjCache := make([]*orkestrav1alpha1.Application, 0, len(appGroup.Spec.Applications))
|
||||
|
||||
// Lookup each application in the App group to check if status is ready or errored
|
||||
for _, application := range appGroup.Spec.Applications {
|
||||
ll := l.WithValues("application", application.Name)
|
||||
ll.V(3).Info("Looking up Application instance")
|
||||
|
||||
obj := &orkestrav1alpha1.Application{}
|
||||
err := r.Client.Get(ctx, types.NamespacedName{Namespace: "", Name: application.Name}, obj)
|
||||
if err != nil {
|
||||
// requeue for IsNotFound error but for any other error this is unrecoverable
|
||||
// and should not be requeued
|
||||
if errors.IsNotFound(err) {
|
||||
ll.V(2).Info("object not found - requeueing")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
ll.Error(err, "unrecoverable application object GET error - will not requeue")
|
||||
return false, err
|
||||
}
|
||||
status := obj.Status
|
||||
|
||||
if !status.Application.Ready || status.Application.Error != "" {
|
||||
ll.V(1).Info("application not in Ready state or in Error state - requeueing")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
appObjCache = append(appObjCache, obj)
|
||||
appReadinessMatrix[obj.Name] = true
|
||||
|
||||
if appGroup.Status.Applications == nil {
|
||||
appGroup.Status.Applications = make([]orkestrav1alpha1.ApplicationStatus, 0)
|
||||
}
|
||||
if !entryExists(obj.Name, appGroup.Status.Applications) {
|
||||
appStatus := orkestrav1alpha1.ApplicationStatus{
|
||||
Name: obj.Name,
|
||||
Application: obj.Status.Application,
|
||||
Subcharts: obj.Status.Subcharts,
|
||||
}
|
||||
appGroup.Status.Applications = append(appGroup.Status.Applications, appStatus)
|
||||
}
|
||||
err := r.reconcileApplications(l, appGroup)
|
||||
if err != nil {
|
||||
l.Error(err, "failed to reconcile the applications")
|
||||
err = fmt.Errorf("failed to reconcile the applications : %w", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Safety check that all applications are accounted for and that all are in READY state
|
||||
for k, v := range appReadinessMatrix {
|
||||
if v == false {
|
||||
l.V(1).Info("application not in ready state - requeueing", "application", k)
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
// if target workflow namespace is unset, then set it to the default namespace explicitly
|
||||
if ns == "" {
|
||||
ns = defaultNamespace()
|
||||
}
|
||||
|
||||
// Generate the Workflow object to submit to Argo
|
||||
return r.generateWorkflow(ctx, l, ns, appGroup, appObjCache)
|
||||
return r.generateWorkflow(ctx, l, ns, appGroup)
|
||||
}
|
||||
|
||||
func populateReadinessMatrix(apps []orkestrav1alpha1.DAG) map[string]bool {
|
||||
readyMatrix := make(map[string]bool)
|
||||
for _, app := range apps {
|
||||
readyMatrix[app.Name] = false
|
||||
}
|
||||
return readyMatrix
|
||||
}
|
||||
func (r *ApplicationGroupReconciler) reconcileApplications(l logr.Logger, appGroup *v1alpha1.ApplicationGroup) error {
|
||||
stagingDir := r.TargetDir + "/" + r.StagingRepoName
|
||||
// Pull and conditionally stage application & dependency charts
|
||||
for i, application := range appGroup.Spec.Applications {
|
||||
ll := l.WithValues("application", application.Name)
|
||||
ll.V(3).Info("performing chart actions")
|
||||
|
||||
func entryExists(name string, ss []orkestrav1alpha1.ApplicationStatus) bool {
|
||||
for _, v := range ss {
|
||||
if v.Name == name {
|
||||
return true
|
||||
repoKey := application.Spec.ChartRepoNickname
|
||||
repoPath := application.Spec.RepoPath
|
||||
name := application.Spec.HelmReleaseSpec.Name
|
||||
version := application.Spec.HelmReleaseSpec.Version
|
||||
|
||||
fpath, appCh, err := r.RegistryClient.PullChart(ll, repoKey, repoPath, name, version)
|
||||
defer func() {
|
||||
if r.Cfg.Cleanup {
|
||||
os.Remove(fpath)
|
||||
}
|
||||
}()
|
||||
if err != nil || appCh == nil {
|
||||
err = fmt.Errorf("failed to pull application chart %s/%s:%s : %w", repoKey, name, version, err)
|
||||
appGroup.Status.Error = err.Error()
|
||||
ll.Error(err, "failed to pull application chart")
|
||||
return err
|
||||
}
|
||||
|
||||
if appCh.Dependencies() != nil {
|
||||
for _, sc := range appCh.Dependencies() {
|
||||
cs := orkestrav1alpha1.ChartStatus{
|
||||
Version: sc.Metadata.Version,
|
||||
}
|
||||
appGroup.Status.Applications[i].Subcharts[sc.Name()] = cs
|
||||
}
|
||||
|
||||
stagingRepoName := r.StagingRepoName
|
||||
// If Dependencies - extract subchart and push each to staging registry
|
||||
if isDependenciesEmbedded(appCh) {
|
||||
for _, sc := range appCh.Dependencies() {
|
||||
cs := orkestrav1alpha1.ChartStatus{
|
||||
Version: sc.Metadata.Version,
|
||||
}
|
||||
|
||||
if err := sc.Validate(); err != nil {
|
||||
ll.Error(err, "failed to validate application subchart for staging registry")
|
||||
err = fmt.Errorf("failed to validate application subchart for staging registry : %w", err)
|
||||
cs.Error = err.Error()
|
||||
appGroup.Status.Applications[i].Subcharts[sc.Name()] = cs
|
||||
appGroup.Status.Error = cs.Error
|
||||
return err
|
||||
}
|
||||
|
||||
path, err := registry.SaveChartPackage(sc, stagingDir)
|
||||
if err != nil {
|
||||
ll.Error(err, "failed to save subchart package as tgz")
|
||||
err = fmt.Errorf("failed to save subchart package as tgz at location %s : %w", path, err)
|
||||
cs.Error = err.Error()
|
||||
appGroup.Status.Applications[i].Subcharts[sc.Name()] = cs
|
||||
appGroup.Status.Error = cs.Error
|
||||
return err
|
||||
}
|
||||
|
||||
err = r.RegistryClient.PushChart(ll, stagingRepoName, path, sc)
|
||||
if err != nil {
|
||||
ll.Error(err, "failed to push application subchart to staging registry")
|
||||
err = fmt.Errorf("failed to push application subchart to staging registry : %w", err)
|
||||
cs.Error = err.Error()
|
||||
appGroup.Status.Applications[i].Subcharts[sc.Name()] = cs
|
||||
appGroup.Status.Error = cs.Error
|
||||
return err
|
||||
}
|
||||
|
||||
cs.Staged = true
|
||||
cs.Version = sc.Metadata.Version
|
||||
cs.Error = ""
|
||||
|
||||
appGroup.Status.Applications[i].Subcharts[sc.Name()] = cs
|
||||
}
|
||||
}
|
||||
|
||||
// Unset dependencies by disabling them.
|
||||
// Using appCh.SetDependencies() does not cut it since some charts rely on subcharts for tpl helpers
|
||||
// provided in the charts directory.
|
||||
// IMPORTANT: This expects charts to follow best practices to allow enabling and disabling subcharts
|
||||
// See: https://helm.sh/docs/topics/charts/ #Chart Dependencies
|
||||
for _, dep := range appCh.Metadata.Dependencies {
|
||||
// Disable subchart through metadata
|
||||
dep.Enabled = false
|
||||
// Precautionary - overwrite values with subcharts disabled
|
||||
appCh.Values[dep.Name] = map[string]interface{}{
|
||||
"enabled": false,
|
||||
}
|
||||
}
|
||||
|
||||
if err := appCh.Validate(); err != nil {
|
||||
ll.Error(err, "failed to validate application chart for staging registry")
|
||||
err = fmt.Errorf("failed to validate application chart for staging registry : %w", err)
|
||||
appGroup.Status.Error = err.Error()
|
||||
appGroup.Status.Applications[i].ChartStatus.Error = err.Error()
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := registry.SaveChartPackage(appCh, stagingDir)
|
||||
if err != nil {
|
||||
ll.Error(err, "failed to save modified app chart to filesystem")
|
||||
err = fmt.Errorf("failed to save modified app chart to filesystem : %w", err)
|
||||
appGroup.Status.Error = err.Error()
|
||||
appGroup.Status.Applications[i].ChartStatus.Error = err.Error()
|
||||
return err
|
||||
}
|
||||
|
||||
// Replace existing chart with modified chart
|
||||
path := stagingDir + "/" + application.Spec.HelmReleaseSpec.Name + "-" + appCh.Metadata.Version + ".tgz"
|
||||
err = r.RegistryClient.PushChart(ll, stagingRepoName, path, appCh)
|
||||
if err != nil {
|
||||
ll.Error(err, "failed to push modified application chart to staging registry")
|
||||
err = fmt.Errorf("failed to push modified application chart to staging registry : %w", err)
|
||||
appGroup.Status.Applications[i].ChartStatus.Error = err.Error()
|
||||
appGroup.Status.Error = err.Error()
|
||||
return err
|
||||
}
|
||||
|
||||
appGroup.Status.Applications[i].ChartStatus.Staged = true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ApplicationGroupReconciler) generateWorkflow(ctx context.Context, logr logr.Logger, ns string, g *orkestrav1alpha1.ApplicationGroup, apps []*orkestrav1alpha1.Application) (requeue bool, err error) {
|
||||
err = r.Engine.Generate(ctx, logr, ns, g, apps)
|
||||
func (r *ApplicationGroupReconciler) generateWorkflow(ctx context.Context, logr logr.Logger, ns string, g *orkestrav1alpha1.ApplicationGroup) (requeue bool, err error) {
|
||||
err = r.Engine.Generate(ctx, logr, ns, g)
|
||||
if err != nil {
|
||||
logr.Error(err, "engine failed to generate workflow")
|
||||
return false, fmt.Errorf("failed to generate workflow : %w", err)
|
||||
|
@ -122,6 +183,8 @@ func (r *ApplicationGroupReconciler) generateWorkflow(ctx context.Context, logr
|
|||
return false, err
|
||||
}
|
||||
|
||||
g.Status.Ready = true
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/tools/record"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
orkestrav1alpha1 "github.com/Azure/Orkestra/api/v1alpha1"
|
||||
"github.com/Azure/Orkestra/pkg/configurer"
|
||||
"github.com/Azure/Orkestra/pkg/registry"
|
||||
)
|
||||
|
||||
const (
|
||||
appNameKey = "appgroup"
|
||||
)
|
||||
|
||||
// ApplicationReconciler reconciles a Application object
|
||||
type ApplicationReconciler struct {
|
||||
client.Client
|
||||
Log logr.Logger
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
// Cfg is the controller configuration that gives access to the helm registry configuration (and more as we add options to configure the controller)
|
||||
Cfg *configurer.Controller
|
||||
|
||||
// RegistryClient interacts with the helm registries to pull and push charts
|
||||
RegistryClient *registry.Client
|
||||
|
||||
// StagingRepoName is the nickname for the repository used for staging artifacts before being deployed using the HelmRelease object
|
||||
StagingRepoName string
|
||||
|
||||
// TargetDir to stage the charts before pushing
|
||||
TargetDir string
|
||||
|
||||
// Recorder generates kubernetes events
|
||||
Recorder record.EventRecorder
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=orkestra.azure.microsoft.com,resources=applications,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=orkestra.azure.microsoft.com,resources=applications/status,verbs=get;update;patch
|
||||
|
||||
func (r *ApplicationReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
||||
var requeue bool
|
||||
var err error
|
||||
var application orkestrav1alpha1.Application
|
||||
|
||||
ctx := context.Background()
|
||||
logr := r.Log.WithValues(appNameKey, req.NamespacedName.Name)
|
||||
|
||||
if err := r.Get(ctx, req.NamespacedName, &application); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
logr.V(3).Info("skip reconciliation since Appllication instance not found on the cluster")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
logr.Error(err, "unable to fetch Application instance")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
logr = logr.WithValues("status-ready", application.Status.Application.Ready, "status-error", application.Status.Application.Error)
|
||||
|
||||
if application.Status.Application.Ready {
|
||||
logr.V(3).Info("skip reconciling since Application has already been successfully reconciled")
|
||||
return ctrl.Result{Requeue: false}, nil
|
||||
}
|
||||
|
||||
// info log if status error is not nil on reconciling
|
||||
if application.Status.Application.Error != "" {
|
||||
logr.V(3).Info("reconciling Application instance previously in error state")
|
||||
}
|
||||
|
||||
requeue, err = r.reconcile(ctx, logr, &application)
|
||||
defer r.updateStatusAndEvent(ctx, application, requeue, err)
|
||||
if err != nil {
|
||||
logr.Error(err, "failed to reconcile application instance")
|
||||
return ctrl.Result{Requeue: requeue}, err
|
||||
}
|
||||
|
||||
return ctrl.Result{Requeue: requeue}, nil
|
||||
}
|
||||
|
||||
func (r *ApplicationReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&orkestrav1alpha1.Application{}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *ApplicationReconciler) updateStatusAndEvent(ctx context.Context, app orkestrav1alpha1.Application, requeue bool, err error) {
|
||||
errStr := ""
|
||||
if err != nil {
|
||||
errStr = err.Error()
|
||||
}
|
||||
|
||||
app.Status.Application.Ready = !requeue
|
||||
app.Status.Application.Error = errStr
|
||||
|
||||
_ = r.Status().Update(ctx, &app)
|
||||
|
||||
if errStr != "" {
|
||||
r.Recorder.Event(&app, "Warning", "ReconcileError", fmt.Sprintf("Failed to reconcile Application %s with Error %s", app.Name, errStr))
|
||||
} else {
|
||||
r.Recorder.Event(&app, "Normal", "ReconcileSuccess", fmt.Sprintf("Successfully reconciled Application %s", app.Name))
|
||||
}
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
orkestrav1alpha1 "github.com/Azure/Orkestra/api/v1alpha1"
|
||||
"github.com/Azure/Orkestra/pkg/registry"
|
||||
"github.com/go-logr/logr"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
)
|
||||
|
||||
func (r *ApplicationReconciler) reconcile(ctx context.Context, l logr.Logger, application *orkestrav1alpha1.Application) (bool, error) {
|
||||
stagingDir := r.TargetDir + "/" + r.StagingRepoName
|
||||
|
||||
ll := l.WithValues("application", application.Name, "group", application.Spec.GroupID)
|
||||
|
||||
application.Status.Name = application.Name
|
||||
|
||||
if application.Status.Subcharts == nil {
|
||||
application.Status.Subcharts = make(map[string]orkestrav1alpha1.ChartStatus)
|
||||
}
|
||||
|
||||
if application.Status.Application.Ready {
|
||||
ll.Info("application already in ready state")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
repoKey := application.Spec.ChartRepoNickname
|
||||
repoPath := application.Spec.RepoPath
|
||||
name := application.Spec.HelmReleaseSpec.Name
|
||||
version := application.Spec.HelmReleaseSpec.Version
|
||||
|
||||
fpath, appCh, err := r.RegistryClient.PullChart(ll, repoKey, repoPath, name, version)
|
||||
defer func() {
|
||||
if r.Cfg.Cleanup {
|
||||
os.Remove(fpath)
|
||||
}
|
||||
}()
|
||||
if err != nil || appCh == nil {
|
||||
ll.Error(err, "failed to pull application chart")
|
||||
return false, fmt.Errorf("failed to pull application chart %s/%s:%s : %w", repoKey, name, version, err)
|
||||
}
|
||||
|
||||
if appCh.Dependencies() != nil {
|
||||
stagingRepoName := r.StagingRepoName
|
||||
// If Dependencies - extract subchart and push each to staging registry
|
||||
if isDependenciesEmbedded(appCh) {
|
||||
for _, sc := range appCh.Dependencies() {
|
||||
cs := orkestrav1alpha1.ChartStatus{}
|
||||
|
||||
if err := sc.Validate(); err != nil {
|
||||
cs.Error = err.Error()
|
||||
ll.Error(err, "failed to validate application subchart for staging registry")
|
||||
return false, fmt.Errorf("failed to validate application subchart for staging registry : %w", err)
|
||||
}
|
||||
|
||||
path, err := registry.SaveChartPackage(sc, stagingDir)
|
||||
if err != nil {
|
||||
cs.Error = err.Error()
|
||||
ll.Error(err, "failed to save subchart package as tgz")
|
||||
return false, fmt.Errorf("failed to save subchart package as tgz at location %s : %w", path, err)
|
||||
}
|
||||
|
||||
err = r.RegistryClient.PushChart(ll, stagingRepoName, path, sc)
|
||||
|
||||
if err != nil {
|
||||
cs.Error = err.Error()
|
||||
ll.Error(err, "failed to push application subchart to staging registry")
|
||||
return false, fmt.Errorf("failed to push application subchart to staging registry : %w", err)
|
||||
}
|
||||
|
||||
cs.Staged = true
|
||||
cs.Version = sc.Metadata.Version
|
||||
cs.Ready = true
|
||||
cs.Error = ""
|
||||
|
||||
application.Status.Subcharts[sc.Name()] = cs
|
||||
application.Status.Application.Staged = true
|
||||
}
|
||||
} else {
|
||||
for _, sc := range appCh.Dependencies() {
|
||||
cs := orkestrav1alpha1.ChartStatus{
|
||||
Staged: false,
|
||||
Version: sc.Metadata.Version,
|
||||
Ready: true,
|
||||
Error: "",
|
||||
}
|
||||
application.Status.Subcharts[sc.Name()] = cs
|
||||
application.Status.Application.Staged = false
|
||||
}
|
||||
}
|
||||
|
||||
// Unset dependencies by disabling them.
|
||||
// Using appCh.SetDependencies() does not cut it since some charts rely on subcharts for tpl helpers
|
||||
// provided in the charts directory.
|
||||
// IMPORTANT: This expects charts to follow best practices to allow enabling and disabling subcharts
|
||||
// See: https://helm.sh/docs/topics/charts/ #Chart Dependencies
|
||||
for _, dep := range appCh.Metadata.Dependencies {
|
||||
// Disable subchart through metadata
|
||||
dep.Enabled = false
|
||||
// Precautionary - overrite values with subcharts disabled:93
|
||||
appCh.Values[dep.Name] = map[string]interface{}{
|
||||
"enabled": false,
|
||||
}
|
||||
}
|
||||
|
||||
if err := appCh.Validate(); err != nil {
|
||||
application.Status.Application.Error = err.Error()
|
||||
ll.Error(err, "failed to validate application chart for staging registry")
|
||||
return false, fmt.Errorf("failed to validate application chart for staging registry : %w", err)
|
||||
}
|
||||
|
||||
_, err := registry.SaveChartPackage(appCh, stagingDir)
|
||||
if err != nil {
|
||||
ll.Error(err, "failed to save modified app chart to filesystem")
|
||||
return false, fmt.Errorf("failed to save modified app chart to filesystem : %w", err)
|
||||
}
|
||||
|
||||
// Replace existing chart with modified chart
|
||||
path := stagingDir + "/" + application.Spec.HelmReleaseSpec.Name + "-" + appCh.Metadata.Version + ".tgz"
|
||||
err = r.RegistryClient.PushChart(ll, stagingRepoName, path, appCh)
|
||||
if err != nil {
|
||||
application.Status.Application.Error = err.Error()
|
||||
ll.Error(err, "failed to push modified application chart to staging registry")
|
||||
return false, fmt.Errorf("failed to push modified application chart to staging registry : %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func isDependenciesEmbedded(ch *chart.Chart) bool {
|
||||
isURI := false
|
||||
for _, d := range ch.Metadata.Dependencies {
|
||||
if _, err := url.ParseRequestURI(d.Repository); err == nil {
|
||||
isURI = true
|
||||
}
|
||||
}
|
||||
|
||||
if !isURI {
|
||||
if len(ch.Dependencies()) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
# > **Out of date** as of 02/22/2021 - *`Application` custom resource has been deprecated*
|
||||
# Sequence Diagrams
|
||||
|
||||
## Submitting an Application Group to a Kubernetes Cluster
|
||||
|
||||
|
||||
<p align="center"><img src="../assets/submit-sequence.png" width="1000x" /></p>
|
||||
|
||||
### Steps
|
||||
|
|
|
@ -7,7 +7,46 @@ spec:
|
|||
- name: kafka-dev
|
||||
dependencies:
|
||||
- redis-dev
|
||||
spec:
|
||||
namespace: "orkestra"
|
||||
repo: bitnami
|
||||
groupID: "dev"
|
||||
subcharts:
|
||||
# subchart ordering
|
||||
- name: zookeeper
|
||||
dependencies: []
|
||||
# HelmRelease spec fields
|
||||
# https://docs.fluxcd.io/projects/helm-operator/en/1.0.0-rc9/references/helmrelease-custom-resource.html#helmrelease-custom-resource
|
||||
chart:
|
||||
repository: "https://charts.bitnami.com/bitnami"
|
||||
name: kafka
|
||||
version: 12.4.1
|
||||
overlays:
|
||||
global:
|
||||
imagePullSecrets: []
|
||||
zookeerper:
|
||||
enabled: true
|
||||
targetNamespace: "kafka-dev-ns"
|
||||
- name: redis-dev
|
||||
spec:
|
||||
namespace: "orkestra"
|
||||
repo: bitnami
|
||||
groupID: "dev"
|
||||
subcharts: []
|
||||
# HelmRelease spec fields
|
||||
# https://docs.fluxcd.io/projects/helm-operator/en/1.0.0-rc9/references/helmrelease-custom-resource.html#helmrelease-custom-resource
|
||||
chart:
|
||||
repository: "https://charts.bitnami.com/bitnami"
|
||||
name: redis
|
||||
version: 12.2.3
|
||||
overlays:
|
||||
global:
|
||||
imagePullSecrets: []
|
||||
master:
|
||||
persistence:
|
||||
size: 4Gi
|
||||
targetNamespace: "redis-dev-ns"
|
||||
|
||||
# TODO (nitishm) - define these fields using Argo Workflow spec
|
||||
# strategy:
|
||||
# rollout:
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
# Example: Kafka with Zookeeper as a dependency
|
||||
apiVersion: orkestra.azure.microsoft.com/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: kafka-dev
|
||||
spec:
|
||||
# Namespace to which the HelmRelease object is deployed
|
||||
namespace: "orkestra"
|
||||
repo: bitnami
|
||||
groupID: "dev"
|
||||
subcharts:
|
||||
# subchart ordering
|
||||
- name: zookeeper
|
||||
dependencies: []
|
||||
# HelmRelease spec fields
|
||||
# https://docs.fluxcd.io/projects/helm-operator/en/1.0.0-rc9/references/helmrelease-custom-resource.html#helmrelease-custom-resource
|
||||
chart:
|
||||
repository: "https://charts.bitnami.com/bitnami"
|
||||
name: kafka
|
||||
version: 12.4.1
|
||||
overlays:
|
||||
global:
|
||||
imagePullSecrets: []
|
||||
zookeerper:
|
||||
enabled: true
|
||||
targetNamespace: "kafka-dev-ns"
|
||||
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
# Example: Kafka with Zookeeper as a dependency
|
||||
apiVersion: orkestra.azure.microsoft.com/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: redis-dev
|
||||
spec:
|
||||
namespace: "orkestra"
|
||||
repo: bitnami
|
||||
groupID: "dev"
|
||||
subcharts: []
|
||||
# HelmRelease spec fields
|
||||
# https://docs.fluxcd.io/projects/helm-operator/en/1.0.0-rc9/references/helmrelease-custom-resource.html#helmrelease-custom-resource
|
||||
chart:
|
||||
repository: "https://charts.bitnami.com/bitnami"
|
||||
name: redis
|
||||
version: 12.2.3
|
||||
overlays:
|
||||
global:
|
||||
imagePullSecrets: []
|
||||
master:
|
||||
persistence:
|
||||
size: 4Gi
|
||||
targetNamespace: "redis-dev-ns"
|
||||
|
||||
|
||||
|
29
main.go
29
main.go
|
@ -103,20 +103,6 @@ func main() {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&controllers.ApplicationReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Log: ctrl.Log.WithName("controllers").WithName("Application"),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Cfg: cfg.Ctrl,
|
||||
RegistryClient: rc,
|
||||
StagingRepoName: stagingRepoName,
|
||||
TargetDir: tempChartStoreTargetDir,
|
||||
Recorder: mgr.GetEventRecorderFor("application-controller"),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Application")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
sCfg, err := cfg.Ctrl.RegistryConfig(stagingRepoName)
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to find staging repo configuration", "controller", "registry-config")
|
||||
|
@ -124,12 +110,15 @@ func main() {
|
|||
}
|
||||
|
||||
if err = (&controllers.ApplicationGroupReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Log: ctrl.Log.WithName("controllers").WithName("ApplicationGroup"),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Cfg: cfg.Ctrl,
|
||||
Engine: workflow.Argo(scheme, mgr.GetClient(), sCfg.URL),
|
||||
Recorder: mgr.GetEventRecorderFor("appgroup-controller"),
|
||||
Client: mgr.GetClient(),
|
||||
Log: ctrl.Log.WithName("controllers").WithName("ApplicationGroup"),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Cfg: cfg.Ctrl,
|
||||
RegistryClient: rc,
|
||||
Engine: workflow.Argo(scheme, mgr.GetClient(), sCfg.URL),
|
||||
StagingRepoName: stagingRepoName,
|
||||
TargetDir: tempChartStoreTargetDir,
|
||||
Recorder: mgr.GetEventRecorderFor("appgroup-controller"),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "ApplicationGroup")
|
||||
os.Exit(1)
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/Azure/Orkestra/api/v1alpha1"
|
||||
v1alpha12 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1"
|
||||
|
@ -64,37 +63,19 @@ func (a *argo) initWorkflowObject() {
|
|||
a.wf.Spec.Templates = make([]v1alpha12.Template, 0)
|
||||
}
|
||||
|
||||
func (a *argo) Generate(ctx context.Context, l logr.Logger, ns string, g *v1alpha1.ApplicationGroup, apps []*v1alpha1.Application) error {
|
||||
func (a *argo) Generate(ctx context.Context, l logr.Logger, ns string, g *v1alpha1.ApplicationGroup) error {
|
||||
if g == nil {
|
||||
l.Error(nil, "ApplicationGroup object cannot be nil")
|
||||
return fmt.Errorf("applicationGroup object cannot be nil")
|
||||
}
|
||||
|
||||
if apps == nil {
|
||||
l.Error(nil, "applications slice cannot be nil")
|
||||
return fmt.Errorf("applications slice cannot be nil")
|
||||
}
|
||||
|
||||
if len(apps) != len(g.Spec.Applications) {
|
||||
l.Error(nil, "application len mismatch")
|
||||
return fmt.Errorf("len application objects [%v] do not match len entries applicationgroup.spec.applications %v", len(apps), len(g.Spec.Applications))
|
||||
}
|
||||
|
||||
a.initWorkflowObject()
|
||||
|
||||
// Set name and namespace based on the input application group
|
||||
a.wf.Name = g.Name
|
||||
a.wf.Namespace = ns
|
||||
|
||||
sort.SliceStable(apps[:], func(i, j int) bool { //nolint:gocritic
|
||||
return apps[i].Name < apps[j].Name
|
||||
})
|
||||
|
||||
sort.SliceStable(g.Spec.Applications[:], func(i, j int) bool { //nolint:gocritic
|
||||
return g.Spec.Applications[i].Name < g.Spec.Applications[j].Name
|
||||
})
|
||||
|
||||
err := a.generateWorkflow(ctx, g, apps)
|
||||
err := a.generateWorkflow(ctx, g)
|
||||
if err != nil {
|
||||
l.Error(err, "failed to generate workflow")
|
||||
return fmt.Errorf("failed to generate argo workflow : %w", err)
|
||||
|
@ -146,9 +127,9 @@ func (a *argo) Submit(ctx context.Context, l logr.Logger, g *v1alpha1.Applicatio
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a *argo) generateWorkflow(ctx context.Context, g *v1alpha1.ApplicationGroup, apps []*v1alpha1.Application) error {
|
||||
func (a *argo) generateWorkflow(ctx context.Context, g *v1alpha1.ApplicationGroup) error {
|
||||
// Generate the Entrypoint template and Application Group DAG
|
||||
err := a.generateAppGroupTpls(ctx, g, apps)
|
||||
err := a.generateAppGroupTpls(ctx, g)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate Application Group DAG : %w", err)
|
||||
}
|
||||
|
@ -156,7 +137,7 @@ func (a *argo) generateWorkflow(ctx context.Context, g *v1alpha1.ApplicationGrou
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a *argo) generateAppGroupTpls(ctx context.Context, g *v1alpha1.ApplicationGroup, apps []*v1alpha1.Application) error {
|
||||
func (a *argo) generateAppGroupTpls(ctx context.Context, g *v1alpha1.ApplicationGroup) error {
|
||||
if a.wf == nil {
|
||||
return fmt.Errorf("workflow cannot be nil")
|
||||
}
|
||||
|
@ -168,13 +149,13 @@ func (a *argo) generateAppGroupTpls(ctx context.Context, g *v1alpha1.Application
|
|||
entry := v1alpha12.Template{
|
||||
Name: entrypointTplName,
|
||||
DAG: &v1alpha12.DAGTemplate{
|
||||
Tasks: make([]v1alpha12.DAGTask, len(apps)),
|
||||
Tasks: make([]v1alpha12.DAGTask, len(g.Spec.Applications)),
|
||||
// TBD (nitishm): Do we need to failfast?
|
||||
// FailFast: true
|
||||
},
|
||||
}
|
||||
|
||||
adt, err := a.generateAppDAGTemplates(ctx, apps, a.stagingRepoURL)
|
||||
adt, err := a.generateAppDAGTemplates(ctx, g, a.stagingRepoURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate application DAG templates : %w", err)
|
||||
}
|
||||
|
@ -213,13 +194,16 @@ func updateAppGroupDAG(g *v1alpha1.ApplicationGroup, entry *v1alpha12.Template,
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a *argo) generateAppDAGTemplates(ctx context.Context, apps []*v1alpha1.Application, repo string) ([]v1alpha12.Template, error) {
|
||||
func (a *argo) generateAppDAGTemplates(ctx context.Context, g *v1alpha1.ApplicationGroup, repo string) ([]v1alpha12.Template, error) {
|
||||
ts := make([]v1alpha12.Template, 0)
|
||||
|
||||
for _, app := range apps {
|
||||
for i, app := range g.Spec.Applications {
|
||||
var hasSubcharts bool
|
||||
app.Spec.Values = app.Spec.Overlays
|
||||
|
||||
appStatus := &g.Status.Applications[i].ChartStatus
|
||||
scStatus := g.Status.Applications[i].Subcharts
|
||||
|
||||
// Create Subchart DAG only when the application chart has dependencies
|
||||
if len(app.Spec.Subcharts) > 0 {
|
||||
hasSubcharts = true
|
||||
|
@ -228,7 +212,7 @@ func (a *argo) generateAppDAGTemplates(ctx context.Context, apps []*v1alpha1.App
|
|||
}
|
||||
|
||||
t.DAG = &v1alpha12.DAGTemplate{}
|
||||
tasks, err := a.generateSubchartAndAppDAGTasks(ctx, app, repo, app.Spec.HelmReleaseSpec.TargetNamespace)
|
||||
tasks, err := a.generateSubchartAndAppDAGTasks(ctx, g, &app.Spec, appStatus, scStatus, repo, app.Spec.HelmReleaseSpec.TargetNamespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate Application Template DAG tasks : %w", err)
|
||||
}
|
||||
|
@ -251,14 +235,16 @@ func (a *argo) generateAppDAGTemplates(ctx context.Context, apps []*v1alpha1.App
|
|||
// Create the namespace since helm-operator does not do this
|
||||
err := a.cli.Get(ctx, types.NamespacedName{Name: ns.Name}, &ns)
|
||||
if err != nil {
|
||||
err = controllerutil.SetControllerReference(app, &ns, a.scheme)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set OwnerReference for Namespace %s : %w", ns.Name, err)
|
||||
}
|
||||
if errors.IsNotFound(err) {
|
||||
err = controllerutil.SetControllerReference(g, &ns, a.scheme)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set OwnerReference for Namespace %s : %w", ns.Name, err)
|
||||
}
|
||||
|
||||
err = a.cli.Create(ctx, &ns)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to CREATE namespace %s object for application %s : %w", ns.Name, app.Name, err)
|
||||
err = a.cli.Create(ctx, &ns)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to CREATE namespace %s object for application %s : %w", ns.Name, app.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,7 +263,7 @@ func (a *argo) generateAppDAGTemplates(ctx context.Context, apps []*v1alpha1.App
|
|||
|
||||
// hr.Spec.RepoChartSource.RepoURL = repo
|
||||
|
||||
if app.Status.Application.Staged {
|
||||
if appStatus.Staged {
|
||||
hr.Spec.RepoURL = repo
|
||||
}
|
||||
|
||||
|
@ -307,23 +293,19 @@ func (a *argo) generateAppDAGTemplates(ctx context.Context, apps []*v1alpha1.App
|
|||
return ts, nil
|
||||
}
|
||||
|
||||
func (a *argo) generateSubchartAndAppDAGTasks(ctx context.Context, app *v1alpha1.Application, repo, targetNS string) ([]v1alpha12.DAGTask, error) {
|
||||
func (a *argo) generateSubchartAndAppDAGTasks(ctx context.Context, g *v1alpha1.ApplicationGroup, app *v1alpha1.ApplicationSpec, status *v1alpha1.ChartStatus, subchartsStatus map[string]v1alpha1.ChartStatus, repo, targetNS string) ([]v1alpha12.DAGTask, error) {
|
||||
if repo == "" {
|
||||
return nil, fmt.Errorf("repo arg must be a valid non-empty string")
|
||||
}
|
||||
|
||||
// XXX (nitishm) Should this be set to nil if no subcharts are found??
|
||||
tasks := make([]v1alpha12.DAGTask, 0, len(app.Spec.Subcharts)+1)
|
||||
tasks := make([]v1alpha12.DAGTask, 0, len(app.Subcharts)+1)
|
||||
|
||||
for _, sc := range app.Spec.Subcharts {
|
||||
s, ok := app.Status.Subcharts[sc.Name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to find subchart info in applications status field")
|
||||
}
|
||||
for _, sc := range app.Subcharts {
|
||||
isStaged := subchartsStatus[sc.Name].Staged
|
||||
version := subchartsStatus[sc.Name].Version
|
||||
|
||||
isStaged := app.Status.Application.Staged
|
||||
|
||||
hr := generateSubchartHelmRelease(app.Spec.HelmReleaseSpec, sc.Name, s.Version, repo, targetNS, isStaged)
|
||||
hr := generateSubchartHelmRelease(app.HelmReleaseSpec, sc.Name, version, repo, targetNS, isStaged)
|
||||
task := v1alpha12.DAGTask{
|
||||
Name: sc.Name,
|
||||
Template: helmReleaseExecutor,
|
||||
|
@ -351,15 +333,17 @@ func (a *argo) generateSubchartAndAppDAGTasks(ctx context.Context, app *v1alpha1
|
|||
// Create the namespace since helm-operator does not do this
|
||||
err := a.cli.Get(ctx, types.NamespacedName{Name: ns.Name}, &ns)
|
||||
if err != nil {
|
||||
// Add OwnershipReference
|
||||
err = controllerutil.SetControllerReference(app, &ns, a.scheme)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set OwnerReference for Namespace %s : %w", ns.Name, err)
|
||||
}
|
||||
if errors.IsNotFound(err) {
|
||||
// Add OwnershipReference
|
||||
err = controllerutil.SetControllerReference(g, &ns, a.scheme)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set OwnerReference for Namespace %s : %w", ns.Name, err)
|
||||
}
|
||||
|
||||
err = a.cli.Create(ctx, &ns)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to CREATE namespace %s object for subchart %s : %w", ns.Name, sc.Name, err)
|
||||
err = a.cli.Create(ctx, &ns)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to CREATE namespace %s object for subchart %s : %w", ns.Name, sc.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -373,9 +357,9 @@ func (a *argo) generateSubchartAndAppDAGTasks(ctx context.Context, app *v1alpha1
|
|||
},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: app.Name,
|
||||
Namespace: app.Spec.HelmReleaseSpec.TargetNamespace,
|
||||
Namespace: app.HelmReleaseSpec.TargetNamespace,
|
||||
},
|
||||
Spec: app.DeepCopy().Spec.HelmReleaseSpec,
|
||||
Spec: app.DeepCopy().HelmReleaseSpec,
|
||||
}
|
||||
|
||||
// staging repo instead of the primary repo
|
||||
|
@ -385,8 +369,8 @@ func (a *argo) generateSubchartAndAppDAGTasks(ctx context.Context, app *v1alpha1
|
|||
// to prevent duplication and possible collision of deployed resources
|
||||
// Since the subchart should have been deployed in a prior DAG step,
|
||||
// we must not redeploy it along with the parent application chart.
|
||||
for d := range app.Status.Subcharts {
|
||||
hr.Spec.Values.Data[d] = map[string]interface{}{
|
||||
for _, d := range app.Subcharts {
|
||||
hr.Spec.Values.Data[d.Name] = map[string]interface{}{
|
||||
"enabled": false,
|
||||
}
|
||||
}
|
||||
|
@ -404,15 +388,17 @@ func (a *argo) generateSubchartAndAppDAGTasks(ctx context.Context, app *v1alpha1
|
|||
// Create the namespace since helm-operator does not do this
|
||||
err := a.cli.Get(ctx, types.NamespacedName{Name: ns.Name}, &ns)
|
||||
if err != nil {
|
||||
// Add OwnershipReference
|
||||
err = controllerutil.SetControllerReference(app, &ns, a.scheme)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set OwnerReference for Namespace %s : %w", ns.Name, err)
|
||||
}
|
||||
if errors.IsNotFound(err) {
|
||||
// Add OwnershipReference
|
||||
err = controllerutil.SetControllerReference(g, &ns, a.scheme)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set OwnerReference for Namespace %s : %w", ns.Name, err)
|
||||
}
|
||||
|
||||
err = a.cli.Create(ctx, &ns)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to CREATE namespace %s object for staged application %s : %w", ns.Name, app.Name, err)
|
||||
err = a.cli.Create(ctx, &ns)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to CREATE namespace %s object for staged application %s : %w", ns.Name, app.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
package workflow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/Orkestra/api/v1alpha1"
|
||||
v1alpha12 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1"
|
||||
helmopv1 "github.com/fluxcd/helm-operator/pkg/apis/helm.fluxcd.io/v1"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
func helmValues(v string) map[string]interface{} {
|
||||
|
@ -900,36 +895,36 @@ func Test_subchartValues(t *testing.T) {
|
|||
// }
|
||||
// }
|
||||
|
||||
func Test_argo_generateAppGroupTpls(t *testing.T) {
|
||||
type fields struct {
|
||||
scheme *runtime.Scheme
|
||||
cli client.Client
|
||||
wf *v1alpha12.Workflow
|
||||
stagingRepoURL string
|
||||
}
|
||||
type args struct {
|
||||
g *v1alpha1.ApplicationGroup
|
||||
apps []*v1alpha1.Application
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := &argo{
|
||||
scheme: tt.fields.scheme,
|
||||
cli: tt.fields.cli,
|
||||
wf: tt.fields.wf,
|
||||
stagingRepoURL: tt.fields.stagingRepoURL,
|
||||
}
|
||||
if err := a.generateAppGroupTpls(context.TODO(), tt.args.g, tt.args.apps); (err != nil) != tt.wantErr {
|
||||
t.Errorf("argo.generateAppGroupTpls() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// func Test_argo_generateAppGroupTpls(t *testing.T) {
|
||||
// type fields struct {
|
||||
// scheme *runtime.Scheme
|
||||
// cli client.Client
|
||||
// wf *v1alpha12.Workflow
|
||||
// stagingRepoURL string
|
||||
// }
|
||||
// type args struct {
|
||||
// g *v1alpha1.ApplicationGroup
|
||||
// apps []*v1alpha1.Application
|
||||
// }
|
||||
// tests := []struct {
|
||||
// name string
|
||||
// fields fields
|
||||
// args args
|
||||
// wantErr bool
|
||||
// }{
|
||||
// // TODO: Add test cases.
|
||||
// }
|
||||
// for _, tt := range tests {
|
||||
// t.Run(tt.name, func(t *testing.T) {
|
||||
// a := &argo{
|
||||
// scheme: tt.fields.scheme,
|
||||
// cli: tt.fields.cli,
|
||||
// wf: tt.fields.wf,
|
||||
// stagingRepoURL: tt.fields.stagingRepoURL,
|
||||
// }
|
||||
// if err := a.generateAppGroupTpls(context.TODO(), tt.args.g, tt.args.apps); (err != nil) != tt.wantErr {
|
||||
// t.Errorf("argo.generateAppGroupTpls() error = %v, wantErr %v", err, tt.wantErr)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
type Engine interface {
|
||||
// Generate the object required by the workflow engine
|
||||
Generate(ctx context.Context, l logr.Logger, ns string, g *v1alpha1.ApplicationGroup, apps []*v1alpha1.Application) error
|
||||
Generate(ctx context.Context, l logr.Logger, ns string, g *v1alpha1.ApplicationGroup) error
|
||||
// Submit the object required by the workflow engine generated by the Generate method
|
||||
Submit(ctx context.Context, l logr.Logger, g *v1alpha1.ApplicationGroup) error
|
||||
}
|
||||
|
|
|
@ -5,3 +5,4 @@ registries:
|
|||
|
||||
staging:
|
||||
url: "http://localhost:8080"
|
||||
path: "charts/"
|
Загрузка…
Ссылка в новой задаче