Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🌱 Make latest check optional behind followLatest field #688

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,12 @@ generate-manifests-api: controller-gen ## Generate ClusterRole and CustomResourc
output:crd:artifacts:config=./config/crd/bases \
output:rbac:dir=./config/rbac \

.PHONY: generate-manifests-chart
generate-manifests-chart: controller-gen kustomize
$(KUSTOMIZE) build ./config/chart > $(CHART_DIR)/templates/rancher-turtles-components.yaml
$(KUSTOMIZE) build ./config/exp/etcdrestore > $(CHART_DIR)/templates/rancher-turtles-exp-etcdrestore-components.yaml
./scripts/process-exp-etcdrestore-manifests.sh $(CHART_DIR)/templates/rancher-turtles-exp-etcdrestore-components.yaml

.PHONY: generate-exp-etcdrestore-manifests-api
generate-exp-etcdrestore-manifests-api: controller-gen ## Generate ClusterRole and CustomResourceDefinition objects for experimental API.
$(CONTROLLER_GEN) rbac:roleName=manager-role crd paths="./exp/etcdrestore/api/..." \
Expand Down Expand Up @@ -521,10 +527,7 @@ release: clean-release $(RELEASE_DIR) ## Builds and push container images using
$(MAKE) release-chart

.PHONY: build-chart
build-chart: $(HELM) $(KUSTOMIZE) $(RELEASE_DIR) $(CHART_RELEASE_DIR) $(CHART_PACKAGE_DIR) ## Builds the chart to publish with a release
$(KUSTOMIZE) build ./config/chart > $(CHART_DIR)/templates/rancher-turtles-components.yaml
$(KUSTOMIZE) build ./config/exp/etcdrestore > $(CHART_DIR)/templates/rancher-turtles-exp-etcdrestore-components.yaml
./scripts/process-exp-etcdrestore-manifests.sh $(CHART_DIR)/templates/rancher-turtles-exp-etcdrestore-components.yaml
build-chart: $(HELM) $(KUSTOMIZE) $(RELEASE_DIR) $(CHART_RELEASE_DIR) $(CHART_PACKAGE_DIR) generate-manifests-chart ## Builds the chart to publish with a release
cp -rf $(CHART_DIR)/* $(CHART_RELEASE_DIR)
sed -i'' -e 's@image: .*@image: '"$(CONTROLLER_IMG)"'@' $(CHART_RELEASE_DIR)/values.yaml
sed -i'' -e 's@imageVersion: .*@imageVersion: '"$(CONTROLLER_IMAGE_VERSION)"'@' $(CHART_RELEASE_DIR)/values.yaml
Expand Down
6 changes: 6 additions & 0 deletions api/v1alpha1/capiprovider_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ type CAPIProviderSpec struct {
// +kubebuilder:example={CLUSTER_TOPOLOGY:"true",EXP_CLUSTER_RESOURCE_SET:"true",EXP_MACHINE_POOL: "true"}
Variables map[string]string `json:"variables,omitempty"`

// FollowLatest allows to track current latest version, and update provider accordingly to keep
// manifests in sync with new releases. Maximum version of the released components is limited by the provider
// override in the clusterctl.yaml data within clusterctl-config `ConfigMap`, or the fetchConfig.url.
// +optional
FollowLatest bool `json:"followLatest,omitempty"`

// ProviderSpec is the spec of the underlying CAPI Provider resource.
operatorv1.ProviderSpec `json:",inline"`
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ metadata:
"helm.sh/hook-weight": "2"
spec:
type: addon
followLatest: true
additionalManifests:
name: fleet-addon-config
namespace: '{{ .Values.rancherTurtles.namespace }}'
Expand Down
1 change: 1 addition & 0 deletions charts/rancher-turtles/templates/core-provider.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ metadata:
"helm.sh/hook-weight": "2"
spec:
name: cluster-api
followLatest: true
type: core
version: {{ index .Values "cluster-api-operator" "cluster-api" "version" }}
additionalManifests:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2769,6 +2769,12 @@ spec:
desired version of the release from GitHub.
type: string
type: object
followLatest:
description: |-
FollowLatest allows to track current latest version, and update provider accordingly to keep
manifests in sync with new releases. Maximum version of the released components is limited by the provider
override in the clusterctl.yaml data within clusterctl-config `ConfigMap`, or the fetchConfig.url.
type: boolean
manager:
description: Manager defines the properties that can be enabled on
the controller manager for the provider.
Expand Down
1 change: 1 addition & 0 deletions charts/rancher-turtles/templates/rke2-bootstrap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ metadata:
spec:
name: rke2
type: bootstrap
followLatest: true
{{- if index .Values "cluster-api-operator" "cluster-api" "rke2" "version" }}
version: {{ index .Values "cluster-api-operator" "cluster-api" "rke2" "version" }}
{{- end }}
Expand Down
1 change: 1 addition & 0 deletions charts/rancher-turtles/templates/rke2-controlplane.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ metadata:
spec:
name: rke2
type: controlPlane
followLatest: true
{{- if index .Values "cluster-api-operator" "cluster-api" "rke2" "version" }}
version: {{ index .Values "cluster-api-operator" "cluster-api" "rke2" "version" }}
{{- end }}
Expand Down
6 changes: 6 additions & 0 deletions config/crd/bases/turtles-capi.cattle.io_capiproviders.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2769,6 +2769,12 @@ spec:
desired version of the release from GitHub.
type: string
type: object
followLatest:
description: |-
FollowLatest allows to track current latest version, and update provider accordingly to keep
manifests in sync with new releases. Maximum version of the released components is limited by the provider
override in the clusterctl.yaml data within clusterctl-config `ConfigMap`, or the fetchConfig.url.
type: boolean
manager:
description: Manager defines the properties that can be enabled on
the controller manager for the provider.
Expand Down
2 changes: 1 addition & 1 deletion internal/sync/provider_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func (s *ProviderSync) rolloutInfrastructure() {

func (s *ProviderSync) updateLatestVersion(ctx context.Context) error {
// Skip for user specified versions
if s.Source.Spec.Version != "" {
if s.Source.Spec.Version != "" || !s.Source.Spec.FollowLatest {
return nil
}

Expand Down
84 changes: 53 additions & 31 deletions internal/sync/provider_sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ var _ = Describe("Provider sync", func() {
ns *corev1.Namespace
otherNs *corev1.Namespace
capiProvider *turtlesv1.CAPIProvider
capiProviderFollowLatest *turtlesv1.CAPIProvider
capiProviderAzure *turtlesv1.CAPIProvider
capiProviderDuplicate *turtlesv1.CAPIProvider
infrastructure *operatorv1.InfrastructureProvider
infrastructureFollowLatest *operatorv1.InfrastructureProvider
infrastructureStatusOutdated operatorv1.ProviderStatus
infrastructureDuplicate *operatorv1.InfrastructureProvider
infrastructureAzure *operatorv1.InfrastructureProvider
Expand Down Expand Up @@ -73,6 +75,10 @@ var _ = Describe("Provider sync", func() {
capiProviderDuplicate = capiProvider.DeepCopy()
capiProviderDuplicate.Namespace = otherNs.Name

capiProviderFollowLatest = capiProvider.DeepCopy()
capiProviderFollowLatest.Name = "latest"
capiProviderFollowLatest.Spec.FollowLatest = true

infrastructure = &operatorv1.InfrastructureProvider{ObjectMeta: metav1.ObjectMeta{
Name: string(capiProvider.Spec.Name),
Namespace: ns.Name,
Expand All @@ -88,6 +94,11 @@ var _ = Describe("Provider sync", func() {
Namespace: otherNs.Name,
}}

infrastructureFollowLatest = &operatorv1.InfrastructureProvider{ObjectMeta: metav1.ObjectMeta{
Name: string(capiProviderFollowLatest.Spec.Name),
Namespace: otherNs.Name,
}}

infrastructureStatusOutdated = operatorv1.ProviderStatus{
Conditions: clusterv1.Conditions{
{
Expand All @@ -103,6 +114,7 @@ var _ = Describe("Provider sync", func() {
},
}

Expect(testEnv.Client.Create(ctx, capiProviderFollowLatest)).To(Succeed())
Expect(testEnv.Client.Create(ctx, capiProvider)).To(Succeed())
Expect(testEnv.Client.Create(ctx, capiProviderDuplicate)).To(Succeed())
Expect(testEnv.Client.Create(ctx, capiProviderAzure)).To(Succeed())
Expand Down Expand Up @@ -169,26 +181,25 @@ var _ = Describe("Provider sync", func() {
g.Expect(s.Sync(ctx)).To(Succeed())
s.Apply(ctx, &err)
g.Expect(conditions.IsTrue(capiProvider, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue())
g.Expect(conditions.IsTrue(capiProvider, turtlesv1.CheckLatestVersionTime)).To(BeTrue())
g.Expect(capiProvider.Status.Conditions).To(HaveLen(2))
g.Expect(capiProvider.Status.Conditions).To(HaveLen(1))
g.Expect(capiProvider).To(HaveField("Status.Phase", Equal(turtlesv1.Provisioning)))
}).Should(Succeed())
})

It("Should update outdated condition, maintain last applied time and empty the hash annotation", func() {
capiProvider.Status.ProviderStatus = infrastructureStatusOutdated
capiProviderFollowLatest.Status.ProviderStatus = infrastructureStatusOutdated

appliedCondition := conditions.Get(capiProvider, turtlesv1.LastAppliedConfigurationTime)
lastVersionCheckCondition := conditions.Get(capiProvider, turtlesv1.CheckLatestVersionTime)
appliedCondition := conditions.Get(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime)
lastVersionCheckCondition := conditions.Get(capiProviderFollowLatest, turtlesv1.CheckLatestVersionTime)

Eventually(testEnv.Status().Update(ctx, capiProvider)).Should(Succeed())
Eventually(testEnv.Status().Update(ctx, capiProviderFollowLatest)).Should(Succeed())
Eventually(func(g Gomega) {
g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(capiProvider), capiProvider)).To(Succeed())
g.Expect(conditions.Get(capiProvider, turtlesv1.LastAppliedConfigurationTime)).ToNot(BeNil())
g.Expect(conditions.Get(capiProvider, turtlesv1.LastAppliedConfigurationTime).LastTransitionTime.Second()).To(Equal(appliedCondition.LastTransitionTime.Second()))
g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(capiProviderFollowLatest), capiProviderFollowLatest)).To(Succeed())
g.Expect(conditions.Get(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime)).ToNot(BeNil())
g.Expect(conditions.Get(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime).LastTransitionTime.Second()).To(Equal(appliedCondition.LastTransitionTime.Second()))
}).Should(Succeed())

s := sync.NewProviderSync(testEnv, capiProvider)
s := sync.NewProviderSync(testEnv, capiProviderFollowLatest)

dest := &operatorv1.InfrastructureProvider{}
Eventually(func(g Gomega) {
Expand All @@ -200,48 +211,61 @@ var _ = Describe("Provider sync", func() {
g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(infrastructure), dest)).To(Succeed())
g.Expect(dest.GetAnnotations()).To(HaveKeyWithValue(sync.AppliedSpecHashAnnotation, ""))
g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(infrastructure), dest)).To(Succeed())
g.Expect(capiProvider.Status.Conditions).To(HaveLen(2))
g.Expect(conditions.IsTrue(capiProvider, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue())
g.Expect(conditions.IsTrue(capiProvider, turtlesv1.CheckLatestVersionTime)).To(BeTrue())
g.Expect(conditions.Get(capiProvider, turtlesv1.CheckLatestVersionTime).LastTransitionTime.Equal(
g.Expect(capiProviderFollowLatest.Status.Conditions).To(HaveLen(2))
g.Expect(conditions.IsTrue(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue())
g.Expect(conditions.IsTrue(capiProviderFollowLatest, turtlesv1.CheckLatestVersionTime)).To(BeTrue())
g.Expect(conditions.Get(capiProviderFollowLatest, turtlesv1.CheckLatestVersionTime).LastTransitionTime.Equal(
&lastVersionCheckCondition.LastTransitionTime)).To(BeTrue())
g.Expect(conditions.Get(capiProvider, turtlesv1.LastAppliedConfigurationTime).LastTransitionTime.After(
g.Expect(conditions.Get(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime).LastTransitionTime.After(
appliedCondition.LastTransitionTime.Time)).To(BeTrue())
}).Should(Succeed())

Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(capiProvider), capiProvider)).To(Succeed())
condition := conditions.Get(capiProvider, turtlesv1.LastAppliedConfigurationTime)
lastVersionCheckCondition = conditions.Get(capiProvider, turtlesv1.CheckLatestVersionTime)
Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(capiProviderFollowLatest), capiProviderFollowLatest)).To(Succeed())
condition := conditions.Get(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime)
lastVersionCheckCondition = conditions.Get(capiProviderFollowLatest, turtlesv1.CheckLatestVersionTime)

Consistently(func(g Gomega) {
err = nil
g.Expect(s.Get(ctx)).To(Succeed())
g.Expect(s.Sync(ctx)).To(Succeed())
s.Apply(ctx, &err)
g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(capiProvider), capiProvider)).To(Succeed())
g.Expect(conditions.Get(capiProvider, turtlesv1.LastAppliedConfigurationTime)).To(Equal(condition))
g.Expect(conditions.Get(capiProvider, turtlesv1.CheckLatestVersionTime)).To(Equal(lastVersionCheckCondition))
g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(capiProviderFollowLatest), capiProviderFollowLatest)).To(Succeed())
g.Expect(conditions.Get(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime)).To(Equal(condition))
g.Expect(conditions.Get(capiProviderFollowLatest, turtlesv1.CheckLatestVersionTime)).To(Equal(lastVersionCheckCondition))
}, 5*time.Second).Should(Succeed())
})

It("Should set the last applied version check condition and empty the version field", func() {
s := sync.NewProviderSync(testEnv, capiProvider)
s := sync.NewProviderSync(testEnv, capiProviderFollowLatest)

infrastructure.Spec.Version = "v1.2.3"
infrastructureFollowLatest.Spec.Version = "v1.2.3"

Expect(testEnv.Create(ctx, infrastructure)).To(Succeed())
Expect(testEnv.Create(ctx, infrastructureFollowLatest)).To(Succeed())

Eventually(func(g Gomega) {
err = nil
g.Expect(s.Get(ctx)).To(Succeed())
g.Expect(s.Sync(ctx)).To(Succeed())
s.Apply(ctx, &err)
g.Expect(conditions.IsTrue(capiProvider, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue())
g.Expect(conditions.IsTrue(capiProvider, turtlesv1.CheckLatestVersionTime)).To(BeTrue())
g.Expect(capiProvider.Status.Conditions).To(HaveLen(2))
g.Expect(conditions.IsTrue(capiProviderFollowLatest, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue())
g.Expect(conditions.IsTrue(capiProviderFollowLatest, turtlesv1.CheckLatestVersionTime)).To(BeTrue())
g.Expect(capiProviderFollowLatest.Status.Conditions).To(HaveLen(2))
}, 5*time.Second).Should(Succeed())
})

It("Should not set the last applied version check, if the FollowLatest is not set", func() {
s := sync.NewProviderSync(testEnv, capiProvider)

Consistently(func(g Gomega) {
err = nil
g.Expect(s.Get(ctx)).To(Succeed())
g.Expect(s.Sync(ctx)).To(Succeed())
s.Apply(ctx, &err)
g.Expect(conditions.IsTrue(capiProvider, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue())
g.Expect(capiProvider.Status.Conditions).To(HaveLen(1))
}).Should(Succeed())
})

It("Should individually sync every provider", func() {
Expect(testEnv.Client.Create(ctx, infrastructure.DeepCopy())).To(Succeed())
Eventually(UpdateStatus(infrastructure, func() {
Expand All @@ -261,9 +285,8 @@ var _ = Describe("Provider sync", func() {
g.Expect(s.Sync(ctx)).To(Succeed())
s.Apply(ctx, &err)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(capiProvider.Status.Conditions).To(HaveLen(2))
g.Expect(capiProvider.Status.Conditions).To(HaveLen(1))
g.Expect(conditions.IsTrue(capiProvider, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue())
g.Expect(conditions.IsTrue(capiProvider, turtlesv1.CheckLatestVersionTime)).To(BeTrue())
g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(infrastructure), dest)).To(Succeed())
}).Should(Succeed())

Expand All @@ -278,9 +301,8 @@ var _ = Describe("Provider sync", func() {

g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(infrastructureDuplicate), dest)).To(Succeed())
g.Expect(dest.GetAnnotations()).To(HaveKeyWithValue(sync.AppliedSpecHashAnnotation, ""))
g.Expect(capiProviderDuplicate.Status.Conditions).To(HaveLen(2))
g.Expect(capiProviderDuplicate.Status.Conditions).To(HaveLen(1))
g.Expect(conditions.IsTrue(capiProviderDuplicate, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue())
g.Expect(conditions.IsTrue(capiProviderDuplicate, turtlesv1.CheckLatestVersionTime)).To(BeTrue())
g.Expect(conditions.Get(capiProviderDuplicate, turtlesv1.LastAppliedConfigurationTime).LastTransitionTime.Minute()).To(Equal(time.Now().UTC().Minute()))
}).Should(Succeed())

Expand Down