feat: Don't reset the number of replicas on deployment update (#1804)

* feat: Support HorizontalPodAutoscaler

Signed-off-by: Anatolii Bazko <abazko@redhat.com>
pull/1805/head
Anatolii Bazko 2024-01-29 15:10:56 +01:00 committed by GitHub
parent 2aed070a00
commit 44c3582f4f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 58 additions and 58 deletions

View File

@ -67,6 +67,10 @@ func SyncDeploymentSpecToCluster(
return false, err
}
if err := setDesiredReplicas(deploymentSpec, deployContext); err != nil {
return false, err
}
done, err := Sync(deployContext, deploymentSpec, deploymentDiffOpts)
if err != nil || !done {
// Failed to sync (update), let's delete and create instead
@ -92,11 +96,11 @@ func SyncDeploymentSpecToCluster(
return false, err
}
if actual.Spec.Strategy.Type == appsv1.RollingUpdateDeploymentStrategyType && actual.Status.Replicas > 1 {
provisioned := actual.Status.UnavailableReplicas == 0
if actual.Spec.Strategy.Type == appsv1.RollingUpdateDeploymentStrategyType && !provisioned {
logrus.Infof("Deployment %s is in the rolling update state.", deploymentSpec.Name)
}
provisioned := actual.Status.AvailableReplicas == 1 && actual.Status.Replicas == 1
return provisioned, nil
}
@ -509,3 +513,14 @@ func MountConfigMaps(specDeployment *appsv1.Deployment, deployContext *chetypes.
return nil
}
// setDesiredReplicas sets replicas count from the actual deployment.
func setDesiredReplicas(deployment *appsv1.Deployment, deployCtx *chetypes.DeployContext) error {
actual := &appsv1.Deployment{}
if exists, err := GetNamespacedObject(deployCtx, deployment.ObjectMeta.Name, actual); !exists {
return err
}
deployment.Spec.Replicas = actual.Spec.Replicas
return nil
}

View File

@ -17,7 +17,6 @@ import (
"encoding/base64"
"fmt"
"io/ioutil"
"strings"
"k8s.io/apimachinery/pkg/api/resource"
@ -77,8 +76,8 @@ func NewGatewayReconciler() *GatewayReconciler {
}
func (p *GatewayReconciler) Reconcile(ctx *chetypes.DeployContext) (reconcile.Result, bool, error) {
err := SyncGatewayToCluster(ctx)
if err != nil {
done, err := SyncGatewayToCluster(ctx)
if !done {
return reconcile.Result{}, false, err
}
@ -90,88 +89,81 @@ func (p *GatewayReconciler) Finalize(ctx *chetypes.DeployContext) bool {
}
// SyncGatewayToCluster installs or deletes the gateway based on the custom resource configuration
func SyncGatewayToCluster(deployContext *chetypes.DeployContext) error {
func SyncGatewayToCluster(deployContext *chetypes.DeployContext) (bool, error) {
return syncAll(deployContext)
}
func syncAll(deployContext *chetypes.DeployContext) error {
func syncAll(deployContext *chetypes.DeployContext) (bool, error) {
instance := deployContext.CheCluster
sa := getGatewayServiceAccountSpec(instance)
if _, err := deploy.Sync(deployContext, &sa, serviceAccountDiffOpts); err != nil {
return err
if done, err := deploy.Sync(deployContext, &sa, serviceAccountDiffOpts); !done {
return done, err
}
role := getGatewayRoleSpec(instance)
if _, err := deploy.Sync(deployContext, &role, roleDiffOpts); err != nil {
return err
if done, err := deploy.Sync(deployContext, &role, roleDiffOpts); !done {
return done, err
}
roleBinding := getGatewayRoleBindingSpec(instance)
if _, err := deploy.Sync(deployContext, &roleBinding, roleBindingDiffOpts); err != nil {
return err
if done, err := deploy.Sync(deployContext, &roleBinding, roleBindingDiffOpts); !done {
return done, err
}
if oauthSecret, err := getGatewaySecretSpec(deployContext); err == nil {
if _, err := deploy.Sync(deployContext, oauthSecret, secretDiffOpts); err != nil {
return err
if done, err := deploy.Sync(deployContext, oauthSecret, secretDiffOpts); !done {
return done, err
}
oauthProxyConfig := getGatewayOauthProxyConfigSpec(deployContext, string(oauthSecret.Data["cookie_secret"]))
if _, err := deploy.Sync(deployContext, &oauthProxyConfig, configMapDiffOpts); err != nil {
return err
if done, err := deploy.Sync(deployContext, &oauthProxyConfig, configMapDiffOpts); !done {
return done, err
}
} else {
return err
return false, err
}
kubeRbacProxyConfig := getGatewayKubeRbacProxyConfigSpec(instance)
if _, err := deploy.Sync(deployContext, &kubeRbacProxyConfig, configMapDiffOpts); err != nil {
return err
if done, err := deploy.Sync(deployContext, &kubeRbacProxyConfig, configMapDiffOpts); !done {
return done, err
}
if instance.IsAccessTokenConfigured() {
if headerRewritePluginConfig, err := getGatewayHeaderRewritePluginConfigSpec(instance); err == nil {
if _, err := deploy.Sync(deployContext, headerRewritePluginConfig, configMapDiffOpts); err != nil {
return err
if done, err := deploy.Sync(deployContext, headerRewritePluginConfig, configMapDiffOpts); !done {
return done, err
}
} else {
return err
return false, err
}
}
traefikConfig := getGatewayTraefikConfigSpec(instance)
if _, err := deploy.Sync(deployContext, &traefikConfig, configMapDiffOpts); err != nil {
return err
if done, err := deploy.Sync(deployContext, &traefikConfig, configMapDiffOpts); !done {
return done, err
}
depl, err := getGatewayDeploymentSpec(deployContext)
if err != nil {
return err
return false, err
}
if _, err := deploy.Sync(deployContext, depl, deploy.DefaultDeploymentDiffOpts); err != nil {
// Failed to sync (update), let's delete and create instead
if strings.Contains(err.Error(), "field is immutable") {
if _, err := deploy.DeleteNamespacedObject(deployContext, depl.Name, &appsv1.Deployment{}); err != nil {
return err
}
// Deleted successfully, return original error
return err
}
return err
if done, err := deploy.SyncDeploymentSpecToCluster(deployContext, depl, deploy.DefaultDeploymentDiffOpts); !done {
return false, err
}
service := getGatewayServiceSpec(instance)
if _, err := deploy.Sync(deployContext, &service, deploy.ServiceDefaultDiffOpts); err != nil {
return err
if done, err := deploy.Sync(deployContext, &service, deploy.ServiceDefaultDiffOpts); !done {
return done, err
}
if serverConfig, cfgErr := getGatewayServerConfigSpec(deployContext); cfgErr == nil {
if _, err := deploy.Sync(deployContext, &serverConfig, configMapDiffOpts); err != nil {
return err
if done, err := deploy.Sync(deployContext, &serverConfig, configMapDiffOpts); !done {
return done, err
}
}
return nil
return true, nil
}
func getGatewaySecretSpec(deployContext *chetypes.DeployContext) (*corev1.Secret, error) {

View File

@ -56,10 +56,9 @@ func TestSyncAllToCluster(t *testing.T) {
Proxy: &chetypes.Proxy{},
}
err := SyncGatewayToCluster(deployContext)
if err != nil {
t.Fatalf("Failed to sync Gateway: %v", err)
}
done, err := SyncGatewayToCluster(deployContext)
assert.True(t, done)
assert.Nil(t, err)
deployment := &appsv1.Deployment{}
err = cli.Get(context.TODO(), types.NamespacedName{Name: GatewayServiceName, Namespace: "eclipse-che"}, deployment)
@ -101,10 +100,9 @@ func TestNativeUserGateway(t *testing.T) {
Proxy: &chetypes.Proxy{},
}
err := SyncGatewayToCluster(deployContext)
if err != nil {
t.Fatalf("Failed to sync Gateway: %v", err)
}
done, err := SyncGatewayToCluster(deployContext)
assert.True(t, done)
assert.Nil(t, err)
deployment := &appsv1.Deployment{}
err = cli.Get(context.TODO(), types.NamespacedName{Name: GatewayServiceName, Namespace: "eclipse-che"}, deployment)

View File

@ -95,7 +95,6 @@ func getJobSpec(
},
Spec: corev1.PodSpec{
ServiceAccountName: serviceAccountName,
DeprecatedServiceAccount: serviceAccountName,
RestartPolicy: "Never",
TerminationGracePeriodSeconds: &terminationGracePeriodSeconds,
Containers: []corev1.Container{

View File

@ -19,7 +19,6 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/pointer"
)
func GetSpecRegistryDeployment(
@ -53,9 +52,7 @@ func GetSpecRegistryDeployment(
Labels: labels,
},
Spec: appsv1.DeploymentSpec{
Replicas: pointer.Int32Ptr(1),
RevisionHistoryLimit: pointer.Int32Ptr(2),
Selector: &metav1.LabelSelector{MatchLabels: labelSelector},
Selector: &metav1.LabelSelector{MatchLabels: labelSelector},
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
RollingUpdate: &appsv1.RollingUpdateDeployment{

View File

@ -118,8 +118,7 @@ func (s CheServerReconciler) getDeploymentSpec(ctx *chetypes.DeployContext) (*ap
Labels: labels,
},
Spec: corev1.PodSpec{
ServiceAccountName: "che",
DeprecatedServiceAccount: "che",
ServiceAccountName: "che",
Volumes: []corev1.Volume{
customPublicCertsVolume,
},

View File

@ -102,13 +102,13 @@ func (s *CheServerReconciler) syncActiveChePhase(ctx *chetypes.DeployContext) (b
}
if exists {
if cheDeployment.Status.AvailableReplicas < 1 {
if cheDeployment.Status.AvailableReplicas == 0 {
if ctx.CheCluster.Status.ChePhase != chev2.ClusterPhaseInactive {
ctx.CheCluster.Status.ChePhase = chev2.ClusterPhaseInactive
err := deploy.UpdateCheCRStatus(ctx, "Phase", chev2.ClusterPhaseInactive)
return false, err
}
} else if cheDeployment.Status.Replicas > 1 {
} else if cheDeployment.Status.Replicas != cheDeployment.Status.AvailableReplicas {
if ctx.CheCluster.Status.ChePhase != chev2.RollingUpdate {
ctx.CheCluster.Status.ChePhase = chev2.RollingUpdate
err := deploy.UpdateCheCRStatus(ctx, "Phase", chev2.RollingUpdate)