diff --git a/app/http/routes/strategies.go b/app/http/routes/strategies.go index e21cc5a9..a3cecd4e 100644 --- a/app/http/routes/strategies.go +++ b/app/http/routes/strategies.go @@ -157,7 +157,7 @@ func instanceStateToRenderOptionsRequestState(instanceState *instance.State) pag Name: instanceState.Name, Status: instanceState.Status, CurrentReplicas: instanceState.CurrentReplicas, - DesiredReplicas: 1, //instanceState.DesiredReplicas, + DesiredReplicas: instanceState.DesiredReplicas, Error: err, } } diff --git a/app/http/routes/strategies_test.go b/app/http/routes/strategies_test.go index 4be35d75..faa1d09b 100644 --- a/app/http/routes/strategies_test.go +++ b/app/http/routes/strategies_test.go @@ -134,11 +134,11 @@ func TestServeStrategy_ServeBlocking(t *testing.T) { }, session: sessions.SessionState{ Instances: createMap([]*instance.State{ - {Name: "nginx", Status: instance.NotReady, CurrentReplicas: 0}, + {Name: "nginx", Status: instance.NotReady, CurrentReplicas: 0, DesiredReplicas: 1}, }), }, }, - expectedBody: `{"session":{"instances":[{"instance":{"name":"nginx","currentReplicas":0,"status":"not-ready"},"error":null}],"status":"not-ready"}}`, + expectedBody: `{"session":{"instances":[{"instance":{"name":"nginx","currentReplicas":0,"desiredReplicas":1,"status":"not-ready"},"error":null}],"status":"not-ready"}}`, expectedHeaderKey: "X-Sablier-Session-Status", expectedHeaderValue: "not-ready", }, @@ -151,11 +151,11 @@ func TestServeStrategy_ServeBlocking(t *testing.T) { }, session: sessions.SessionState{ Instances: createMap([]*instance.State{ - {Name: "nginx", Status: instance.Ready, CurrentReplicas: 1}, + {Name: "nginx", Status: instance.Ready, CurrentReplicas: 1, DesiredReplicas: 1}, }), }, }, - expectedBody: `{"session":{"instances":[{"instance":{"name":"nginx","currentReplicas":1,"status":"ready"},"error":null}],"status":"ready"}}`, + expectedBody: `{"session":{"instances":[{"instance":{"name":"nginx","currentReplicas":1,"desiredReplicas":1,"status":"ready"},"error":null}],"status":"ready"}}`, expectedHeaderKey: "X-Sablier-Session-Status", expectedHeaderValue: "ready", }, diff --git a/app/instance/instance.go b/app/instance/instance.go index af3c76f8..75921459 100644 --- a/app/instance/instance.go +++ b/app/instance/instance.go @@ -9,6 +9,7 @@ var Unrecoverable = "unrecoverable" type State struct { Name string `json:"name"` CurrentReplicas int `json:"currentReplicas"` + DesiredReplicas int `json:"desiredReplicas"` Status string `json:"status"` Message string `json:"message,omitempty"` } @@ -17,54 +18,42 @@ func (instance State) IsReady() bool { return instance.Status == Ready } -func ErrorInstanceState(name string, err error) (State, error) { +func ErrorInstanceState(name string, err error, desiredReplicas int) (State, error) { log.Error(err.Error()) return State{ Name: name, CurrentReplicas: 0, + DesiredReplicas: desiredReplicas, Status: Unrecoverable, Message: err.Error(), }, err } -func UnrecoverableInstanceState(name string, message string) (State, error) { +func UnrecoverableInstanceState(name string, message string, desiredReplicas int) (State, error) { log.Warn(message) return State{ Name: name, CurrentReplicas: 0, + DesiredReplicas: desiredReplicas, Status: Unrecoverable, Message: message, }, nil } -func ReadyInstanceState(name string) (State, error) { - return State{ - Name: name, - CurrentReplicas: 1, - Status: Ready, - }, nil -} - -func ReadyInstanceStateOfReplicas(name string, replicas int) (State, error) { +func ReadyInstanceState(name string, replicas int) (State, error) { return State{ Name: name, CurrentReplicas: replicas, + DesiredReplicas: replicas, Status: Ready, }, nil } -func NotReadyInstanceState(name string) (State, error) { - return State{ - Name: name, - CurrentReplicas: 0, - Status: NotReady, - }, nil -} - -func NotReadyInstanceStateOfReplicas(name string, replicas int) (State, error) { +func NotReadyInstanceState(name string, currentReplicas int, desiredReplicas int) (State, error) { return State{ Name: name, - CurrentReplicas: replicas, + CurrentReplicas: currentReplicas, + DesiredReplicas: desiredReplicas, Status: NotReady, }, nil } diff --git a/app/providers/docker_classic.go b/app/providers/docker_classic.go index 3a34406e..ef708be0 100644 --- a/app/providers/docker_classic.go +++ b/app/providers/docker_classic.go @@ -11,7 +11,8 @@ import ( ) type DockerClassicProvider struct { - Client client.ContainerAPIClient + Client client.ContainerAPIClient + desiredReplicas int } func NewDockerClassicProvider() (*DockerClassicProvider, error) { @@ -21,7 +22,8 @@ func NewDockerClassicProvider() (*DockerClassicProvider, error) { return nil, err } return &DockerClassicProvider{ - Client: cli, + Client: cli, + desiredReplicas: 1, }, nil } @@ -31,12 +33,13 @@ func (provider *DockerClassicProvider) Start(name string) (instance.State, error err := provider.Client.ContainerStart(ctx, name, types.ContainerStartOptions{}) if err != nil { - return instance.ErrorInstanceState(name, err) + return instance.ErrorInstanceState(name, err, provider.desiredReplicas) } return instance.State{ Name: name, CurrentReplicas: 0, + DesiredReplicas: provider.desiredReplicas, Status: instance.NotReady, }, err } @@ -48,12 +51,13 @@ func (provider *DockerClassicProvider) Stop(name string) (instance.State, error) err := provider.Client.ContainerStop(ctx, name, nil) if err != nil { - return instance.ErrorInstanceState(name, err) + return instance.ErrorInstanceState(name, err, provider.desiredReplicas) } return instance.State{ Name: name, CurrentReplicas: 0, + DesiredReplicas: provider.desiredReplicas, Status: instance.NotReady, }, nil } @@ -64,38 +68,38 @@ func (provider *DockerClassicProvider) GetState(name string) (instance.State, er spec, err := provider.Client.ContainerInspect(ctx, name) if err != nil { - return instance.ErrorInstanceState(name, err) + return instance.ErrorInstanceState(name, err, provider.desiredReplicas) } // "created", "running", "paused", "restarting", "removing", "exited", or "dead" switch spec.State.Status { case "created", "paused", "restarting", "removing": - return instance.NotReadyInstanceState(name) + return instance.NotReadyInstanceState(name, 0, provider.desiredReplicas) case "running": if spec.State.Health != nil { // // "starting", "healthy" or "unhealthy" if spec.State.Health.Status == "healthy" { - return instance.ReadyInstanceState(name) + return instance.ReadyInstanceState(name, provider.desiredReplicas) } else if spec.State.Health.Status == "unhealthy" { if len(spec.State.Health.Log) >= 1 { lastLog := spec.State.Health.Log[len(spec.State.Health.Log)-1] - return instance.UnrecoverableInstanceState(name, fmt.Sprintf("container is unhealthy: %s (%d)", lastLog.Output, lastLog.ExitCode)) + return instance.UnrecoverableInstanceState(name, fmt.Sprintf("container is unhealthy: %s (%d)", lastLog.Output, lastLog.ExitCode), provider.desiredReplicas) } else { - return instance.UnrecoverableInstanceState(name, "container is unhealthy: no log available") + return instance.UnrecoverableInstanceState(name, "container is unhealthy: no log available", provider.desiredReplicas) } } else { - return instance.NotReadyInstanceState(name) + return instance.NotReadyInstanceState(name, 0, provider.desiredReplicas) } } - return instance.ReadyInstanceState(name) + return instance.ReadyInstanceState(name, provider.desiredReplicas) case "exited": if spec.State.ExitCode != 0 { - return instance.UnrecoverableInstanceState(name, fmt.Sprintf("container exited with code \"%d\"", spec.State.ExitCode)) + return instance.UnrecoverableInstanceState(name, fmt.Sprintf("container exited with code \"%d\"", spec.State.ExitCode), provider.desiredReplicas) } - return instance.NotReadyInstanceState(name) + return instance.NotReadyInstanceState(name, 0, provider.desiredReplicas) case "dead": - return instance.UnrecoverableInstanceState(name, "container in \"dead\" state cannot be restarted") + return instance.UnrecoverableInstanceState(name, "container in \"dead\" state cannot be restarted", provider.desiredReplicas) default: - return instance.UnrecoverableInstanceState(name, fmt.Sprintf("container status \"%s\" not handled", spec.State.Status)) + return instance.UnrecoverableInstanceState(name, fmt.Sprintf("container status \"%s\" not handled", spec.State.Status), provider.desiredReplicas) } } diff --git a/app/providers/docker_classic_test.go b/app/providers/docker_classic_test.go index 8b7e1d0e..39aafa9f 100644 --- a/app/providers/docker_classic_test.go +++ b/app/providers/docker_classic_test.go @@ -38,6 +38,7 @@ func TestDockerClassicProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantErr: false, @@ -54,6 +55,7 @@ func TestDockerClassicProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 1, + DesiredReplicas: 1, Status: instance.Ready, }, wantErr: false, @@ -70,6 +72,7 @@ func TestDockerClassicProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantErr: false, @@ -86,6 +89,7 @@ func TestDockerClassicProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.Unrecoverable, Message: "container is unhealthy: curl http://localhost failed (1)", }, @@ -103,6 +107,7 @@ func TestDockerClassicProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 1, + DesiredReplicas: 1, Status: instance.Ready, }, wantErr: false, @@ -119,6 +124,7 @@ func TestDockerClassicProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantErr: false, @@ -135,6 +141,7 @@ func TestDockerClassicProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantErr: false, @@ -151,6 +158,7 @@ func TestDockerClassicProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantErr: false, @@ -167,6 +175,7 @@ func TestDockerClassicProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantErr: false, @@ -183,6 +192,7 @@ func TestDockerClassicProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.Unrecoverable, Message: "container exited with code \"137\"", }, @@ -200,6 +210,7 @@ func TestDockerClassicProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.Unrecoverable, Message: "container in \"dead\" state cannot be restarted", }, @@ -217,6 +228,7 @@ func TestDockerClassicProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.Unrecoverable, Message: "container with name \"nginx\" was not found", }, @@ -228,7 +240,8 @@ func TestDockerClassicProvider_GetState(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { provider := &DockerClassicProvider{ - Client: tt.fields.Client, + Client: tt.fields.Client, + desiredReplicas: 1, } tt.fields.Client.On("ContainerInspect", mock.Anything, mock.Anything).Return(tt.containerSpec, tt.err) @@ -271,6 +284,7 @@ func TestDockerClassicProvider_Stop(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.Unrecoverable, Message: "container with name \"nginx\" was not found", }, @@ -288,6 +302,7 @@ func TestDockerClassicProvider_Stop(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantErr: false, @@ -297,7 +312,8 @@ func TestDockerClassicProvider_Stop(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { provider := &DockerClassicProvider{ - Client: tt.fields.Client, + Client: tt.fields.Client, + desiredReplicas: 1, } tt.fields.Client.On("ContainerStop", mock.Anything, mock.Anything, mock.Anything).Return(tt.err) @@ -340,6 +356,7 @@ func TestDockerClassicProvider_Start(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.Unrecoverable, Message: "container with name \"nginx\" was not found", }, @@ -357,6 +374,7 @@ func TestDockerClassicProvider_Start(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantErr: false, @@ -366,7 +384,8 @@ func TestDockerClassicProvider_Start(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { provider := &DockerClassicProvider{ - Client: tt.fields.Client, + Client: tt.fields.Client, + desiredReplicas: 1, } tt.fields.Client.On("ContainerStart", mock.Anything, mock.Anything, mock.Anything).Return(tt.err) diff --git a/app/providers/docker_swarm.go b/app/providers/docker_swarm.go index 3e94f0eb..c1135f03 100644 --- a/app/providers/docker_swarm.go +++ b/app/providers/docker_swarm.go @@ -14,7 +14,8 @@ import ( ) type DockerSwarmProvider struct { - Client client.ServiceAPIClient + Client client.ServiceAPIClient + desiredReplicas int } func NewDockerSwarmProvider() (*DockerSwarmProvider, error) { @@ -23,12 +24,13 @@ func NewDockerSwarmProvider() (*DockerSwarmProvider, error) { return nil, err } return &DockerSwarmProvider{ - Client: cli, + Client: cli, + desiredReplicas: 1, }, nil } func (provider *DockerSwarmProvider) Start(name string) (instance.State, error) { - return provider.scale(name, 1) + return provider.scale(name, uint64(provider.desiredReplicas)) } func (provider *DockerSwarmProvider) Stop(name string) (instance.State, error) { @@ -40,13 +42,13 @@ func (provider *DockerSwarmProvider) scale(name string, replicas uint64) (instan service, err := provider.getServiceByName(name, ctx) if err != nil { - return instance.ErrorInstanceState(name, err) + return instance.ErrorInstanceState(name, err, provider.desiredReplicas) } foundName := provider.getInstanceName(name, *service) if service.Spec.Mode.Replicated == nil { - return instance.UnrecoverableInstanceState(foundName, "swarm service is not in \"replicated\" mode") + return instance.UnrecoverableInstanceState(foundName, "swarm service is not in \"replicated\" mode", provider.desiredReplicas) } service.Spec.Mode.Replicated.Replicas = &replicas @@ -54,14 +56,14 @@ func (provider *DockerSwarmProvider) scale(name string, replicas uint64) (instan response, err := provider.Client.ServiceUpdate(ctx, service.ID, service.Meta.Version, service.Spec, types.ServiceUpdateOptions{}) if err != nil { - return instance.ErrorInstanceState(foundName, err) + return instance.ErrorInstanceState(foundName, err, provider.desiredReplicas) } if len(response.Warnings) > 0 { - return instance.UnrecoverableInstanceState(foundName, strings.Join(response.Warnings, ", ")) + return instance.UnrecoverableInstanceState(foundName, strings.Join(response.Warnings, ", "), provider.desiredReplicas) } - return instance.NotReadyInstanceState(foundName) + return instance.NotReadyInstanceState(foundName, 0, provider.desiredReplicas) } func (provider *DockerSwarmProvider) GetState(name string) (instance.State, error) { @@ -69,20 +71,20 @@ func (provider *DockerSwarmProvider) GetState(name string) (instance.State, erro service, err := provider.getServiceByName(name, ctx) if err != nil { - return instance.ErrorInstanceState(name, err) + return instance.ErrorInstanceState(name, err, provider.desiredReplicas) } foundName := provider.getInstanceName(name, *service) if service.Spec.Mode.Replicated == nil { - return instance.UnrecoverableInstanceState(foundName, "swarm service is not in \"replicated\" mode") + return instance.UnrecoverableInstanceState(foundName, "swarm service is not in \"replicated\" mode", provider.desiredReplicas) } if service.ServiceStatus.DesiredTasks != service.ServiceStatus.RunningTasks || service.ServiceStatus.DesiredTasks == 0 { - return instance.NotReadyInstanceState(foundName) + return instance.NotReadyInstanceState(foundName, 0, provider.desiredReplicas) } - return instance.ReadyInstanceState(foundName) + return instance.ReadyInstanceState(foundName, provider.desiredReplicas) } func (provider *DockerSwarmProvider) getServiceByName(name string, ctx context.Context) (*swarm.Service, error) { diff --git a/app/providers/docker_swarm_test.go b/app/providers/docker_swarm_test.go index a049c41d..628d0f49 100644 --- a/app/providers/docker_swarm_test.go +++ b/app/providers/docker_swarm_test.go @@ -45,6 +45,7 @@ func TestDockerSwarmProvider_Start(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantService: mocks.ServiceReplicated("nginx", 1), @@ -68,6 +69,7 @@ func TestDockerSwarmProvider_Start(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.Unrecoverable, Message: "ambiguous service names found for \"nginx\" (STACK1_nginx, STACK2_nginx)", }, @@ -93,6 +95,7 @@ func TestDockerSwarmProvider_Start(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantService: mocks.ServiceReplicated("nginx", 1), @@ -116,6 +119,7 @@ func TestDockerSwarmProvider_Start(t *testing.T) { want: instance.State{ Name: "nginx (STACK1_nginx)", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantService: mocks.ServiceReplicated("STACK1_nginx", 1), @@ -138,6 +142,7 @@ func TestDockerSwarmProvider_Start(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.Unrecoverable, Message: "swarm service is not in \"replicated\" mode", }, @@ -148,7 +153,8 @@ func TestDockerSwarmProvider_Start(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { provider := &DockerSwarmProvider{ - Client: tt.fields.Client, + Client: tt.fields.Client, + desiredReplicas: 1, } tt.fields.Client.On("ServiceList", mock.Anything, mock.Anything).Return(tt.serviceList, nil) @@ -200,6 +206,7 @@ func TestDockerSwarmProvider_Stop(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantService: mocks.ServiceReplicated("nginx", 0), @@ -223,6 +230,7 @@ func TestDockerSwarmProvider_Stop(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.Unrecoverable, Message: "ambiguous service names found for \"nginx\" (STACK1_nginx, STACK2_nginx)", }, @@ -248,6 +256,7 @@ func TestDockerSwarmProvider_Stop(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantService: mocks.ServiceReplicated("nginx", 0), @@ -271,6 +280,7 @@ func TestDockerSwarmProvider_Stop(t *testing.T) { want: instance.State{ Name: "nginx (STACK1_nginx)", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantService: mocks.ServiceReplicated("STACK1_nginx", 0), @@ -293,6 +303,7 @@ func TestDockerSwarmProvider_Stop(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.Unrecoverable, Message: "swarm service is not in \"replicated\" mode", }, @@ -303,7 +314,8 @@ func TestDockerSwarmProvider_Stop(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { provider := &DockerSwarmProvider{ - Client: tt.fields.Client, + Client: tt.fields.Client, + desiredReplicas: 1, } tt.fields.Client.On("ServiceList", mock.Anything, mock.Anything).Return(tt.serviceList, nil) @@ -350,6 +362,7 @@ func TestDockerSwarmProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 1, + DesiredReplicas: 1, Status: instance.Ready, }, wantErr: false, @@ -368,6 +381,7 @@ func TestDockerSwarmProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantErr: false, @@ -386,6 +400,7 @@ func TestDockerSwarmProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx (STACK_nginx)", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.NotReady, }, wantErr: false, @@ -404,6 +419,7 @@ func TestDockerSwarmProvider_GetState(t *testing.T) { want: instance.State{ Name: "nginx", CurrentReplicas: 0, + DesiredReplicas: 1, Status: instance.Unrecoverable, Message: "swarm service is not in \"replicated\" mode", }, @@ -413,7 +429,8 @@ func TestDockerSwarmProvider_GetState(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { provider := &DockerSwarmProvider{ - Client: tt.fields.Client, + Client: tt.fields.Client, + desiredReplicas: 1, } tt.fields.Client.On("ServiceList", mock.Anything, mock.Anything).Return(tt.serviceList, nil) diff --git a/app/providers/kubernetes.go b/app/providers/kubernetes.go index 847b8b91..3006ed78 100644 --- a/app/providers/kubernetes.go +++ b/app/providers/kubernetes.go @@ -71,25 +71,23 @@ func NewKubernetesProvider() (*KubernetesProvider, error) { func (provider *KubernetesProvider) Start(name string) (instance.State, error) { config, err := convertName(name) if err != nil { - return instance.UnrecoverableInstanceState(name, err.Error()) + return instance.UnrecoverableInstanceState(name, err.Error(), int(config.Replicas)) } - return provider.scale(config) + return provider.scale(config, config.Replicas) } func (provider *KubernetesProvider) Stop(name string) (instance.State, error) { config, err := convertName(name) if err != nil { - return instance.UnrecoverableInstanceState(name, err.Error()) + return instance.UnrecoverableInstanceState(name, err.Error(), int(config.Replicas)) } - config.Replicas = 0 - - return provider.scale(config) + return provider.scale(config, 0) } -func (provider *KubernetesProvider) scale(config *Config) (instance.State, error) { +func (provider *KubernetesProvider) scale(config *Config, replicas int32) (instance.State, error) { ctx := context.Background() var workload Workload @@ -100,28 +98,28 @@ func (provider *KubernetesProvider) scale(config *Config) (instance.State, error case "statefulset": workload = provider.Client.AppsV1().StatefulSets(config.Namespace) default: - return instance.UnrecoverableInstanceState(config.OriginalName, fmt.Sprintf("unsupported kind \"%s\" must be one of \"deployment\", \"statefulset\"", config.Kind)) + return instance.UnrecoverableInstanceState(config.OriginalName, fmt.Sprintf("unsupported kind \"%s\" must be one of \"deployment\", \"statefulset\"", config.Kind), int(config.Replicas)) } s, err := workload.GetScale(ctx, config.Name, metav1.GetOptions{}) if err != nil { - return instance.ErrorInstanceState(config.OriginalName, err) + return instance.ErrorInstanceState(config.OriginalName, err, int(config.Replicas)) } - s.Spec.Replicas = config.Replicas + s.Spec.Replicas = replicas _, err = workload.UpdateScale(ctx, config.Name, s, metav1.UpdateOptions{}) if err != nil { - return instance.ErrorInstanceState(config.OriginalName, err) + return instance.ErrorInstanceState(config.OriginalName, err, int(config.Replicas)) } - return instance.NotReadyInstanceState(config.OriginalName) + return instance.NotReadyInstanceState(config.OriginalName, 0, int(config.Replicas)) } func (provider *KubernetesProvider) GetState(name string) (instance.State, error) { config, err := convertName(name) if err != nil { - return instance.UnrecoverableInstanceState(name, err.Error()) + return instance.UnrecoverableInstanceState(name, err.Error(), int(config.Replicas)) } switch config.Kind { @@ -130,7 +128,7 @@ func (provider *KubernetesProvider) GetState(name string) (instance.State, error case "statefulset": return provider.getStatefulsetState(config) default: - return instance.UnrecoverableInstanceState(config.OriginalName, fmt.Sprintf("unsupported kind \"%s\" must be one of \"deployment\", \"statefulset\"", config.Kind)) + return instance.UnrecoverableInstanceState(config.OriginalName, fmt.Sprintf("unsupported kind \"%s\" must be one of \"deployment\", \"statefulset\"", config.Kind), int(config.Replicas)) } } @@ -141,14 +139,14 @@ func (provider *KubernetesProvider) getDeploymentState(config *Config) (instance Get(ctx, config.Name, metav1.GetOptions{}) if err != nil { - return instance.ErrorInstanceState(config.OriginalName, err) + return instance.ErrorInstanceState(config.OriginalName, err, int(config.Replicas)) } if *d.Spec.Replicas == d.Status.ReadyReplicas { - return instance.ReadyInstanceStateOfReplicas(config.OriginalName, int(d.Status.ReadyReplicas)) + return instance.ReadyInstanceState(config.OriginalName, int(config.Replicas)) } - return instance.NotReadyInstanceStateOfReplicas(config.OriginalName, int(d.Status.ReadyReplicas)) + return instance.NotReadyInstanceState(config.OriginalName, int(d.Status.ReadyReplicas), int(config.Replicas)) } func (provider *KubernetesProvider) getStatefulsetState(config *Config) (instance.State, error) { @@ -158,12 +156,12 @@ func (provider *KubernetesProvider) getStatefulsetState(config *Config) (instanc Get(ctx, config.Name, metav1.GetOptions{}) if err != nil { - return instance.ErrorInstanceState(config.OriginalName, err) + return instance.ErrorInstanceState(config.OriginalName, err, int(config.Replicas)) } if *ss.Spec.Replicas == ss.Status.ReadyReplicas { - return instance.ReadyInstanceStateOfReplicas(config.OriginalName, int(ss.Status.ReadyReplicas)) + return instance.ReadyInstanceState(config.OriginalName, int(config.Replicas)) } - return instance.NotReadyInstanceStateOfReplicas(config.OriginalName, int(ss.Status.ReadyReplicas)) + return instance.NotReadyInstanceState(config.OriginalName, int(ss.Status.ReadyReplicas), int(config.Replicas)) } diff --git a/app/providers/kubernetes_test.go b/app/providers/kubernetes_test.go index 9481a283..60b829d9 100644 --- a/app/providers/kubernetes_test.go +++ b/app/providers/kubernetes_test.go @@ -36,6 +36,7 @@ func TestKubernetesProvider_Start(t *testing.T) { want: instance.State{ Name: "deployment_default_nginx_2", CurrentReplicas: 0, + DesiredReplicas: 2, Status: instance.NotReady, }, data: data{ @@ -53,6 +54,7 @@ func TestKubernetesProvider_Start(t *testing.T) { want: instance.State{ Name: "statefulset_default_nginx_2", CurrentReplicas: 0, + DesiredReplicas: 2, Status: instance.NotReady, }, data: data{ @@ -70,6 +72,7 @@ func TestKubernetesProvider_Start(t *testing.T) { want: instance.State{ Name: "gateway_default_nginx_2", CurrentReplicas: 0, + DesiredReplicas: 2, Status: instance.Unrecoverable, Message: "unsupported kind \"gateway\" must be one of \"deployment\", \"statefulset\"", }, @@ -131,6 +134,7 @@ func TestKubernetesProvider_Stop(t *testing.T) { want: instance.State{ Name: "deployment_default_nginx_2", CurrentReplicas: 0, + DesiredReplicas: 2, Status: instance.NotReady, }, data: data{ @@ -148,6 +152,7 @@ func TestKubernetesProvider_Stop(t *testing.T) { want: instance.State{ Name: "statefulset_default_nginx_2", CurrentReplicas: 0, + DesiredReplicas: 2, Status: instance.NotReady, }, data: data{ @@ -165,6 +170,7 @@ func TestKubernetesProvider_Stop(t *testing.T) { want: instance.State{ Name: "gateway_default_nginx_2", CurrentReplicas: 0, + DesiredReplicas: 2, Status: instance.Unrecoverable, Message: "unsupported kind \"gateway\" must be one of \"deployment\", \"statefulset\"", }, @@ -226,6 +232,7 @@ func TestKubernetesProvider_GetState(t *testing.T) { want: instance.State{ Name: "deployment_default_nginx_2", CurrentReplicas: 2, + DesiredReplicas: 2, Status: instance.Ready, }, data: data{ @@ -242,6 +249,7 @@ func TestKubernetesProvider_GetState(t *testing.T) { want: instance.State{ Name: "deployment_default_nginx_2", CurrentReplicas: 1, + DesiredReplicas: 2, Status: instance.NotReady, }, data: data{ @@ -258,6 +266,7 @@ func TestKubernetesProvider_GetState(t *testing.T) { want: instance.State{ Name: "statefulset_default_nginx_2", CurrentReplicas: 2, + DesiredReplicas: 2, Status: instance.Ready, }, data: data{ @@ -274,6 +283,7 @@ func TestKubernetesProvider_GetState(t *testing.T) { want: instance.State{ Name: "statefulset_default_nginx_2", CurrentReplicas: 1, + DesiredReplicas: 2, Status: instance.NotReady, }, data: data{ @@ -290,6 +300,7 @@ func TestKubernetesProvider_GetState(t *testing.T) { want: instance.State{ Name: "gateway_default_nginx_2", CurrentReplicas: 0, + DesiredReplicas: 2, Status: instance.Unrecoverable, Message: "unsupported kind \"gateway\" must be one of \"deployment\", \"statefulset\"", }, diff --git a/app/sessions/sessions_manager.go b/app/sessions/sessions_manager.go index 8134d6a3..eac4a627 100644 --- a/app/sessions/sessions_manager.go +++ b/app/sessions/sessions_manager.go @@ -129,6 +129,7 @@ func (s *SessionsManager) requestSessionInstance(name string, duration time.Dura requestState.Name = state.Name requestState.CurrentReplicas = state.CurrentReplicas + requestState.DesiredReplicas = state.DesiredReplicas requestState.Status = state.Status requestState.Message = state.Message log.Debugf("status for %s=%s", name, requestState.Status) @@ -143,6 +144,7 @@ func (s *SessionsManager) requestSessionInstance(name string, duration time.Dura requestState.Name = state.Name requestState.CurrentReplicas = state.CurrentReplicas + requestState.DesiredReplicas = state.DesiredReplicas requestState.Status = state.Status requestState.Message = state.Message log.Debugf("status for %s=%s", name, requestState.Status)