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

Adding a annotation flag to trigger rollout on change of cm/secrets #771

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ spec:
- you may want to prevent watching certain resources with the `--resources-to-ignore` flag
- you can configure logging in JSON format with the `--log-format=json` option
- you can configure the "reload strategy" with the `--reload-strategy=<strategy-name>` option (details below)
- you can add `reloader.stakater.com/triggerRollout="true"` to the rollout annotation, which will trigger a rollout.
strategy on changes to configmap/secret. Default (`false`) will just restart the pod.

## Reload Strategies

Expand Down
16 changes: 14 additions & 2 deletions internal/pkg/callbacks/rolling_upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package callbacks

import (
"context"
"time"
"fmt"
"strconv"
"time"

"github.com/sirupsen/logrus"
"github.com/stakater/Reloader/internal/pkg/options"
"github.com/stakater/Reloader/pkg/kube"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
Expand Down Expand Up @@ -330,10 +332,20 @@ func UpdateDeploymentConfig(clients kube.Clients, namespace string, resource run
// UpdateRollout performs rolling upgrade on rollout
func UpdateRollout(clients kube.Clients, namespace string, resource runtime.Object) error {
rollout := resource.(*argorolloutv1alpha1.Rollout)
annotations := GetRolloutAnnotations(rollout)
triggerRollout, _ := strconv.ParseBool(annotations[options.TriggerRolloutAnnotation])

rolloutBefore, _ := clients.ArgoRolloutClient.ArgoprojV1alpha1().Rollouts(namespace).Get(context.TODO(), rollout.Name, meta_v1.GetOptions{})
logrus.Warnf("Before: %+v", rolloutBefore.Spec.Template.Spec.Containers[0].Env)
logrus.Warnf("After: %+v", rollout.Spec.Template.Spec.Containers[0].Env)
_, err := clients.ArgoRolloutClient.ArgoprojV1alpha1().Rollouts(namespace).Patch(context.TODO(), rollout.Name, patchtypes.MergePatchType, []byte(fmt.Sprintf(`{"spec": {"restartAt": "%s"}}`, time.Now().Format(time.RFC3339))), meta_v1.PatchOptions{FieldManager: "Reloader"})
var err error

if triggerRollout {
_, err = clients.ArgoRolloutClient.ArgoprojV1alpha1().Rollouts(namespace).Update(context.TODO(), rollout, meta_v1.UpdateOptions{FieldManager: "Reloader"})
} else {
_, err = clients.ArgoRolloutClient.ArgoprojV1alpha1().Rollouts(namespace).Patch(context.TODO(), rollout.Name, patchtypes.MergePatchType, []byte(fmt.Sprintf(`{"spec": {"restartAt": "%s"}}`, time.Now().Format(time.RFC3339))), meta_v1.PatchOptions{FieldManager: "Reloader"})
}

return err
}

Expand Down
47 changes: 46 additions & 1 deletion internal/pkg/handler/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"
"time"

testclientargorollout "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned/fake"
"github.com/prometheus/client_golang/prometheus"
promtestutil "github.com/prometheus/client_golang/prometheus/testutil"
"github.com/sirupsen/logrus"
Expand All @@ -24,7 +25,7 @@ import (
)

var (
clients = kube.Clients{KubernetesClient: testclient.NewSimpleClientset()}
clients = kube.Clients{KubernetesClient: testclient.NewSimpleClientset(), ArgoRolloutClient: testclientargorollout.NewSimpleClientset()}

arsNamespace = "test-handler-" + testutil.RandSeq(5)
arsConfigmapName = "testconfigmap-handler-" + testutil.RandSeq(5)
Expand Down Expand Up @@ -1053,6 +1054,12 @@ func setupErs() {
if err != nil {
logrus.Errorf("Error in Deployment with both annotations: %v", err)
}

// Creating Rollout
_, err = testutil.CreateRollout(clients.ArgoRolloutClient, ersConfigmapName, ersNamespace, true, true)
if err != nil {
logrus.Errorf("Error in Rollout: %v", err)
}
}

func teardownErs() {
Expand Down Expand Up @@ -1254,6 +1261,12 @@ func teardownErs() {
logrus.Errorf("Error while deleting statefulSet with secret as env var source %v", statefulSetError)
}

// Deleting Rollout with configmap
rolloutError := testutil.DeleteRollout(clients.ArgoRolloutClient, ersNamespace, ersConfigmapName)
if rolloutError != nil {
logrus.Errorf("Error while deleting rollout with configmap %v", rolloutError)
}

// Deleting Configmap
err := testutil.DeleteConfigMap(clients.KubernetesClient, ersNamespace, ersConfigmapName)
if err != nil {
Expand Down Expand Up @@ -3548,3 +3561,35 @@ func TestFailedRollingUpgradeUsingErs(t *testing.T) {
t.Errorf("Counter by namespace was not increased")
}
}


func TestRollingUpgradeForArgoRolloutsUsingErs(t *testing.T) {
options.ReloadStrategy = constants.EnvVarsReloadStrategy
options.IsArgoRollouts = "true"
envVarPostfix := constants.ConfigmapEnvVarPostfix

shaData := testutil.ConvertResourceToSHA(testutil.ConfigmapResourceType, ersNamespace, ersConfigmapName, "www.stakater.com")
config := getConfigWithAnnotations(envVarPostfix, ersConfigmapName, shaData, options.ConfigmapUpdateOnChangeAnnotation, options.ConfigmapReloaderAutoAnnotation)
argoRolloutFuncs := GetArgoRolloutRollingUpgradeFuncs()
collectors := getCollectors()

err := PerformAction(clients, config, argoRolloutFuncs, collectors, nil, invokeReloadStrategy)
time.Sleep(5 * time.Second)
if err != nil {
t.Errorf("Rolling upgrade failed for Argo Rollouts")
}

logrus.Infof("Verifying argo rollout update")
updated := testutil.VerifyResourceEnvVarUpdate(clients, config, envVarPostfix, argoRolloutFuncs)
if !updated {
t.Errorf("Argo Rollout was not updated")
}

if promtestutil.ToFloat64(collectors.Reloaded.With(labelSucceeded)) != 1 {
t.Errorf("Counter was not increased")
}

if promtestutil.ToFloat64(collectors.ReloadedByNamespace.With(prometheus.Labels{"success": "true", "namespace": ersNamespace})) != 1 {
t.Errorf("Counter by namespace was not increased")
}
}
2 changes: 2 additions & 0 deletions internal/pkg/options/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ var (
// SearchMatchAnnotation is an annotation to tag secrets to be found with
// AutoSearchAnnotation
SearchMatchAnnotation = "reloader.stakater.com/match"
// TriggerRolloutAnnotation controls if rollout strategy will be used to reload
TriggerRolloutAnnotation = "reloader.stakater.com/triggerRollout"
// LogFormat is the log format to use (json, or empty string for default)
LogFormat = ""
// LogLevel is the log level to use (trace, debug, info, warning, error, fatal and panic)
Expand Down
66 changes: 65 additions & 1 deletion internal/pkg/testutil/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"strings"
"time"

"github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1"
argorollout "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned"
openshiftv1 "github.com/openshift/api/apps/v1"
appsclient "github.com/openshift/client-go/apps/clientset/versioned"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -78,7 +80,7 @@ func getObjectMeta(namespace string, name string, autoReload bool, secretAutoRel
}
}

func getAnnotations(name string, autoReload bool, secretAutoReload bool, configmapAutoReload bool) map[string]string {
func getAnnotations(name string, autoReload, secretAutoReload, configmapAutoReload bool) map[string]string {
annotations := make(map[string]string)
if autoReload {
annotations[options.ReloaderAutoAnnotation] = "true"
Expand Down Expand Up @@ -680,6 +682,45 @@ func GetResourceSHAFromAnnotation(podAnnotations map[string]string) string {
return last.Hash
}

// GetRollout provides rollout for testing
func GetRollout(namespace string, rolloutName string, triggerRollout bool) *v1alpha1.Rollout {
replicaset := int32(1)
objectMeta := getObjectMeta(namespace, rolloutName, false, false, false)
if triggerRollout {
objectMeta.Annotations[options.TriggerRolloutAnnotation] = "true"
}
return &v1alpha1.Rollout{
ObjectMeta: objectMeta,
Spec: v1alpha1.RolloutSpec{
Replicas: &replicaset,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"secondLabel": "temp"},
},
Strategy: v1alpha1.RolloutStrategy{
BlueGreen: &v1alpha1.BlueGreenStrategy{
ActiveService: "active-service",
},
},
Template: getPodTemplateSpecWithVolumes(rolloutName),
},
}
}

// GetRolloutWithEnvVars provides rollout for testing
func GetRolloutWithEnvVars(namespace string, rolloutName string, triggerRollout bool) *v1alpha1.Rollout {
replicaset := int32(1)
return &v1alpha1.Rollout{
ObjectMeta: getObjectMeta(namespace, rolloutName, false, false, false),
Spec: v1alpha1.RolloutSpec{
Replicas: &replicaset,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"app": rolloutName},
},
Template: getPodTemplateSpecWithEnvVars(rolloutName),
},
}
}

// ConvertResourceToSHA generates SHA from secret or configmap data
func ConvertResourceToSHA(resourceType string, namespace string, resourceName string, data string) string {
values := []string{}
Expand Down Expand Up @@ -843,6 +884,21 @@ func CreateStatefulSet(client kubernetes.Interface, statefulsetName string, name
return statefulset, err
}

// CreateRollout creates a rollout in given namespace and returns the Rollout
func CreateRollout(client argorollout.Interface, rolloutName string, namespace string, volumeMount, triggerRollout bool) (*v1alpha1.Rollout, error) {
logrus.Infof("Creating Rollout")
rolloutClient := client.ArgoprojV1alpha1().Rollouts(namespace)
var rolloutObj *v1alpha1.Rollout
if volumeMount {
rolloutObj = GetRollout(namespace, rolloutName, triggerRollout)
} else {
rolloutObj = GetRolloutWithEnvVars(namespace, rolloutName, triggerRollout)
}
rollout, err := rolloutClient.Create(context.TODO(), rolloutObj, metav1.CreateOptions{})
time.Sleep(3 * time.Second)
return rollout, err
}

// DeleteDeployment creates a deployment in given namespace and returns the error if any
func DeleteDeployment(client kubernetes.Interface, namespace string, deploymentName string) error {
logrus.Infof("Deleting Deployment")
Expand All @@ -851,6 +907,14 @@ func DeleteDeployment(client kubernetes.Interface, namespace string, deploymentN
return deploymentError
}

// DeleteRollout deletes a rollout in given namespace and returns the error if any
func DeleteRollout(client argorollout.Interface, namespace string, rolloutName string) error {
logrus.Infof("Deleting Rollout")
rolloutError := client.ArgoprojV1alpha1().Rollouts(namespace).Delete(context.TODO(), rolloutName, metav1.DeleteOptions{})
time.Sleep(3 * time.Second)
return rolloutError
}

// DeleteDeploymentConfig deletes a deploymentConfig in given namespace and returns the error if any
func DeleteDeploymentConfig(client appsclient.Interface, namespace string, deploymentConfigName string) error {
logrus.Infof("Deleting DeploymentConfig")
Expand Down