Introduce HelmChart API and controller
- Add the HelmChart types and controller - Semver expressions are found by utilizing Helm repository index helpers. As Helm makes use of `masterminds/semver`, the support for i.e. ranges less mature than the `GitRepository` implementation. - Recorded semver is as defined in the metadata of the chart. The used name for the artifact does however include the checksum of the chart archive, as chart maintainers may not always properly apply semver. - Switches to `sigs.k8s.io/yaml` for YAML operations as this among other things is able to properly unmarshal embedded structures. - Directly requeues on transient errors instead of using the defined interval as a back-off strategy is applied on repeated failures.
This commit is contained in:
parent
f9a35a6613
commit
d378bd1852
|
@ -43,13 +43,15 @@ jobs:
|
|||
run: |
|
||||
kubectl apply -f ./config/samples
|
||||
kubectl -n source-system rollout status deploy/source-controller --timeout=1m
|
||||
kubectl wait gitrepository/podinfo --for=condition=ready --timeout=1m
|
||||
kubectl wait helmrepository/podinfo --for=condition=ready --timeout=1m
|
||||
kubectl wait gitrepository/gitrepository-sample --for=condition=ready --timeout=1m
|
||||
kubectl wait helmrepository/helmrepository-sample --for=condition=ready --timeout=1m
|
||||
kubectl wait helmchart/helmchart-sample --for=condition=ready --timeout=1m
|
||||
kubectl -n source-system logs deploy/source-controller
|
||||
- name: Debug failure
|
||||
if: failure()
|
||||
run: |
|
||||
kubectl get gitrepositories -oyaml
|
||||
kubectl get helmrepositories -oyaml
|
||||
kubectl get helmcharts -oyaml
|
||||
kubectl -n source-system get all
|
||||
kubectl -n source-system logs deploy/source-controller
|
||||
|
|
3
PROJECT
3
PROJECT
|
@ -7,4 +7,7 @@ resources:
|
|||
- group: source
|
||||
kind: HelmRepository
|
||||
version: v1alpha1
|
||||
- group: source
|
||||
kind: HelmChart
|
||||
version: v1alpha1
|
||||
version: "2"
|
||||
|
|
|
@ -76,34 +76,6 @@ type GitRepositoryStatus struct {
|
|||
Artifact *Artifact `json:"artifact,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.spec.url`
|
||||
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
|
||||
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
|
||||
|
||||
// GitRepository is the Schema for the gitrepositories API
|
||||
type GitRepository struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec GitRepositorySpec `json:"spec,omitempty"`
|
||||
Status GitRepositoryStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// GitRepositoryList contains a list of GitRepository
|
||||
// +kubebuilder:object:root=true
|
||||
type GitRepositoryList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []GitRepository `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&GitRepository{}, &GitRepositoryList{})
|
||||
}
|
||||
|
||||
const (
|
||||
GitOperationSucceedReason string = "GitOperationSucceed"
|
||||
GitOperationFailedReason string = "GitOperationFailed"
|
||||
|
@ -153,3 +125,31 @@ func GitRepositoryReadyMessage(repository GitRepository) string {
|
|||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.spec.url`
|
||||
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
|
||||
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
|
||||
|
||||
// GitRepository is the Schema for the gitrepositories API
|
||||
type GitRepository struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec GitRepositorySpec `json:"spec,omitempty"`
|
||||
Status GitRepositoryStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// GitRepositoryList contains a list of GitRepository
|
||||
// +kubebuilder:object:root=true
|
||||
type GitRepositoryList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []GitRepository `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&GitRepository{}, &GitRepositoryList{})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// HelmChartSpec defines the desired state of HelmChart
|
||||
type HelmChartSpec struct {
|
||||
// The name of the Helm chart, as made available by the referenced
|
||||
// Helm repository.
|
||||
// +required
|
||||
Name string `json:"name"`
|
||||
|
||||
// The chart version semver expression, defaults to latest when
|
||||
// omitted.
|
||||
// +optional
|
||||
Version string `json:"version,omitempty"`
|
||||
|
||||
// The name of the HelmRepository the chart is available at.
|
||||
// +required
|
||||
HelmRepositoryRef corev1.LocalObjectReference `json:"helmRepositoryRef"`
|
||||
|
||||
// The interval at which to check the Helm repository for updates.
|
||||
// Defaults to the interval of the Helm repository.
|
||||
// +optional
|
||||
Interval *metav1.Duration `json:"interval,omitempty"`
|
||||
}
|
||||
|
||||
// IntervalOrDefault returns the defined interval on the HelmChartSpec
|
||||
// or the given default.
|
||||
func (s HelmChartSpec) IntervalOrDefault(interval metav1.Duration) metav1.Duration {
|
||||
if s.Interval == nil {
|
||||
return interval
|
||||
}
|
||||
return *s.Interval
|
||||
}
|
||||
|
||||
// HelmChartStatus defines the observed state of HelmChart
|
||||
type HelmChartStatus struct {
|
||||
// +optional
|
||||
Conditions []SourceCondition `json:"conditions,omitempty"`
|
||||
|
||||
// URL is the download link for the last chart pulled.
|
||||
// +optional
|
||||
URL string `json:"url,omitempty"`
|
||||
|
||||
// URI for the artifact of the latest successful chart pull.
|
||||
// +optional
|
||||
Artifact *Artifact `json:"artifact,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
// ChartPullFailedReason represents the fact that the pull of the
|
||||
// Helm chart failed.
|
||||
ChartPullFailedReason string = "ChartPullFailed"
|
||||
|
||||
// ChartPulLSucceededReason represents the fact that the pull of
|
||||
// the Helm chart succeeded.
|
||||
ChartPullSucceededReason string = "ChartPullSucceeded"
|
||||
)
|
||||
|
||||
func HelmChartReady(chart HelmChart, artifact Artifact, url, reason, message string) HelmChart {
|
||||
chart.Status.Conditions = []SourceCondition{
|
||||
{
|
||||
Type: ReadyCondition,
|
||||
Status: corev1.ConditionTrue,
|
||||
LastTransitionTime: metav1.Now(),
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
},
|
||||
}
|
||||
chart.Status.URL = url
|
||||
|
||||
if chart.Status.Artifact != nil {
|
||||
if chart.Status.Artifact.Path != artifact.Path {
|
||||
chart.Status.Artifact = &artifact
|
||||
}
|
||||
} else {
|
||||
chart.Status.Artifact = &artifact
|
||||
}
|
||||
|
||||
return chart
|
||||
}
|
||||
|
||||
func HelmChartNotReady(chart HelmChart, reason, message string) HelmChart {
|
||||
chart.Status.Conditions = []SourceCondition{
|
||||
{
|
||||
Type: ReadyCondition,
|
||||
Status: corev1.ConditionFalse,
|
||||
LastTransitionTime: metav1.Now(),
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
},
|
||||
}
|
||||
return chart
|
||||
}
|
||||
|
||||
func HelmChartReadyMessage(chart HelmChart) string {
|
||||
for _, condition := range chart.Status.Conditions {
|
||||
if condition.Type == ReadyCondition {
|
||||
return condition.Message
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Name",type=string,JSONPath=`.spec.name`
|
||||
// +kubebuilder:printcolumn:name="Version",type=string,JSONPath=`.spec.version`
|
||||
// +kubebuilder:printcolumn:name="Repository",type=string,JSONPath=`.spec.helmRepositoryRef.name`
|
||||
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
|
||||
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
|
||||
|
||||
// HelmChart is the Schema for the helmcharts API
|
||||
type HelmChart struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec HelmChartSpec `json:"spec,omitempty"`
|
||||
Status HelmChartStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// HelmChartList contains a list of HelmChart
|
||||
type HelmChartList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []HelmChart `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&HelmChart{}, &HelmChartList{})
|
||||
}
|
|
@ -47,34 +47,6 @@ type HelmRepositoryStatus struct {
|
|||
Artifact *Artifact `json:"artifact,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.spec.url`
|
||||
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
|
||||
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
|
||||
|
||||
// HelmRepository is the Schema for the helmrepositories API
|
||||
type HelmRepository struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec HelmRepositorySpec `json:"spec,omitempty"`
|
||||
Status HelmRepositoryStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// HelmRepositoryList contains a list of HelmRepository
|
||||
// +kubebuilder:object:root=true
|
||||
type HelmRepositoryList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []HelmRepository `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&HelmRepository{}, &HelmRepositoryList{})
|
||||
}
|
||||
|
||||
const (
|
||||
// IndexationFailedReason represents the fact that the indexation
|
||||
// of the given Helm repository failed.
|
||||
|
@ -129,3 +101,31 @@ func HelmRepositoryReadyMessage(repository HelmRepository) string {
|
|||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.spec.url`
|
||||
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
|
||||
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
|
||||
|
||||
// HelmRepository is the Schema for the helmrepositories API
|
||||
type HelmRepository struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec HelmRepositorySpec `json:"spec,omitempty"`
|
||||
Status HelmRepositoryStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// HelmRepositoryList contains a list of HelmRepository
|
||||
// +kubebuilder:object:root=true
|
||||
type HelmRepositoryList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []HelmRepository `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&HelmRepository{}, &HelmRepositoryList{})
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ package v1alpha1
|
|||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
|
@ -168,6 +169,113 @@ func (in *GitRepositoryStatus) DeepCopy() *GitRepositoryStatus {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChart) DeepCopyInto(out *HelmChart) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChart.
|
||||
func (in *HelmChart) DeepCopy() *HelmChart {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChart)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *HelmChart) 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 *HelmChartList) DeepCopyInto(out *HelmChartList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]HelmChart, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartList.
|
||||
func (in *HelmChartList) DeepCopy() *HelmChartList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *HelmChartList) 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 *HelmChartSpec) DeepCopyInto(out *HelmChartSpec) {
|
||||
*out = *in
|
||||
out.HelmRepositoryRef = in.HelmRepositoryRef
|
||||
if in.Interval != nil {
|
||||
in, out := &in.Interval, &out.Interval
|
||||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartSpec.
|
||||
func (in *HelmChartSpec) DeepCopy() *HelmChartSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmChartStatus) DeepCopyInto(out *HelmChartStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]SourceCondition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Artifact != nil {
|
||||
in, out := &in.Artifact, &out.Artifact
|
||||
*out = new(Artifact)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmChartStatus.
|
||||
func (in *HelmChartStatus) DeepCopy() *HelmChartStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HelmChartStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HelmRepository) DeepCopyInto(out *HelmRepository) {
|
||||
*out = *in
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.2.5
|
||||
creationTimestamp: null
|
||||
name: helmcharts.source.fluxcd.io
|
||||
spec:
|
||||
additionalPrinterColumns:
|
||||
- JSONPath: .spec.name
|
||||
name: Name
|
||||
type: string
|
||||
- JSONPath: .spec.version
|
||||
name: Version
|
||||
type: string
|
||||
- JSONPath: .spec.helmRepositoryRef.name
|
||||
name: Repository
|
||||
type: string
|
||||
- JSONPath: .status.conditions[?(@.type=="Ready")].status
|
||||
name: Ready
|
||||
type: string
|
||||
- JSONPath: .status.conditions[?(@.type=="Ready")].message
|
||||
name: Status
|
||||
type: string
|
||||
- JSONPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
group: source.fluxcd.io
|
||||
names:
|
||||
kind: HelmChart
|
||||
listKind: HelmChartList
|
||||
plural: helmcharts
|
||||
singular: helmchart
|
||||
scope: Namespaced
|
||||
subresources:
|
||||
status: {}
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
description: HelmChart is the Schema for the helmcharts 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: HelmChartSpec defines the desired state of HelmChart
|
||||
properties:
|
||||
helmRepositoryRef:
|
||||
description: The name of the HelmRepository the chart is available at.
|
||||
properties:
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?'
|
||||
type: string
|
||||
type: object
|
||||
interval:
|
||||
description: The interval at which to check the Helm repository for
|
||||
updates. Defaults to the interval of the Helm repository.
|
||||
type: string
|
||||
name:
|
||||
description: The name of the Helm chart, as made available by the referenced
|
||||
Helm repository.
|
||||
type: string
|
||||
version:
|
||||
description: The chart version semver expression, defaults to latest
|
||||
when omitted.
|
||||
type: string
|
||||
required:
|
||||
- helmRepositoryRef
|
||||
- name
|
||||
type: object
|
||||
status:
|
||||
description: HelmChartStatus defines the observed state of HelmChart
|
||||
properties:
|
||||
artifact:
|
||||
description: URI for the artifact of the latest successful chart pull.
|
||||
properties:
|
||||
lastUpdateTime:
|
||||
description: LastUpdateTime is the timestamp corresponding to the
|
||||
last update of this artifact.
|
||||
format: date-time
|
||||
type: string
|
||||
path:
|
||||
description: Path is the local file path of this artifact.
|
||||
type: string
|
||||
revision:
|
||||
description: Revision is a human readable identifier traceable in
|
||||
the origin source system. It can be a commit sha, git tag, a helm
|
||||
index timestamp, a helm chart version, a checksum, etc.
|
||||
type: string
|
||||
url:
|
||||
description: URL is the HTTP address of this artifact.
|
||||
type: string
|
||||
required:
|
||||
- path
|
||||
- url
|
||||
type: object
|
||||
conditions:
|
||||
items:
|
||||
description: SourceCondition contains condition information for a
|
||||
source
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: LastTransitionTime is the timestamp corresponding
|
||||
to the last status change of this condition.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: Message is a human readable description of the details
|
||||
of the last transition, complementing reason.
|
||||
type: string
|
||||
reason:
|
||||
description: Reason is a brief machine readable explanation for
|
||||
the condition's last transition.
|
||||
type: string
|
||||
status:
|
||||
description: Status of the condition, one of ('True', 'False',
|
||||
'Unknown').
|
||||
type: string
|
||||
type:
|
||||
description: Type of the condition, currently ('Ready').
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
url:
|
||||
description: URL is the download link for the last chart pulled.
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
|
@ -3,5 +3,6 @@ kind: Kustomization
|
|||
resources:
|
||||
- bases/source.fluxcd.io_gitrepositories.yaml
|
||||
- bases/source.fluxcd.io_helmrepositories.yaml
|
||||
- bases/source.fluxcd.io_helmcharts.yaml
|
||||
# +kubebuilder:scaffold:crdkustomizeresource
|
||||
|
||||
|
|
|
@ -6,4 +6,5 @@ resources:
|
|||
- deployment.yaml
|
||||
images:
|
||||
- name: fluxcd/source-controller
|
||||
newTag: 0.0.1-alpha.1
|
||||
newName: fluxcd/source-controller
|
||||
newTag: latest
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# permissions for end users to edit helmcharts.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: helmchart-editor-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- source.fluxcd.io
|
||||
resources:
|
||||
- helmcharts
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- source.fluxcd.io
|
||||
resources:
|
||||
- helmcharts/status
|
||||
verbs:
|
||||
- get
|
|
@ -0,0 +1,20 @@
|
|||
# permissions for end users to view helmcharts.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: helmchart-viewer-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- source.fluxcd.io
|
||||
resources:
|
||||
- helmcharts
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- source.fluxcd.io
|
||||
resources:
|
||||
- helmcharts/status
|
||||
verbs:
|
||||
- get
|
|
@ -26,6 +26,26 @@ rules:
|
|||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- source.fluxcd.io
|
||||
resources:
|
||||
- helmcharts
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- source.fluxcd.io
|
||||
resources:
|
||||
- helmcharts/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- source.fluxcd.io
|
||||
resources:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
apiVersion: source.fluxcd.io/v1alpha1
|
||||
kind: GitRepository
|
||||
metadata:
|
||||
name: podinfo
|
||||
name: gitrepository-sample
|
||||
annotations:
|
||||
source.fluxcd.io/syncAt: "2020-04-06T15:39:52+03:00"
|
||||
spec:
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: source.fluxcd.io/v1alpha1
|
||||
kind: HelmChart
|
||||
metadata:
|
||||
name: helmchart-sample
|
||||
spec:
|
||||
name: podinfo
|
||||
version: '^2.0.0'
|
||||
helmRepositoryRef:
|
||||
name: helmrepository-sample
|
|
@ -1,7 +1,7 @@
|
|||
apiVersion: source.fluxcd.io/v1alpha1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: podinfo
|
||||
name: helmrepository-sample
|
||||
spec:
|
||||
interval: 1m
|
||||
url: https://stefanprodan.github.io/podinfo
|
||||
|
|
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
Copyright 2020 The Flux CD contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/pkg/errors"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
|
||||
)
|
||||
|
||||
// HelmChartReconciler reconciles a HelmChart object
|
||||
type HelmChartReconciler struct {
|
||||
client.Client
|
||||
Log logr.Logger
|
||||
Scheme *runtime.Scheme
|
||||
Storage *Storage
|
||||
Kind string
|
||||
Getters getter.Providers
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=source.fluxcd.io,resources=helmcharts,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=source.fluxcd.io,resources=helmcharts/status,verbs=get;update;patch
|
||||
|
||||
func (r *HelmChartReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var chart sourcev1.HelmChart
|
||||
if err := r.Get(ctx, req.NamespacedName, &chart); err != nil {
|
||||
return ctrl.Result{Requeue: true}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
log := r.Log.WithValues(chart.Kind, req.NamespacedName)
|
||||
|
||||
// set initial status
|
||||
if reset, status := r.shouldResetStatus(chart); reset {
|
||||
log.Info("Initializing Helm chart")
|
||||
chart.Status = status
|
||||
if err := r.Status().Update(ctx, &chart); err != nil {
|
||||
log.Error(err, "unable to update HelmChart status")
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
}
|
||||
|
||||
// try to remove old artifacts
|
||||
r.gc(chart)
|
||||
|
||||
repository, err := r.chartRepository(ctx, chart)
|
||||
if err != nil {
|
||||
chart = sourcev1.HelmChartNotReady(*chart.DeepCopy(), sourcev1.ChartPullFailedReason, err.Error())
|
||||
if err := r.Status().Update(ctx, &chart); err != nil {
|
||||
log.Error(err, "unable to update HelmChart status")
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
|
||||
// try to pull chart
|
||||
pulledChart, err := r.sync(repository, *chart.DeepCopy())
|
||||
if err != nil {
|
||||
log.Info("Helm chart pull failed", "error", err.Error())
|
||||
}
|
||||
|
||||
// update status
|
||||
if err := r.Status().Update(ctx, &pulledChart); err != nil {
|
||||
log.Error(err, "unable to update HelmChart status")
|
||||
return ctrl.Result{Requeue: true}, err
|
||||
}
|
||||
|
||||
log.Info("Helm chart sync succeeded", "msg", sourcev1.HelmChartReadyMessage(pulledChart))
|
||||
|
||||
// requeue chart
|
||||
return ctrl.Result{RequeueAfter: repository.Spec.Interval.Duration}, nil
|
||||
}
|
||||
|
||||
func (r *HelmChartReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&sourcev1.HelmChart{}).
|
||||
WithEventFilter(RepositoryChangePredicate{}).
|
||||
WithEventFilter(predicate.Funcs{
|
||||
DeleteFunc: func(e event.DeleteEvent) bool {
|
||||
// delete artifacts
|
||||
artifact := r.Storage.ArtifactFor(r.Kind, e.Meta, "", "")
|
||||
if err := r.Storage.RemoveAll(artifact); err != nil {
|
||||
r.Log.Error(err, "unable to delete artifacts",
|
||||
r.Kind, fmt.Sprintf("%s/%s", e.Meta.GetNamespace(), e.Meta.GetName()))
|
||||
} else {
|
||||
r.Log.Info("Helm chart artifacts deleted",
|
||||
r.Kind, fmt.Sprintf("%s/%s", e.Meta.GetNamespace(), e.Meta.GetName()))
|
||||
}
|
||||
return false
|
||||
},
|
||||
}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *HelmChartReconciler) sync(repository sourcev1.HelmRepository, chart sourcev1.HelmChart) (sourcev1.HelmChart, error) {
|
||||
indexBytes, err := ioutil.ReadFile(repository.Status.Artifact.Path)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to read Helm repository index file: %w", err)
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
index := &repo.IndexFile{}
|
||||
if err := yaml.Unmarshal(indexBytes, index); err != nil {
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
// find referenced chart in index
|
||||
cv, err := index.Get(chart.Spec.Name, chart.Spec.Version)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case repo.ErrNoChartName:
|
||||
err = fmt.Errorf("chart '%s' could not be found in Helm repository '%s'", chart.Spec.Name, repository.Name)
|
||||
case repo.ErrNoChartVersion:
|
||||
err = fmt.Errorf("no chart with version '%s' found for '%s'", chart.Spec.Version, chart.Spec.Name)
|
||||
}
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.ChartPullFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
if len(cv.URLs) == 0 {
|
||||
err = fmt.Errorf("chart '%s' has no downloadable URLs", cv.Name)
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.ChartPullFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
// TODO(hidde): according to the Helm source the first item is not
|
||||
// always the correct one to pick, check for updates once in awhile.
|
||||
ref := cv.URLs[0]
|
||||
u, err := url.Parse(ref)
|
||||
if err != nil {
|
||||
err = errors.Errorf("invalid chart URL format '%s': %w", ref, err)
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.ChartPullFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
// TODO(hidde): auth options from Helm repository probably need to be
|
||||
// substituted here
|
||||
c, err := r.Getters.ByScheme(u.Scheme)
|
||||
if err != nil {
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.ChartPullFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
res, err := c.Get(u.String(), getter.WithURL(repository.Spec.URL))
|
||||
if err != nil {
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.ChartPullFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
chartBytes, err := ioutil.ReadAll(res)
|
||||
if err != nil {
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.ChartPullFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
sum := r.Storage.Checksum(chartBytes)
|
||||
artifact := r.Storage.ArtifactFor(chart.Kind, chart.GetObjectMeta(),
|
||||
fmt.Sprintf("%s-%s-%s.tgz", cv.Name, cv.Version, sum), cv.Version)
|
||||
|
||||
// create artifact dir
|
||||
err = r.Storage.MkdirAll(artifact)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to create chart directory: %w", err)
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.ChartPullFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
// acquire lock
|
||||
unlock, err := r.Storage.Lock(artifact)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to acquire lock: %w", err)
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.ChartPullFailedReason, err.Error()), err
|
||||
}
|
||||
defer unlock()
|
||||
|
||||
// save artifact to storage
|
||||
err = r.Storage.WriteFile(artifact, chartBytes)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to write chart file: %w", err)
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.ChartPullFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
// update index symlink
|
||||
chartUrl, err := r.Storage.Symlink(artifact, fmt.Sprintf("%s-latest.tgz", cv.Name))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("storage error: %w", err)
|
||||
return sourcev1.HelmChartNotReady(chart, sourcev1.StorageOperationFailedReason, err.Error()), err
|
||||
}
|
||||
|
||||
message := fmt.Sprintf("Helm chart is available at: %s", artifact.Path)
|
||||
return sourcev1.HelmChartReady(chart, artifact, chartUrl, sourcev1.ChartPullSucceededReason, message), nil
|
||||
}
|
||||
|
||||
func (r *HelmChartReconciler) chartRepository(ctx context.Context, chart sourcev1.HelmChart) (sourcev1.HelmRepository, error) {
|
||||
if chart.Spec.HelmRepositoryRef.Name == "" {
|
||||
return sourcev1.HelmRepository{}, fmt.Errorf("no HelmRepository reference given")
|
||||
}
|
||||
|
||||
name := types.NamespacedName{
|
||||
Namespace: chart.GetNamespace(),
|
||||
Name: chart.Spec.HelmRepositoryRef.Name,
|
||||
}
|
||||
|
||||
var repository sourcev1.HelmRepository
|
||||
err := r.Client.Get(ctx, name, &repository)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to get HelmRepository '%s': %w", name, err)
|
||||
}
|
||||
|
||||
if repository.Status.Artifact == nil {
|
||||
err = fmt.Errorf("no repository index artifect found in HelmRepository '%s'", repository.Name)
|
||||
}
|
||||
|
||||
return repository, err
|
||||
}
|
||||
|
||||
func (r *HelmChartReconciler) shouldResetStatus(chart sourcev1.HelmChart) (bool, sourcev1.HelmChartStatus) {
|
||||
resetStatus := false
|
||||
if chart.Status.Artifact != nil {
|
||||
if !r.Storage.ArtifactExist(*chart.Status.Artifact) {
|
||||
resetStatus = true
|
||||
}
|
||||
}
|
||||
|
||||
// set initial status
|
||||
if len(chart.Status.Conditions) == 0 || resetStatus {
|
||||
resetStatus = true
|
||||
}
|
||||
|
||||
return resetStatus, sourcev1.HelmChartStatus{
|
||||
Conditions: []sourcev1.SourceCondition{
|
||||
{
|
||||
Type: sourcev1.ReadyCondition,
|
||||
Status: corev1.ConditionUnknown,
|
||||
Reason: sourcev1.InitializingReason,
|
||||
LastTransitionTime: metav1.Now(),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *HelmChartReconciler) gc(chart sourcev1.HelmChart) {
|
||||
if chart.Status.Artifact != nil {
|
||||
if err := r.Storage.RemoveAllButCurrent(*chart.Status.Artifact); err != nil {
|
||||
r.Log.Info("Artifacts GC failed", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,7 +25,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"gopkg.in/yaml.v2"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
@ -35,6 +34,7 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
|
||||
)
|
||||
|
|
|
@ -68,6 +68,9 @@ var _ = BeforeSuite(func(done Done) {
|
|||
err = sourcev1.AddToScheme(scheme.Scheme)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = sourcev1.AddToScheme(scheme.Scheme)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// +kubebuilder:scaffold:scheme
|
||||
|
||||
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
|
||||
|
|
3
go.mod
3
go.mod
|
@ -8,10 +8,11 @@ require (
|
|||
github.com/go-logr/logr v0.1.0
|
||||
github.com/onsi/ginkgo v1.11.0
|
||||
github.com/onsi/gomega v1.8.1
|
||||
gopkg.in/yaml.v2 v2.2.4
|
||||
github.com/pkg/errors v0.9.1
|
||||
helm.sh/helm/v3 v3.1.2
|
||||
k8s.io/api v0.17.2
|
||||
k8s.io/apimachinery v0.17.2
|
||||
k8s.io/client-go v0.17.2
|
||||
sigs.k8s.io/controller-runtime v0.5.0
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
)
|
||||
|
|
7
go.sum
7
go.sum
|
@ -4,6 +4,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
|||
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
|
@ -116,6 +117,7 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ
|
|||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
|
||||
|
@ -275,6 +277,7 @@ 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.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
|
||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
|
@ -295,6 +298,7 @@ github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT
|
|||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
|
@ -410,6 +414,7 @@ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX
|
|||
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
|
@ -629,8 +634,8 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
helm.sh/helm v2.16.5+incompatible h1:cWIBFS2bwUEgWSIggw4gvwdWFH0BLE54X7/3WszxjWc=
|
||||
helm.sh/helm/v3 v3.1.2 h1:VpNzaNv2DX4aRnOCcV7v5Of+XT2SZrJ8iOQ25AGKOos=
|
||||
helm.sh/helm/v3 v3.1.2/go.mod h1:WYsFJuMASa/4XUqLyv54s0U/f3mlAaRErGmyy4z921g=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
24
main.go
24
main.go
|
@ -41,6 +41,12 @@ import (
|
|||
var (
|
||||
scheme = runtime.NewScheme()
|
||||
setupLog = ctrl.Log.WithName("setup")
|
||||
getters = getter.Providers{
|
||||
getter.Provider{
|
||||
Schemes: []string{"http", "https"},
|
||||
New: getter.NewHTTPGetter,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -98,16 +104,22 @@ func main() {
|
|||
Scheme: mgr.GetScheme(),
|
||||
Kind: "helmrepository",
|
||||
Storage: storage,
|
||||
Getters: getter.Providers{
|
||||
getter.Provider{
|
||||
Schemes: []string{"http", "https"},
|
||||
New: getter.NewHTTPGetter,
|
||||
},
|
||||
},
|
||||
Getters: getters,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "HelmRepository")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err = (&controllers.HelmChartReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Log: ctrl.Log.WithName("controllers").WithName("HelmChart"),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Kind: "helmchart",
|
||||
Storage: storage,
|
||||
Getters: getters,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "HelmChart")
|
||||
os.Exit(1)
|
||||
}
|
||||
// +kubebuilder:scaffold:builder
|
||||
|
||||
setupLog.Info("starting manager")
|
||||
|
|
Loading…
Reference in New Issue