360 lines
9.7 KiB
Go
360 lines
9.7 KiB
Go
package kube
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"gitlab.com/unboundsoftware/apex-mocks"
|
|
v1 "k8s.io/api/apps/v1"
|
|
batchapiv1 "k8s.io/api/batch/v1"
|
|
v12 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
appsv1 "k8s.io/client-go/kubernetes/typed/apps/v1"
|
|
batchv1 "k8s.io/client-go/kubernetes/typed/batch/v1"
|
|
)
|
|
|
|
func TestClient_GetImages(t *testing.T) {
|
|
type fields struct {
|
|
provider ClientProvider
|
|
}
|
|
type args struct {
|
|
namespaces []string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
want map[string][]string
|
|
wantLogged []string
|
|
wantErr assert.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
name: "error getting client",
|
|
fields: fields{
|
|
provider: MockClientProvider(func() (APIClient, error) {
|
|
return nil, fmt.Errorf("error")
|
|
}),
|
|
},
|
|
args: args{},
|
|
want: nil,
|
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
|
return assert.EqualError(t, err, "error")
|
|
},
|
|
},
|
|
{
|
|
name: "error fetching deployments",
|
|
fields: fields{
|
|
provider: MockClientProvider(func() (APIClient, error) {
|
|
return &MockAPIClient{
|
|
Apps: func() appsv1.AppsV1Interface {
|
|
return &MockApps{
|
|
DeploymentsFn: func(namespace string) appsv1.DeploymentInterface {
|
|
assert.Equal(t, "default", namespace)
|
|
return &MockDeployments{
|
|
ListFn: func(ctx context.Context, opts metav1.ListOptions) (*v1.DeploymentList, error) {
|
|
return nil, fmt.Errorf("error")
|
|
},
|
|
}
|
|
},
|
|
}
|
|
},
|
|
}, nil
|
|
}),
|
|
},
|
|
args: args{
|
|
namespaces: []string{"default"},
|
|
},
|
|
want: nil,
|
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
|
return assert.EqualError(t, err, "error")
|
|
},
|
|
},
|
|
{
|
|
name: "error fetching cron jobs",
|
|
fields: fields{
|
|
provider: MockClientProvider(func() (APIClient, error) {
|
|
return &MockAPIClient{
|
|
Apps: func() appsv1.AppsV1Interface {
|
|
return &MockApps{
|
|
DeploymentsFn: func(namespace string) appsv1.DeploymentInterface {
|
|
return &MockDeployments{
|
|
ListFn: func(ctx context.Context, opts metav1.ListOptions) (*v1.DeploymentList, error) {
|
|
return &v1.DeploymentList{}, nil
|
|
},
|
|
}
|
|
},
|
|
}
|
|
},
|
|
Batch: func() batchv1.BatchV1Interface {
|
|
return &MockBatch{
|
|
CronJobsFn: func(namespace string) batchv1.CronJobInterface {
|
|
assert.Equal(t, "default", namespace)
|
|
return &MockCronJobs{
|
|
ListFn: func(ctx context.Context, opts metav1.ListOptions) (*batchapiv1.CronJobList, error) {
|
|
return nil, fmt.Errorf("error")
|
|
},
|
|
}
|
|
},
|
|
}
|
|
},
|
|
}, nil
|
|
}),
|
|
},
|
|
args: args{
|
|
namespaces: []string{"default"},
|
|
},
|
|
want: nil,
|
|
wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
|
|
return assert.EqualError(t, err, "error")
|
|
},
|
|
},
|
|
{
|
|
name: "no deployments or cronjobs",
|
|
fields: fields{
|
|
provider: MockClientProvider(func() (APIClient, error) {
|
|
return &MockAPIClient{
|
|
Apps: func() appsv1.AppsV1Interface {
|
|
return &MockApps{
|
|
DeploymentsFn: func(namespace string) appsv1.DeploymentInterface {
|
|
return &MockDeployments{
|
|
ListFn: func(ctx context.Context, opts metav1.ListOptions) (*v1.DeploymentList, error) {
|
|
return &v1.DeploymentList{}, nil
|
|
},
|
|
}
|
|
},
|
|
}
|
|
},
|
|
Batch: func() batchv1.BatchV1Interface {
|
|
return &MockBatch{
|
|
CronJobsFn: func(namespace string) batchv1.CronJobInterface {
|
|
return &MockCronJobs{
|
|
ListFn: func(ctx context.Context, opts metav1.ListOptions) (*batchapiv1.CronJobList, error) {
|
|
return &batchapiv1.CronJobList{}, nil
|
|
},
|
|
}
|
|
},
|
|
}
|
|
},
|
|
}, nil
|
|
}),
|
|
},
|
|
args: args{
|
|
namespaces: []string{"default"},
|
|
},
|
|
want: map[string][]string{},
|
|
wantErr: assert.NoError,
|
|
},
|
|
{
|
|
name: "deployments and cronjobs in multiple namespaces",
|
|
fields: fields{
|
|
provider: MockClientProvider(func() (APIClient, error) {
|
|
return &MockAPIClient{
|
|
Apps: func() appsv1.AppsV1Interface {
|
|
return &MockApps{
|
|
DeploymentsFn: func(namespace string) appsv1.DeploymentInterface {
|
|
return &MockDeployments{
|
|
ListFn: func(ctx context.Context, opts metav1.ListOptions) (*v1.DeploymentList, error) {
|
|
if namespace == "default" {
|
|
return &v1.DeploymentList{
|
|
Items: []v1.Deployment{
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "some-deployment",
|
|
},
|
|
Spec: v1.DeploymentSpec{
|
|
Template: v12.PodTemplateSpec{
|
|
Spec: v12.PodSpec{
|
|
Containers: []v12.Container{
|
|
{
|
|
Image: "registry.gitlab.com/unboundsoftware/dummy:abc123",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
return &v1.DeploymentList{
|
|
Items: []v1.Deployment{
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "other-deployment",
|
|
},
|
|
Spec: v1.DeploymentSpec{
|
|
Template: v12.PodTemplateSpec{
|
|
Spec: v12.PodSpec{
|
|
Containers: []v12.Container{
|
|
{
|
|
Image: "registry.gitlab.com/unboundsoftware/dummy:def456",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, nil
|
|
},
|
|
}
|
|
},
|
|
}
|
|
},
|
|
Batch: func() batchv1.BatchV1Interface {
|
|
return &MockBatch{
|
|
CronJobsFn: func(namespace string) batchv1.CronJobInterface {
|
|
return &MockCronJobs{
|
|
ListFn: func(ctx context.Context, opts metav1.ListOptions) (*batchapiv1.CronJobList, error) {
|
|
if namespace == "other" {
|
|
return &batchapiv1.CronJobList{
|
|
Items: []batchapiv1.CronJob{
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "some-cronjob",
|
|
},
|
|
Spec: batchapiv1.CronJobSpec{
|
|
JobTemplate: batchapiv1.JobTemplateSpec{
|
|
Spec: batchapiv1.JobSpec{
|
|
Template: v12.PodTemplateSpec{
|
|
Spec: v12.PodSpec{
|
|
Containers: []v12.Container{
|
|
{
|
|
Image: "registry.gitlab.com/unboundsoftware/other:xxx111",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
return &batchapiv1.CronJobList{}, nil
|
|
},
|
|
}
|
|
},
|
|
}
|
|
},
|
|
}, nil
|
|
}),
|
|
},
|
|
args: args{
|
|
namespaces: []string{"default", "other"},
|
|
},
|
|
want: map[string][]string{
|
|
"unboundsoftware/dummy": {"abc123", "def456"},
|
|
"unboundsoftware/other": {"xxx111"},
|
|
},
|
|
wantLogged: []string{
|
|
"info: Found image 'unboundsoftware/dummy:abc123' in deployment default.some-deployment",
|
|
"info: Found image 'unboundsoftware/dummy:def456' in deployment other.other-deployment",
|
|
"info: Found image 'unboundsoftware/other:xxx111' in cronjob other.some-cronjob",
|
|
},
|
|
wantErr: assert.NoError,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
c := &Client{
|
|
provider: tt.fields.provider,
|
|
}
|
|
logger := apex.New()
|
|
ctx := context.Background()
|
|
got, err := c.GetImages(ctx, logger, tt.args.namespaces)
|
|
if !tt.wantErr(t, err, fmt.Sprintf("GetImages(%v, %v, %v)", ctx, logger, tt.args.namespaces)) {
|
|
return
|
|
}
|
|
assert.Equalf(t, tt.want, got, "GetImages(%v, %v, %v)", ctx, logger, tt.args.namespaces)
|
|
logger.Check(t, tt.wantLogged)
|
|
})
|
|
}
|
|
}
|
|
|
|
type MockClientProvider func() (APIClient, error)
|
|
|
|
func (m MockClientProvider) Provide() (APIClient, error) {
|
|
return m()
|
|
}
|
|
|
|
type MockAPIClient struct {
|
|
Apps func() appsv1.AppsV1Interface
|
|
Batch func() batchv1.BatchV1Interface
|
|
}
|
|
|
|
func (m *MockAPIClient) AppsV1() appsv1.AppsV1Interface {
|
|
if m.Apps == nil {
|
|
return nil
|
|
}
|
|
return m.Apps()
|
|
}
|
|
|
|
func (m *MockAPIClient) BatchV1() batchv1.BatchV1Interface {
|
|
if m.Batch == nil {
|
|
return nil
|
|
}
|
|
return m.Batch()
|
|
}
|
|
|
|
var _ APIClient = &MockAPIClient{}
|
|
|
|
type MockApps struct {
|
|
appsv1.AppsV1Interface
|
|
DeploymentsFn func(namespace string) appsv1.DeploymentInterface
|
|
}
|
|
|
|
func (a *MockApps) Deployments(namespace string) appsv1.DeploymentInterface {
|
|
if a.DeploymentsFn == nil {
|
|
return nil
|
|
}
|
|
return a.DeploymentsFn(namespace)
|
|
}
|
|
|
|
var _ appsv1.AppsV1Interface = &MockApps{}
|
|
|
|
type MockDeployments struct {
|
|
appsv1.DeploymentInterface
|
|
ListFn func(ctx context.Context, opts metav1.ListOptions) (*v1.DeploymentList, error)
|
|
}
|
|
|
|
func (d *MockDeployments) List(ctx context.Context, opts metav1.ListOptions) (*v1.DeploymentList, error) {
|
|
if d.ListFn == nil {
|
|
return nil, nil
|
|
}
|
|
return d.ListFn(ctx, opts)
|
|
}
|
|
|
|
var _ appsv1.DeploymentInterface = &MockDeployments{}
|
|
|
|
type MockBatch struct {
|
|
batchv1.BatchV1Interface
|
|
CronJobsFn func(namespace string) batchv1.CronJobInterface
|
|
}
|
|
|
|
func (b *MockBatch) CronJobs(namespace string) batchv1.CronJobInterface {
|
|
if b.CronJobsFn == nil {
|
|
return nil
|
|
}
|
|
return b.CronJobsFn(namespace)
|
|
}
|
|
|
|
var _ batchv1.BatchV1Interface = &MockBatch{}
|
|
|
|
type MockCronJobs struct {
|
|
batchv1.CronJobInterface
|
|
ListFn func(ctx context.Context, opts metav1.ListOptions) (*batchapiv1.CronJobList, error)
|
|
}
|
|
|
|
func (m *MockCronJobs) List(ctx context.Context, opts metav1.ListOptions) (*batchapiv1.CronJobList, error) {
|
|
if m.ListFn == nil {
|
|
return nil, nil
|
|
}
|
|
return m.ListFn(ctx, opts)
|
|
}
|
|
|
|
var _ batchv1.CronJobInterface = &MockCronJobs{}
|