refactor: use common object for describing instances

This commit is contained in:
Alexis Couvreur
2022-10-25 14:51:12 +00:00
parent a33c822334
commit 67bf03780c
8 changed files with 228 additions and 219 deletions

74
app/instance/instance.go Normal file
View File

@@ -0,0 +1,74 @@
package instance
import log "github.com/sirupsen/logrus"
var Ready = "ready"
var NotReady = "not-ready"
var Error = "error"
type State struct {
Name string
CurrentReplicas int
Status string
Error string
}
func (instance State) IsReady() bool {
return instance.Status == Ready
}
func (instance State) HasError() bool {
return instance.Status == Error
}
func ErrorInstanceState(name string, err error) (State, error) {
log.Error(err.Error())
return State{
Name: name,
CurrentReplicas: 0,
Status: Error,
Error: err.Error(),
}, err
}
func UnrecoverableInstanceState(name string, err string) (State, error) {
log.Warn(err)
return State{
Name: name,
CurrentReplicas: 0,
Status: Error,
Error: err,
}, nil
}
func ReadyInstanceState(name string) (State, error) {
return State{
Name: name,
CurrentReplicas: 1,
Status: Ready,
}, nil
}
func ReadyInstanceStateOfReplicas(name string, replicas int) (State, error) {
return State{
Name: name,
CurrentReplicas: 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) {
return State{
Name: name,
CurrentReplicas: replicas,
Status: NotReady,
}, nil
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"github.com/acouvreur/sablier/app/instance"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
log "github.com/sirupsen/logrus"
@@ -24,77 +25,77 @@ func NewDockerClassicProvider() (*DockerClassicProvider, error) {
}, nil
}
func (provider *DockerClassicProvider) Start(name string) (InstanceState, error) {
func (provider *DockerClassicProvider) Start(name string) (instance.State, error) {
ctx := context.Background()
err := provider.Client.ContainerStart(ctx, name, types.ContainerStartOptions{})
if err != nil {
return errorInstanceState(name, err)
return instance.ErrorInstanceState(name, err)
}
return InstanceState{
return instance.State{
Name: name,
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
}, err
}
func (provider *DockerClassicProvider) Stop(name string) (InstanceState, error) {
func (provider *DockerClassicProvider) Stop(name string) (instance.State, error) {
ctx := context.Background()
// TODO: Allow to specify a termination timeout
err := provider.Client.ContainerStop(ctx, name, nil)
if err != nil {
return errorInstanceState(name, err)
return instance.ErrorInstanceState(name, err)
}
return InstanceState{
return instance.State{
Name: name,
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
}, nil
}
func (provider *DockerClassicProvider) GetState(name string) (InstanceState, error) {
func (provider *DockerClassicProvider) GetState(name string) (instance.State, error) {
ctx := context.Background()
spec, err := provider.Client.ContainerInspect(ctx, name)
if err != nil {
return errorInstanceState(name, err)
return instance.ErrorInstanceState(name, err)
}
// "created", "running", "paused", "restarting", "removing", "exited", or "dead"
switch spec.State.Status {
case "created", "paused", "restarting", "removing":
return notReadyInstanceState(name)
return instance.NotReadyInstanceState(name)
case "running":
if spec.State.Health != nil {
// // "starting", "healthy" or "unhealthy"
if spec.State.Health.Status == "healthy" {
return readyInstanceState(name)
return instance.ReadyInstanceState(name)
} 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 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))
} else {
return unrecoverableInstanceState(name, "container is unhealthy: no log available")
return instance.UnrecoverableInstanceState(name, "container is unhealthy: no log available")
}
} else {
return notReadyInstanceState(name)
return instance.NotReadyInstanceState(name)
}
}
return readyInstanceState(name)
return instance.ReadyInstanceState(name)
case "exited":
if spec.State.ExitCode != 0 {
return 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))
}
return notReadyInstanceState(name)
return instance.NotReadyInstanceState(name)
case "dead":
return unrecoverableInstanceState(name, "container in \"dead\" state cannot be restarted")
return instance.UnrecoverableInstanceState(name, "container in \"dead\" state cannot be restarted")
default:
return 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))
}
}

View File

@@ -5,6 +5,7 @@ import (
"reflect"
"testing"
"github.com/acouvreur/sablier/app/instance"
"github.com/acouvreur/sablier/app/providers/mocks"
"github.com/docker/docker/api/types"
"github.com/stretchr/testify/mock"
@@ -21,7 +22,7 @@ func TestDockerClassicProvider_GetState(t *testing.T) {
name string
fields fields
args args
want InstanceState
want instance.State
wantErr bool
containerSpec types.ContainerJSON
err error
@@ -34,10 +35,10 @@ func TestDockerClassicProvider_GetState(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantErr: false,
containerSpec: mocks.CreatedContainerSpec("nginx"),
@@ -50,10 +51,10 @@ func TestDockerClassicProvider_GetState(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 1,
Status: Ready,
Status: instance.Ready,
},
wantErr: false,
containerSpec: mocks.RunningWithoutHealthcheckContainerSpec("nginx"),
@@ -66,10 +67,10 @@ func TestDockerClassicProvider_GetState(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantErr: false,
containerSpec: mocks.RunningWithHealthcheckContainerSpec("nginx", "starting"),
@@ -82,10 +83,10 @@ func TestDockerClassicProvider_GetState(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: Error,
Status: instance.Error,
Error: "container is unhealthy: curl http://localhost failed (1)",
},
wantErr: false,
@@ -99,10 +100,10 @@ func TestDockerClassicProvider_GetState(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 1,
Status: Ready,
Status: instance.Ready,
},
wantErr: false,
containerSpec: mocks.RunningWithHealthcheckContainerSpec("nginx", "healthy"),
@@ -115,10 +116,10 @@ func TestDockerClassicProvider_GetState(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantErr: false,
containerSpec: mocks.PausedContainerSpec("nginx"),
@@ -131,10 +132,10 @@ func TestDockerClassicProvider_GetState(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantErr: false,
containerSpec: mocks.RestartingContainerSpec("nginx"),
@@ -147,10 +148,10 @@ func TestDockerClassicProvider_GetState(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantErr: false,
containerSpec: mocks.RemovingContainerSpec("nginx"),
@@ -163,10 +164,10 @@ func TestDockerClassicProvider_GetState(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantErr: false,
containerSpec: mocks.ExitedContainerSpec("nginx", 0),
@@ -179,10 +180,10 @@ func TestDockerClassicProvider_GetState(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: Error,
Status: instance.Error,
Error: "container exited with code \"137\"",
},
wantErr: false,
@@ -196,10 +197,10 @@ func TestDockerClassicProvider_GetState(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: Error,
Status: instance.Error,
Error: "container in \"dead\" state cannot be restarted",
},
wantErr: false,
@@ -213,10 +214,10 @@ func TestDockerClassicProvider_GetState(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: Error,
Status: instance.Error,
Error: "container with name \"nginx\" was not found",
},
wantErr: true,
@@ -255,7 +256,7 @@ func TestDockerClassicProvider_Stop(t *testing.T) {
name string
fields fields
args args
want InstanceState
want instance.State
wantErr bool
err error
}{
@@ -267,10 +268,10 @@ func TestDockerClassicProvider_Stop(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: Error,
Status: instance.Error,
Error: "container with name \"nginx\" was not found",
},
wantErr: true,
@@ -284,10 +285,10 @@ func TestDockerClassicProvider_Stop(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantErr: false,
err: nil,
@@ -324,7 +325,7 @@ func TestDockerClassicProvider_Start(t *testing.T) {
name string
fields fields
args args
want InstanceState
want instance.State
wantErr bool
err error
}{
@@ -336,10 +337,10 @@ func TestDockerClassicProvider_Start(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: Error,
Status: instance.Error,
Error: "container with name \"nginx\" was not found",
},
wantErr: true,
@@ -353,10 +354,10 @@ func TestDockerClassicProvider_Start(t *testing.T) {
args: args{
name: "nginx",
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantErr: false,
err: nil,

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"strings"
"github.com/acouvreur/sablier/app/instance"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/swarm"
@@ -27,26 +28,26 @@ func NewDockerSwarmProvider() (*DockerSwarmProvider, error) {
}, nil
}
func (provider *DockerSwarmProvider) Start(name string) (InstanceState, error) {
func (provider *DockerSwarmProvider) Start(name string) (instance.State, error) {
return provider.scale(name, 1)
}
func (provider *DockerSwarmProvider) Stop(name string) (InstanceState, error) {
func (provider *DockerSwarmProvider) Stop(name string) (instance.State, error) {
return provider.scale(name, 0)
}
func (provider *DockerSwarmProvider) scale(name string, replicas uint64) (InstanceState, error) {
func (provider *DockerSwarmProvider) scale(name string, replicas uint64) (instance.State, error) {
ctx := context.Background()
service, err := provider.getServiceByName(name, ctx)
if err != nil {
return errorInstanceState(name, err)
return instance.ErrorInstanceState(name, err)
}
foundName := provider.getInstanceName(name, *service)
if service.Spec.Mode.Replicated == nil {
return unrecoverableInstanceState(foundName, "swarm service is not in \"replicated\" mode")
return instance.UnrecoverableInstanceState(foundName, "swarm service is not in \"replicated\" mode")
}
service.Spec.Mode.Replicated.Replicas = &replicas
@@ -54,35 +55,35 @@ 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 errorInstanceState(foundName, err)
return instance.ErrorInstanceState(foundName, err)
}
if len(response.Warnings) > 0 {
return unrecoverableInstanceState(foundName, strings.Join(response.Warnings, ", "))
return instance.UnrecoverableInstanceState(foundName, strings.Join(response.Warnings, ", "))
}
return notReadyInstanceState(foundName)
return instance.NotReadyInstanceState(foundName)
}
func (provider *DockerSwarmProvider) GetState(name string) (InstanceState, error) {
func (provider *DockerSwarmProvider) GetState(name string) (instance.State, error) {
ctx := context.Background()
service, err := provider.getServiceByName(name, ctx)
if err != nil {
return errorInstanceState(name, err)
return instance.ErrorInstanceState(name, err)
}
foundName := provider.getInstanceName(name, *service)
if service.Spec.Mode.Replicated == nil {
return unrecoverableInstanceState(foundName, "swarm service is not in \"replicated\" mode")
return instance.UnrecoverableInstanceState(foundName, "swarm service is not in \"replicated\" mode")
}
if service.ServiceStatus.DesiredTasks != service.ServiceStatus.RunningTasks || service.ServiceStatus.DesiredTasks == 0 {
return notReadyInstanceState(foundName)
return instance.NotReadyInstanceState(foundName)
}
return readyInstanceState(foundName)
return instance.ReadyInstanceState(foundName)
}
func (provider *DockerSwarmProvider) getServiceByName(name string, ctx context.Context) (*swarm.Service, error) {

View File

@@ -4,6 +4,7 @@ import (
"reflect"
"testing"
"github.com/acouvreur/sablier/app/instance"
"github.com/acouvreur/sablier/app/providers/mocks"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
@@ -21,7 +22,7 @@ func TestDockerSwarmProvider_Start(t *testing.T) {
name string
fields fields
args args
want InstanceState
want instance.State
serviceList []swarm.Service
response types.ServiceUpdateResponse
wantService swarm.Service
@@ -41,10 +42,10 @@ func TestDockerSwarmProvider_Start(t *testing.T) {
response: types.ServiceUpdateResponse{
Warnings: []string{},
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantService: mocks.ServiceReplicated("nginx", 1),
wantErr: false,
@@ -64,10 +65,10 @@ func TestDockerSwarmProvider_Start(t *testing.T) {
response: types.ServiceUpdateResponse{
Warnings: []string{},
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: Error,
Status: instance.Error,
Error: "ambiguous service names found for \"nginx\" (STACK1_nginx, STACK2_nginx)",
},
wantService: mocks.ServiceReplicated("nginx", 1),
@@ -89,10 +90,10 @@ func TestDockerSwarmProvider_Start(t *testing.T) {
response: types.ServiceUpdateResponse{
Warnings: []string{},
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantService: mocks.ServiceReplicated("nginx", 1),
wantErr: false,
@@ -112,10 +113,10 @@ func TestDockerSwarmProvider_Start(t *testing.T) {
response: types.ServiceUpdateResponse{
Warnings: []string{},
},
want: InstanceState{
want: instance.State{
Name: "nginx (STACK1_nginx)",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantService: mocks.ServiceReplicated("STACK1_nginx", 1),
wantErr: false,
@@ -134,10 +135,10 @@ func TestDockerSwarmProvider_Start(t *testing.T) {
response: types.ServiceUpdateResponse{
Warnings: []string{},
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: Error,
Status: instance.Error,
Error: "swarm service is not in \"replicated\" mode",
},
wantService: mocks.ServiceReplicated("nginx", 1),
@@ -176,7 +177,7 @@ func TestDockerSwarmProvider_Stop(t *testing.T) {
name string
fields fields
args args
want InstanceState
want instance.State
serviceList []swarm.Service
response types.ServiceUpdateResponse
wantService swarm.Service
@@ -196,10 +197,10 @@ func TestDockerSwarmProvider_Stop(t *testing.T) {
response: types.ServiceUpdateResponse{
Warnings: []string{},
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantService: mocks.ServiceReplicated("nginx", 0),
wantErr: false,
@@ -219,10 +220,10 @@ func TestDockerSwarmProvider_Stop(t *testing.T) {
response: types.ServiceUpdateResponse{
Warnings: []string{},
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: Error,
Status: instance.Error,
Error: "ambiguous service names found for \"nginx\" (STACK1_nginx, STACK2_nginx)",
},
wantService: mocks.ServiceReplicated("nginx", 1),
@@ -244,10 +245,10 @@ func TestDockerSwarmProvider_Stop(t *testing.T) {
response: types.ServiceUpdateResponse{
Warnings: []string{},
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantService: mocks.ServiceReplicated("nginx", 0),
wantErr: false,
@@ -267,10 +268,10 @@ func TestDockerSwarmProvider_Stop(t *testing.T) {
response: types.ServiceUpdateResponse{
Warnings: []string{},
},
want: InstanceState{
want: instance.State{
Name: "nginx (STACK1_nginx)",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantService: mocks.ServiceReplicated("STACK1_nginx", 0),
wantErr: false,
@@ -289,10 +290,10 @@ func TestDockerSwarmProvider_Stop(t *testing.T) {
response: types.ServiceUpdateResponse{
Warnings: []string{},
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: Error,
Status: instance.Error,
Error: "swarm service is not in \"replicated\" mode",
},
wantService: mocks.ServiceReplicated("nginx", 1),
@@ -331,7 +332,7 @@ func TestDockerSwarmProvider_GetState(t *testing.T) {
name string
fields fields
args args
want InstanceState
want instance.State
serviceList []swarm.Service
wantErr bool
}{
@@ -346,10 +347,10 @@ func TestDockerSwarmProvider_GetState(t *testing.T) {
serviceList: []swarm.Service{
mocks.ServiceReplicated("nginx", 1),
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 1,
Status: Ready,
Status: instance.Ready,
},
wantErr: false,
},
@@ -364,10 +365,10 @@ func TestDockerSwarmProvider_GetState(t *testing.T) {
serviceList: []swarm.Service{
mocks.ServiceNotReadyReplicated("nginx", 1, 0),
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantErr: false,
},
@@ -382,10 +383,10 @@ func TestDockerSwarmProvider_GetState(t *testing.T) {
serviceList: []swarm.Service{
mocks.ServiceNotReadyReplicated("STACK_nginx", 1, 0),
},
want: InstanceState{
want: instance.State{
Name: "nginx (STACK_nginx)",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
wantErr: false,
},
@@ -400,10 +401,10 @@ func TestDockerSwarmProvider_GetState(t *testing.T) {
serviceList: []swarm.Service{
mocks.ServiceGlobal("nginx"),
},
want: InstanceState{
want: instance.State{
Name: "nginx",
CurrentReplicas: 0,
Status: Error,
Status: instance.Error,
Error: "swarm service is not in \"replicated\" mode",
},
wantErr: false,

View File

@@ -7,6 +7,7 @@ import (
"strconv"
"strings"
"github.com/acouvreur/sablier/app/instance"
log "github.com/sirupsen/logrus"
autoscalingv1 "k8s.io/api/autoscaling/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -68,19 +69,19 @@ func NewKubernetesProvider() *KubernetesProvider {
}
}
func (provider *KubernetesProvider) Start(name string) (InstanceState, error) {
func (provider *KubernetesProvider) Start(name string) (instance.State, error) {
config, err := convertName(name)
if err != nil {
return unrecoverableInstanceState(name, err.Error())
return instance.UnrecoverableInstanceState(name, err.Error())
}
return provider.scale(config)
}
func (provider *KubernetesProvider) Stop(name string) (InstanceState, error) {
func (provider *KubernetesProvider) Stop(name string) (instance.State, error) {
config, err := convertName(name)
if err != nil {
return unrecoverableInstanceState(name, err.Error())
return instance.UnrecoverableInstanceState(name, err.Error())
}
config.Replicas = 0
@@ -89,7 +90,7 @@ func (provider *KubernetesProvider) Stop(name string) (InstanceState, error) {
}
func (provider *KubernetesProvider) scale(config *Config) (InstanceState, error) {
func (provider *KubernetesProvider) scale(config *Config) (instance.State, error) {
ctx := context.Background()
var workload Workload
@@ -100,28 +101,28 @@ func (provider *KubernetesProvider) scale(config *Config) (InstanceState, error)
case "statefulset":
workload = provider.Client.AppsV1().StatefulSets(config.Namespace)
default:
return 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))
}
s, err := workload.GetScale(ctx, config.Name, metav1.GetOptions{})
if err != nil {
return errorInstanceState(config.OriginalName, err)
return instance.ErrorInstanceState(config.OriginalName, err)
}
s.Spec.Replicas = config.Replicas
_, err = workload.UpdateScale(ctx, config.Name, s, metav1.UpdateOptions{})
if err != nil {
return errorInstanceState(config.OriginalName, err)
return instance.ErrorInstanceState(config.OriginalName, err)
}
return notReadyInstanceState(config.OriginalName)
return instance.NotReadyInstanceState(config.OriginalName)
}
func (provider *KubernetesProvider) GetState(name string) (InstanceState, error) {
func (provider *KubernetesProvider) GetState(name string) (instance.State, error) {
config, err := convertName(name)
if err != nil {
return unrecoverableInstanceState(name, err.Error())
return instance.UnrecoverableInstanceState(name, err.Error())
}
switch config.Kind {
@@ -130,40 +131,40 @@ func (provider *KubernetesProvider) GetState(name string) (InstanceState, error)
case "statefulset":
return provider.getStatefulsetState(config)
default:
return 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))
}
}
func (provider *KubernetesProvider) getDeploymentState(config *Config) (InstanceState, error) {
func (provider *KubernetesProvider) getDeploymentState(config *Config) (instance.State, error) {
ctx := context.Background()
d, err := provider.Client.AppsV1().Deployments(config.Namespace).
Get(ctx, config.Name, metav1.GetOptions{})
if err != nil {
return errorInstanceState(config.OriginalName, err)
return instance.ErrorInstanceState(config.OriginalName, err)
}
if *d.Spec.Replicas == d.Status.ReadyReplicas {
return readyInstanceStateOfReplicas(config.OriginalName, int(d.Status.ReadyReplicas))
return instance.ReadyInstanceStateOfReplicas(config.OriginalName, int(d.Status.ReadyReplicas))
}
return notReadyInstanceStateOfReplicas(config.OriginalName, int(d.Status.ReadyReplicas))
return instance.NotReadyInstanceStateOfReplicas(config.OriginalName, int(d.Status.ReadyReplicas))
}
func (provider *KubernetesProvider) getStatefulsetState(config *Config) (InstanceState, error) {
func (provider *KubernetesProvider) getStatefulsetState(config *Config) (instance.State, error) {
ctx := context.Background()
ss, err := provider.Client.AppsV1().StatefulSets(config.Namespace).
Get(ctx, config.Name, metav1.GetOptions{})
if err != nil {
return errorInstanceState(config.OriginalName, err)
return instance.ErrorInstanceState(config.OriginalName, err)
}
if *ss.Spec.Replicas == ss.Status.ReadyReplicas {
return readyInstanceStateOfReplicas(config.OriginalName, int(ss.Status.ReadyReplicas))
return instance.ReadyInstanceStateOfReplicas(config.OriginalName, int(ss.Status.ReadyReplicas))
}
return notReadyInstanceStateOfReplicas(config.OriginalName, int(ss.Status.ReadyReplicas))
return instance.NotReadyInstanceStateOfReplicas(config.OriginalName, int(ss.Status.ReadyReplicas))
}

View File

@@ -4,6 +4,7 @@ import (
"reflect"
"testing"
"github.com/acouvreur/sablier/app/instance"
"github.com/acouvreur/sablier/app/providers/mocks"
"github.com/stretchr/testify/mock"
v1 "k8s.io/api/apps/v1"
@@ -23,7 +24,7 @@ func TestKubernetesProvider_Start(t *testing.T) {
tests := []struct {
name string
args args
want InstanceState
want instance.State
data data
wantErr bool
}{
@@ -32,10 +33,10 @@ func TestKubernetesProvider_Start(t *testing.T) {
args: args{
name: "deployment_default_nginx_2",
},
want: InstanceState{
want: instance.State{
Name: "deployment_default_nginx_2",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
data: data{
name: "nginx",
@@ -49,10 +50,10 @@ func TestKubernetesProvider_Start(t *testing.T) {
args: args{
name: "statefulset_default_nginx_2",
},
want: InstanceState{
want: instance.State{
Name: "statefulset_default_nginx_2",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
data: data{
name: "nginx",
@@ -66,10 +67,10 @@ func TestKubernetesProvider_Start(t *testing.T) {
args: args{
name: "gateway_default_nginx_2",
},
want: InstanceState{
want: instance.State{
Name: "gateway_default_nginx_2",
CurrentReplicas: 0,
Status: Error,
Status: instance.Error,
Error: "unsupported kind \"gateway\" must be one of \"deployment\", \"statefulset\"",
},
data: data{
@@ -118,7 +119,7 @@ func TestKubernetesProvider_Stop(t *testing.T) {
tests := []struct {
name string
args args
want InstanceState
want instance.State
data data
wantErr bool
}{
@@ -127,10 +128,10 @@ func TestKubernetesProvider_Stop(t *testing.T) {
args: args{
name: "deployment_default_nginx_2",
},
want: InstanceState{
want: instance.State{
Name: "deployment_default_nginx_2",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
data: data{
name: "nginx",
@@ -144,10 +145,10 @@ func TestKubernetesProvider_Stop(t *testing.T) {
args: args{
name: "statefulset_default_nginx_2",
},
want: InstanceState{
want: instance.State{
Name: "statefulset_default_nginx_2",
CurrentReplicas: 0,
Status: NotReady,
Status: instance.NotReady,
},
data: data{
name: "nginx",
@@ -161,10 +162,10 @@ func TestKubernetesProvider_Stop(t *testing.T) {
args: args{
name: "gateway_default_nginx_2",
},
want: InstanceState{
want: instance.State{
Name: "gateway_default_nginx_2",
CurrentReplicas: 0,
Status: Error,
Status: instance.Error,
Error: "unsupported kind \"gateway\" must be one of \"deployment\", \"statefulset\"",
},
data: data{
@@ -213,7 +214,7 @@ func TestKubernetesProvider_GetState(t *testing.T) {
tests := []struct {
name string
args args
want InstanceState
want instance.State
data data
wantErr bool
}{
@@ -222,10 +223,10 @@ func TestKubernetesProvider_GetState(t *testing.T) {
args: args{
name: "deployment_default_nginx_2",
},
want: InstanceState{
want: instance.State{
Name: "deployment_default_nginx_2",
CurrentReplicas: 2,
Status: Ready,
Status: instance.Ready,
},
data: data{
name: "nginx",
@@ -238,10 +239,10 @@ func TestKubernetesProvider_GetState(t *testing.T) {
args: args{
name: "deployment_default_nginx_2",
},
want: InstanceState{
want: instance.State{
Name: "deployment_default_nginx_2",
CurrentReplicas: 1,
Status: NotReady,
Status: instance.NotReady,
},
data: data{
name: "nginx",
@@ -254,10 +255,10 @@ func TestKubernetesProvider_GetState(t *testing.T) {
args: args{
name: "statefulset_default_nginx_2",
},
want: InstanceState{
want: instance.State{
Name: "statefulset_default_nginx_2",
CurrentReplicas: 2,
Status: Ready,
Status: instance.Ready,
},
data: data{
name: "nginx",
@@ -270,10 +271,10 @@ func TestKubernetesProvider_GetState(t *testing.T) {
args: args{
name: "statefulset_default_nginx_2",
},
want: InstanceState{
want: instance.State{
Name: "statefulset_default_nginx_2",
CurrentReplicas: 1,
Status: NotReady,
Status: instance.NotReady,
},
data: data{
name: "nginx",
@@ -286,10 +287,10 @@ func TestKubernetesProvider_GetState(t *testing.T) {
args: args{
name: "gateway_default_nginx_2",
},
want: InstanceState{
want: instance.State{
Name: "gateway_default_nginx_2",
CurrentReplicas: 0,
Status: Error,
Status: instance.Error,
Error: "unsupported kind \"gateway\" must be one of \"deployment\", \"statefulset\"",
},
data: data{

View File

@@ -1,80 +1,9 @@
package providers
import log "github.com/sirupsen/logrus"
type InstanceState struct {
Name string
CurrentReplicas int
Status string
Error string
}
var Ready = "ready"
var NotReady = "not-ready"
var Error = "error"
import "github.com/acouvreur/sablier/app/instance"
type Provider interface {
Start(name string) (InstanceState, error)
Stop(name string) (InstanceState, error)
GetState(name string) (InstanceState, error)
}
func (instance InstanceState) IsReady() bool {
return instance.Status == Ready
}
func (instance InstanceState) HasError() bool {
return instance.Status == Error
}
func errorInstanceState(name string, err error) (InstanceState, error) {
log.Error(err.Error())
return InstanceState{
Name: name,
CurrentReplicas: 0,
Status: Error,
Error: err.Error(),
}, err
}
func unrecoverableInstanceState(name string, err string) (InstanceState, error) {
log.Warn(err)
return InstanceState{
Name: name,
CurrentReplicas: 0,
Status: Error,
Error: err,
}, nil
}
func readyInstanceState(name string) (InstanceState, error) {
return InstanceState{
Name: name,
CurrentReplicas: 1,
Status: Ready,
}, nil
}
func readyInstanceStateOfReplicas(name string, replicas int) (InstanceState, error) {
return InstanceState{
Name: name,
CurrentReplicas: replicas,
Status: Ready,
}, nil
}
func notReadyInstanceState(name string) (InstanceState, error) {
return InstanceState{
Name: name,
CurrentReplicas: 0,
Status: NotReady,
}, nil
}
func notReadyInstanceStateOfReplicas(name string, replicas int) (InstanceState, error) {
return InstanceState{
Name: name,
CurrentReplicas: replicas,
Status: NotReady,
}, nil
Start(name string) (instance.State, error)
Stop(name string) (instance.State, error)
GetState(name string) (instance.State, error)
}