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
parent
2aed070a00
commit
44c3582f4f
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -95,7 +95,6 @@ func getJobSpec(
|
|||
},
|
||||
Spec: corev1.PodSpec{
|
||||
ServiceAccountName: serviceAccountName,
|
||||
DeprecatedServiceAccount: serviceAccountName,
|
||||
RestartPolicy: "Never",
|
||||
TerminationGracePeriodSeconds: &terminationGracePeriodSeconds,
|
||||
Containers: []corev1.Container{
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue