Skip to content

Commit

Permalink
Check CAPI provider latest version daily (#679)
Browse files Browse the repository at this point in the history
- Perform check that CAPIProvider is up-to-date with current latest
  daily. Longer delay is set to prevent hitting github rate limits.

Signed-off-by: Danil-Grigorev <danil.grigorev@suse.com>
  • Loading branch information
Danil-Grigorev authored Aug 26, 2024
1 parent d74ef8c commit 4e377f9
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 25 deletions.
5 changes: 4 additions & 1 deletion api/v1alpha1/conditions_consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ const (
// RancherCredentialSourceMissing occures when a source credential secret is missing.
RancherCredentialSourceMissing = "RancherCredentialSourceMissing"

// LastAppliedConfigurationTime is set as a timestamp infor of the last configuration update byt the CAPI Operator resource.
// LastAppliedConfigurationTime is set as a timestamp info of the last configuration update byt the CAPI Operator resource.
LastAppliedConfigurationTime = "LastAppliedConfigurationTime"

// CheckLatestVersionTime is set as a timestamp info of the last timestamp of the latest version being up-to-date for the CAPIProvider.
CheckLatestVersionTime = "CheckLatestVersionTime"
)
35 changes: 33 additions & 2 deletions internal/sync/provider_sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"strings"
"time"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

Expand Down Expand Up @@ -89,10 +90,10 @@ func (ProviderSync) Template(capiProvider *turtlesv1.CAPIProvider) client.Object
// Direction of updates:
// Spec -> down
// up <- Status.
func (s *ProviderSync) Sync(_ context.Context) error {
func (s *ProviderSync) Sync(ctx context.Context) error {
s.SyncObjects()

return nil
return s.updateLatestVersion(ctx)
}

// SyncObjects updates the Source CAPIProvider object and the destination provider object states.
Expand Down Expand Up @@ -149,3 +150,33 @@ func (s *ProviderSync) rolloutInfrastructure() {

conditions.MarkTrue(s.Source, turtlesv1.LastAppliedConfigurationTime)
}

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

now := time.Now().UTC()
lastCheck := conditions.Get(s.Source, turtlesv1.CheckLatestVersionTime)

if lastCheck != nil && lastCheck.Status == corev1.ConditionTrue && lastCheck.LastTransitionTime.Add(24*time.Hour).After(now) {
return nil
}

patchBase := client.MergeFrom(s.Destination)

// Unsetting .spec.version to force latest version rollout
spec := s.Destination.GetSpec()
spec.Version = ""
s.Destination.SetSpec(spec)

conditions.MarkTrue(s.Source, turtlesv1.CheckLatestVersionTime)

err := s.client.Patch(ctx, s.Destination, patchBase)
if err != nil {
conditions.MarkUnknown(s.Source, turtlesv1.CheckLatestVersionTime, "Requesting latest version rollout", "")
}

return client.IgnoreNotFound(err)
}
82 changes: 60 additions & 22 deletions internal/sync/provider_sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,18 @@ var _ = Describe("Provider sync", func() {
}}

infrastructureStatusOutdated = operatorv1.ProviderStatus{
Conditions: clusterv1.Conditions{{
Type: turtlesv1.LastAppliedConfigurationTime,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.NewTime(time.Now().UTC().Truncate(time.Second).Add(-24 * 100 * time.Hour)),
}},
Conditions: clusterv1.Conditions{
{
Type: turtlesv1.CheckLatestVersionTime,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.NewTime(time.Now().UTC().Truncate(time.Second).Add(-23 * time.Hour)),
},
{
Type: turtlesv1.LastAppliedConfigurationTime,
Status: corev1.ConditionTrue,
LastTransitionTime: metav1.NewTime(time.Now().UTC().Truncate(time.Second).Add(-24 * 100 * time.Hour)),
},
},
}

Expect(testEnv.Client.Create(ctx, capiProvider)).To(Succeed())
Expand All @@ -107,13 +114,14 @@ var _ = Describe("Provider sync", func() {

It("Should sync spec down", func() {
s := sync.NewProviderSync(testEnv, capiProvider.DeepCopy())
Eventually(s.Get(ctx)).Should(Succeed())

Expect(s.Sync(ctx)).To(Succeed())

var err error
s.Apply(ctx, &err)
Expect(err).To(Succeed())
Eventually(func(g Gomega) {
g.Expect(s.Get(ctx)).To(Succeed())
g.Expect(s.Sync(ctx)).To(Succeed())
var err error = nil
s.Apply(ctx, &err)
g.Expect(err).To(Succeed())
}).Should(Succeed())

Eventually(Object(infrastructure)).Should(
HaveField("Spec.ProviderSpec", Equal(capiProvider.Spec.ProviderSpec)))
Expand All @@ -122,12 +130,13 @@ var _ = Describe("Provider sync", func() {
It("Should sync azure spec", func() {
s := sync.NewAzureProviderSync(testEnv, capiProviderAzure)

Eventually(s.Get(ctx)).Should(Succeed())
Expect(s.Sync(ctx)).To(Succeed())

var err error
s.Apply(ctx, &err)
Expect(err).To(Succeed())
Eventually(func(g Gomega) {
g.Expect(s.Get(ctx)).To(Succeed())
g.Expect(s.Sync(ctx)).To(Succeed())
var err error = nil
s.Apply(ctx, &err)
Expect(err).To(Succeed())
}).Should(Succeed())

capiProviderAzure.Spec.Deployment = &operatorv1.DeploymentSpec{
Containers: []operatorv1.ContainerSpec{{
Expand Down Expand Up @@ -160,14 +169,18 @@ 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(capiProvider.Status.Conditions).To(HaveLen(1))
g.Expect(conditions.IsTrue(capiProvider, turtlesv1.CheckLatestVersionTime)).To(BeTrue())
g.Expect(capiProvider.Status.Conditions).To(HaveLen(2))
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

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

Eventually(testEnv.Status().Update(ctx, capiProvider)).Should(Succeed())
Eventually(func(g Gomega) {
g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(capiProvider), capiProvider)).To(Succeed())
Expand All @@ -187,14 +200,18 @@ 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(1))
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(
&lastVersionCheckCondition.LastTransitionTime)).To(BeTrue())
g.Expect(conditions.Get(capiProvider, 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)

Consistently(func(g Gomega) {
err = nil
Expand All @@ -203,6 +220,25 @@ var _ = Describe("Provider sync", func() {
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))
}, 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)

infrastructure.Spec.Version = "v1.2.3"

Expect(testEnv.Create(ctx, infrastructure)).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))
}, 5*time.Second).Should(Succeed())
})

Expand All @@ -225,8 +261,9 @@ 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(conditions.IsTrue(capiProvider, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue())
g.Expect(capiProvider.Status.Conditions).To(HaveLen(1))
g.Expect(conditions.IsTrue(capiProvider, turtlesv1.CheckLatestVersionTime)).To(BeTrue())
g.Expect(testEnv.Get(ctx, client.ObjectKeyFromObject(infrastructure), dest)).To(Succeed())
}).Should(Succeed())

Expand All @@ -241,9 +278,10 @@ 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(1))
g.Expect(capiProviderDuplicate.Status.Conditions).To(HaveLen(2))
g.Expect(conditions.IsTrue(capiProviderDuplicate, turtlesv1.LastAppliedConfigurationTime)).To(BeTrue())
g.Expect(conditions.Get(capiProviderDuplicate, turtlesv1.LastAppliedConfigurationTime).LastTransitionTime.Second()).To(Equal(time.Now().UTC().Second()))
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())

// Provider manifest should be created and phase set to provisioning
Expand Down

0 comments on commit 4e377f9

Please sign in to comment.