1721 lines
38 KiB
Go
1721 lines
38 KiB
Go
package kubernetes
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/keel-hq/keel/approvals"
|
|
"github.com/keel-hq/keel/extension/notification"
|
|
"github.com/keel-hq/keel/internal/k8s"
|
|
"github.com/keel-hq/keel/pkg/store/sql"
|
|
"github.com/keel-hq/keel/types"
|
|
|
|
apps_v1 "k8s.io/api/apps/v1"
|
|
v1 "k8s.io/api/core/v1"
|
|
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
core_v1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
|
)
|
|
|
|
type fakeProvider struct {
|
|
submitted []types.Event
|
|
images []*types.TrackedImage
|
|
}
|
|
|
|
func (p *fakeProvider) Submit(event types.Event) error {
|
|
p.submitted = append(p.submitted, event)
|
|
return nil
|
|
}
|
|
|
|
func (p *fakeProvider) TrackedImages() ([]*types.TrackedImage, error) {
|
|
return p.images, nil
|
|
}
|
|
func (p *fakeProvider) List() []string {
|
|
return []string{"fakeprovider"}
|
|
}
|
|
func (p *fakeProvider) Stop() {
|
|
return
|
|
}
|
|
func (p *fakeProvider) GetName() string {
|
|
return "fp"
|
|
}
|
|
|
|
type fakeImplementer struct {
|
|
namespaces *v1.NamespaceList
|
|
deployment *apps_v1.Deployment
|
|
deploymentList *apps_v1.DeploymentList
|
|
|
|
podList *v1.PodList
|
|
deletedPods []*v1.Pod
|
|
|
|
// stores value of an updated deployment
|
|
updated *k8s.GenericResource
|
|
|
|
availableSecret *v1.Secret
|
|
}
|
|
|
|
func (i *fakeImplementer) Namespaces() (*v1.NamespaceList, error) {
|
|
return i.namespaces, nil
|
|
}
|
|
|
|
func (i *fakeImplementer) Deployment(namespace, name string) (*apps_v1.Deployment, error) {
|
|
return i.deployment, nil
|
|
}
|
|
|
|
func (i *fakeImplementer) Deployments(namespace string) (*apps_v1.DeploymentList, error) {
|
|
return i.deploymentList, nil
|
|
}
|
|
|
|
func (i *fakeImplementer) Update(obj *k8s.GenericResource) error {
|
|
i.updated = obj
|
|
return nil
|
|
}
|
|
|
|
func (i *fakeImplementer) Secret(namespace, name string) (*v1.Secret, error) {
|
|
return i.availableSecret, nil
|
|
}
|
|
|
|
func (i *fakeImplementer) Pods(namespace, labelSelector string) (*v1.PodList, error) {
|
|
return i.podList, nil
|
|
}
|
|
|
|
func (i *fakeImplementer) DeletePod(namespace, name string, opts *meta_v1.DeleteOptions) error {
|
|
i.deletedPods = append(i.deletedPods, &v1.Pod{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: name,
|
|
Namespace: namespace,
|
|
},
|
|
v1.PodSpec{},
|
|
v1.PodStatus{},
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (i *fakeImplementer) ConfigMaps(namespace string) core_v1.ConfigMapInterface {
|
|
return nil
|
|
}
|
|
|
|
type fakeSender struct {
|
|
sentEvent types.EventNotification
|
|
}
|
|
|
|
func (s *fakeSender) Configure(cfg *notification.Config) (bool, error) {
|
|
return true, nil
|
|
}
|
|
|
|
func (s *fakeSender) Send(event types.EventNotification) error {
|
|
s.sentEvent = event
|
|
return nil
|
|
}
|
|
|
|
func NewTestingUtils() (*sql.SQLStore, func()) {
|
|
dir, err := ioutil.TempDir("", "whstoretest")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
tmpfn := filepath.Join(dir, "gorm.db")
|
|
// defer
|
|
store, err := sql.New(sql.Opts{DatabaseType: "sqlite3", URI: tmpfn})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
teardown := func() {
|
|
os.RemoveAll(dir) // clean up
|
|
}
|
|
|
|
return store, teardown
|
|
}
|
|
|
|
func approver() (*approvals.DefaultManager, func()) {
|
|
store, teardown := NewTestingUtils()
|
|
return approvals.New(&approvals.Opts{
|
|
Store: store,
|
|
}), teardown
|
|
}
|
|
|
|
func TestGetNamespaces(t *testing.T) {
|
|
fi := &fakeImplementer{
|
|
namespaces: &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
grc := &k8s.GenericResourceCache{}
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fi, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
namespaces, err := provider.namespaces()
|
|
if err != nil {
|
|
t.Errorf("failed to get namespaces: %s", err)
|
|
}
|
|
|
|
if namespaces.Items[0].Name != "xxxx" {
|
|
t.Errorf("expected xxxx but got %s", namespaces.Items[0].Name)
|
|
}
|
|
}
|
|
|
|
func TestGetImageName(t *testing.T) {
|
|
name := versionreg.ReplaceAllString("gcr.io/v2-namespace/hello-world:1.1", "")
|
|
if name != "gcr.io/v2-namespace/hello-world" {
|
|
t.Errorf("expected 'gcr.io/v2-namespace/hello-world' but got '%s'", name)
|
|
}
|
|
}
|
|
|
|
func MustParseGR(obj interface{}) *k8s.GenericResource {
|
|
gr, err := k8s.NewGenericResource(obj)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return gr
|
|
}
|
|
|
|
func MustParseGRS(objs []*apps_v1.Deployment) []*k8s.GenericResource {
|
|
grs := make([]*k8s.GenericResource, len(objs))
|
|
for idx, obj := range objs {
|
|
var err error
|
|
var gr *k8s.GenericResource
|
|
gr, err = k8s.NewGenericResource(obj)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
grs[idx] = gr
|
|
}
|
|
return grs
|
|
}
|
|
|
|
func TestGetImpacted(t *testing.T) {
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-1",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-2",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{"whatever": "all"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
// creating "new version" event
|
|
repo := &types.Repository{
|
|
Name: "gcr.io/v2-namespace/hello-world",
|
|
Tag: "1.1.2",
|
|
}
|
|
|
|
plans, err := provider.createUpdatePlans(repo)
|
|
if err != nil {
|
|
t.Errorf("failed to get deployments: %s", err)
|
|
}
|
|
|
|
if len(plans) != 1 {
|
|
t.Fatalf("expected to find 1 deployment update plan but found %d", len(plans))
|
|
}
|
|
|
|
found := false
|
|
for _, c := range plans[0].Resource.Containers() {
|
|
|
|
containerImageName := versionreg.ReplaceAllString(c.Image, "")
|
|
|
|
if containerImageName == repo.Name {
|
|
found = true
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
t.Errorf("couldn't find expected deployment in impacted deployment list")
|
|
}
|
|
|
|
}
|
|
|
|
func TestGetImpactedInit(t *testing.T) {
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-1",
|
|
Namespace: "xxxx",
|
|
Annotations: map[string]string{types.KeelInitContainerAnnotation: "true"},
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
InitContainers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-2",
|
|
Namespace: "xxxx",
|
|
Annotations: map[string]string{types.KeelInitContainerAnnotation: "false"},
|
|
Labels: map[string]string{"whatever": "all"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
InitContainers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
// creating "new version" event
|
|
repo := &types.Repository{
|
|
Name: "gcr.io/v2-namespace/hello-world",
|
|
Tag: "1.1.2",
|
|
}
|
|
|
|
plans, err := provider.createUpdatePlans(repo)
|
|
if err != nil {
|
|
t.Errorf("failed to get deployments: %s", err)
|
|
}
|
|
|
|
if len(plans) != 1 {
|
|
t.Fatalf("expected to find 1 deployment update plan but found %d", len(plans))
|
|
}
|
|
|
|
found := false
|
|
for _, c := range plans[0].Resource.InitContainers() {
|
|
|
|
containerImageName := versionreg.ReplaceAllString(c.Image, "")
|
|
|
|
if containerImageName == repo.Name {
|
|
found = true
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
t.Errorf("couldn't find expected deployment in impacted deployment list")
|
|
}
|
|
|
|
}
|
|
|
|
func TestGetImpactedPolicyAnnotations(t *testing.T) {
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-1",
|
|
Namespace: "xxxx",
|
|
Annotations: map[string]string{types.KeelPolicyLabel: "all"},
|
|
Labels: map[string]string{"foo": "all"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-2",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{"whatever": "all"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
// creating "new version" event
|
|
repo := &types.Repository{
|
|
Name: "gcr.io/v2-namespace/hello-world",
|
|
Tag: "1.1.2",
|
|
}
|
|
|
|
plans, err := provider.createUpdatePlans(repo)
|
|
if err != nil {
|
|
t.Errorf("failed to get deployments: %s", err)
|
|
}
|
|
|
|
if len(plans) != 1 {
|
|
t.Fatalf("expected to find 1 deployment update plan but found %d", len(plans))
|
|
}
|
|
|
|
found := false
|
|
for _, c := range plans[0].Resource.Containers() {
|
|
|
|
containerImageName := versionreg.ReplaceAllString(c.Image, "")
|
|
|
|
if containerImageName == repo.Name {
|
|
found = true
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
t.Errorf("couldn't find expected deployment in impacted deployment list")
|
|
}
|
|
|
|
}
|
|
func TestPrereleaseGetImpactedA(t *testing.T) {
|
|
// test scenario when we have two deployments, one with pre-release tag
|
|
// and one without. New image comes without the prerelease tag. Expected scenario
|
|
// is to get one update plan for the second deployment. Deployment with prerelease tag
|
|
// should be ignored
|
|
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-1",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "major"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1-staging",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-2",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "major"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
// creating "new version" event
|
|
repo := &types.Repository{
|
|
Name: "gcr.io/v2-namespace/hello-world",
|
|
Tag: "1.1.2",
|
|
}
|
|
|
|
plans, err := provider.createUpdatePlans(repo)
|
|
if err != nil {
|
|
t.Errorf("failed to get deployments: %s", err)
|
|
}
|
|
|
|
if len(plans) != 1 {
|
|
t.Fatalf("expected to find 1 deployment update plan but found %d", len(plans))
|
|
}
|
|
|
|
if plans[0].Resource.Identifier != "deployment/xxxx/dep-2" {
|
|
t.Errorf("expected to get 'deployment/xxxx/dep-2', but got: %s", plans[0].Resource.Identifier)
|
|
}
|
|
}
|
|
|
|
func TestPrereleaseGetImpactedB(t *testing.T) {
|
|
// test scenario when we have two deployments, one with pre-release tag
|
|
// and one without. New image comes without the prerelease tag. Expected scenario
|
|
// is to get one update plan for the second deployment. Deployment with prerelease tag
|
|
// should be ignored
|
|
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-1",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1-staging",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-2",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "major"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
// creating "new version" event
|
|
repo := &types.Repository{
|
|
Name: "gcr.io/v2-namespace/hello-world",
|
|
Tag: "1.1.2-staging",
|
|
}
|
|
|
|
plans, err := provider.createUpdatePlans(repo)
|
|
if err != nil {
|
|
t.Errorf("failed to get deployments: %s", err)
|
|
}
|
|
|
|
if len(plans) != 1 {
|
|
t.Fatalf("expected to find 1 deployment update plan but found %d", len(plans))
|
|
}
|
|
|
|
if plans[0].Resource.Identifier != "deployment/xxxx/dep-1" {
|
|
t.Errorf("expected to get 'deployment/xxxx/dep-1', but got: %s", plans[0].Resource.Identifier)
|
|
}
|
|
}
|
|
|
|
func TestProcessEvent(t *testing.T) {
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "deployment-1",
|
|
Namespace: "ns-1",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
Annotations: map[string]string{},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
ObjectMeta: meta_v1.ObjectMeta{
|
|
Annotations: map[string]string{
|
|
"this": "that",
|
|
},
|
|
},
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "deployment-2",
|
|
Namespace: "ns-2",
|
|
Labels: map[string]string{"whatever": "all"},
|
|
Annotations: map[string]string{},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
ObjectMeta: meta_v1.ObjectMeta{
|
|
Annotations: map[string]string{
|
|
"this": "that",
|
|
},
|
|
},
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/bye-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "deployment-3",
|
|
Namespace: "ns-3",
|
|
Labels: map[string]string{
|
|
"whatever": "all",
|
|
"foo": "bar",
|
|
},
|
|
Annotations: map[string]string{
|
|
"ann": "1",
|
|
},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
ObjectMeta: meta_v1.ObjectMeta{
|
|
Annotations: map[string]string{
|
|
"this": "that",
|
|
},
|
|
},
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/bye-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
repo := types.Repository{
|
|
Name: "gcr.io/v2-namespace/hello-world",
|
|
Tag: "1.4.5",
|
|
}
|
|
|
|
event := &types.Event{Repository: repo}
|
|
_, err = provider.processEvent(event)
|
|
if err != nil {
|
|
t.Errorf("got error while processing event: %s", err)
|
|
}
|
|
|
|
if fp.updated == nil {
|
|
t.Fatalf("resource was not updated")
|
|
}
|
|
|
|
if fp.updated.Containers()[0].Image != repo.Name+":"+repo.Tag {
|
|
t.Errorf("expected to find a deployment with updated image but found: %s", fp.updated.Containers()[0].Image)
|
|
}
|
|
}
|
|
|
|
func TestProcessEventBuildNumber(t *testing.T) {
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "deployment-1",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
Annotations: map[string]string{},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:10",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
repo := types.Repository{
|
|
Name: "gcr.io/v2-namespace/hello-world",
|
|
Tag: "11",
|
|
}
|
|
|
|
event := &types.Event{Repository: repo}
|
|
_, err = provider.processEvent(event)
|
|
if err != nil {
|
|
t.Errorf("got error while processing event: %s", err)
|
|
}
|
|
|
|
if fp.updated != nil {
|
|
t.Errorf("didn't expect to get updated containers, bot got: %s", fp.updated.Identifier)
|
|
}
|
|
}
|
|
|
|
func TestEventSent(t *testing.T) {
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "deployment-1",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
Annotations: map[string]string{},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:10.0.0",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
fs := &fakeSender{}
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, fs, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
repo := types.Repository{
|
|
Name: "gcr.io/v2-namespace/hello-world",
|
|
Tag: "11.0.0",
|
|
}
|
|
|
|
event := &types.Event{Repository: repo}
|
|
_, err = provider.processEvent(event)
|
|
if err != nil {
|
|
t.Errorf("got error while processing event: %s", err)
|
|
}
|
|
|
|
if fp.updated.Containers()[0].Image != repo.Name+":"+repo.Tag {
|
|
t.Errorf("expected to find a deployment with updated image but found: %s", fp.updated.Containers()[0].Image)
|
|
}
|
|
|
|
if fs.sentEvent.Message != "Successfully updated deployment xxxx/deployment-1 10.0.0->11.0.0 (gcr.io/v2-namespace/hello-world:11.0.0)" {
|
|
t.Errorf("expected 'Successfully updated deployment xxxx/deployment-1 10.0.0->11.0.0 (gcr.io/v2-namespace/hello-world:11.0.0)' sent message, got: %s", fs.sentEvent.Message)
|
|
}
|
|
}
|
|
|
|
func TestEventSentWithReleaseNotes(t *testing.T) {
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "deployment-1",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
Annotations: map[string]string{types.KeelReleaseNotesURL: "https://github.com/keel-hq/keel/releases"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:10.0.0",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
fs := &fakeSender{}
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, fs, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
repo := types.Repository{
|
|
Name: "gcr.io/v2-namespace/hello-world",
|
|
Tag: "11.0.0",
|
|
}
|
|
|
|
event := &types.Event{Repository: repo}
|
|
_, err = provider.processEvent(event)
|
|
if err != nil {
|
|
t.Errorf("got error while processing event: %s", err)
|
|
}
|
|
|
|
if fp.updated.Containers()[0].Image != repo.Name+":"+repo.Tag {
|
|
t.Errorf("expected to find a deployment with updated image but found: %s", fp.updated.Containers()[0].Image)
|
|
}
|
|
|
|
if fs.sentEvent.Level != types.LevelSuccess {
|
|
t.Errorf("expected level %s, got: %s", types.LevelSuccess, fs.sentEvent.Level)
|
|
}
|
|
|
|
if fs.sentEvent.Message != "Successfully updated deployment xxxx/deployment-1 10.0.0->11.0.0 (gcr.io/v2-namespace/hello-world:11.0.0). Release notes: https://github.com/keel-hq/keel/releases" {
|
|
t.Errorf("expected 'Successfully updated deployment xxxx/deployment-1 10.0.0->11.0.0 (gcr.io/v2-namespace/hello-world:11.0.0). Release notes: https://github.com/keel-hq/keel/releases' sent message, got: %s", fs.sentEvent.Message)
|
|
}
|
|
}
|
|
|
|
// Test to check how many deployments are "impacted" if we have sidecar container
|
|
func TestGetImpactedTwoContainersInSameDeployment(t *testing.T) {
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-1",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
Annotations: map[string]string{},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
{
|
|
Image: "gcr.io/v2-namespace/greetings-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-2",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{"whatever": "all"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
// creating "new version" event
|
|
repo := &types.Repository{
|
|
Name: "gcr.io/v2-namespace/hello-world",
|
|
Tag: "1.1.2",
|
|
}
|
|
|
|
plans, err := provider.createUpdatePlans(repo)
|
|
if err != nil {
|
|
t.Errorf("failed to get deployments: %s", err)
|
|
}
|
|
|
|
if len(plans) != 1 {
|
|
t.Errorf("expected to find 1 deployment but found %d", len(plans))
|
|
}
|
|
|
|
found := false
|
|
for _, c := range plans[0].Resource.Containers() {
|
|
|
|
containerImageName := versionreg.ReplaceAllString(c.Image, "")
|
|
|
|
if containerImageName == repo.Name {
|
|
found = true
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
t.Errorf("couldn't find expected deployment in impacted deployment list")
|
|
}
|
|
|
|
}
|
|
|
|
// Test to check how many deployments are "impacted" if we have two init containers
|
|
func TestGetImpactedTwoInitContainersInSameDeployment(t *testing.T) {
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-1",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
Annotations: map[string]string{types.KeelInitContainerAnnotation: "true"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
InitContainers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
{
|
|
Image: "gcr.io/v2-namespace/greetings-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-2",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{"whatever": "all"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
InitContainers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
// creating "new version" event
|
|
repo := &types.Repository{
|
|
Name: "gcr.io/v2-namespace/hello-world",
|
|
Tag: "1.1.2",
|
|
}
|
|
|
|
plans, err := provider.createUpdatePlans(repo)
|
|
if err != nil {
|
|
t.Errorf("failed to get deployments: %s", err)
|
|
}
|
|
|
|
if len(plans) != 1 {
|
|
t.Errorf("expected to find 1 deployment but found %d", len(plans))
|
|
}
|
|
|
|
found := false
|
|
for _, c := range plans[0].Resource.InitContainers() {
|
|
|
|
containerImageName := versionreg.ReplaceAllString(c.Image, "")
|
|
|
|
if containerImageName == repo.Name {
|
|
found = true
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
t.Errorf("couldn't find expected deployment in impacted deployment list")
|
|
}
|
|
|
|
}
|
|
|
|
func TestGetImpactedTwoSameContainersInSameDeployment(t *testing.T) {
|
|
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-1",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
Annotations: map[string]string{},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-2",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{"whatever": "all"},
|
|
Annotations: map[string]string{},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
// creating "new version" event
|
|
repo := &types.Repository{
|
|
Name: "gcr.io/v2-namespace/hello-world",
|
|
Tag: "1.1.2",
|
|
}
|
|
|
|
plans, err := provider.createUpdatePlans(repo)
|
|
if err != nil {
|
|
t.Errorf("failed to get deployments: %s", err)
|
|
}
|
|
|
|
if len(plans) != 1 {
|
|
t.Errorf("expected to find 1 deployment but found %d", len(plans))
|
|
}
|
|
|
|
found := false
|
|
for _, c := range plans[0].Resource.Containers() {
|
|
|
|
containerImageName := versionreg.ReplaceAllString(c.Image, "")
|
|
|
|
if containerImageName == repo.Name {
|
|
found = true
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
t.Errorf("couldn't find expected deployment in impacted deployment list")
|
|
}
|
|
|
|
}
|
|
|
|
func TestGetImpactedUntaggedImage(t *testing.T) {
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-1",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
Annotations: map[string]string{},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/foo-world",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-2",
|
|
Namespace: "xxxx",
|
|
Annotations: map[string]string{},
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
// creating "new version" event
|
|
repo := &types.Repository{
|
|
Name: "gcr.io/v2-namespace/hello-world",
|
|
Tag: "1.1.2",
|
|
}
|
|
|
|
plans, err := provider.createUpdatePlans(repo)
|
|
if err != nil {
|
|
t.Errorf("failed to get deployments: %s", err)
|
|
}
|
|
|
|
if len(plans) != 1 {
|
|
t.Errorf("expected to find 1 deployment but found %d", len(plans))
|
|
}
|
|
|
|
found := false
|
|
for _, c := range plans[0].Resource.Containers() {
|
|
|
|
containerImageName := versionreg.ReplaceAllString(c.Image, "")
|
|
|
|
if containerImageName == repo.Name {
|
|
found = true
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
t.Errorf("couldn't find expected deployment in impacted deployment list")
|
|
}
|
|
|
|
}
|
|
|
|
// test to check whether we get impacted deployment when it's untagged (we should)
|
|
func TestGetImpactedUntaggedOneImage(t *testing.T) {
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-1",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
Annotations: map[string]string{},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-2",
|
|
Namespace: "xxxx",
|
|
Annotations: map[string]string{},
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
// creating "new version" event
|
|
repo := &types.Repository{
|
|
Name: "gcr.io/v2-namespace/hello-world",
|
|
Tag: "1.1.2",
|
|
}
|
|
|
|
plans, err := provider.createUpdatePlans(repo)
|
|
if err != nil {
|
|
t.Errorf("failed to get deployments: %s", err)
|
|
}
|
|
|
|
if len(plans) != 2 {
|
|
t.Fatalf("expected to find 2 deployment but found %d", len(plans))
|
|
}
|
|
|
|
found := false
|
|
for _, plan := range plans {
|
|
for _, c := range plan.Resource.Containers() {
|
|
|
|
containerImageName := versionreg.ReplaceAllString(c.Image, "")
|
|
|
|
if containerImageName == repo.Name {
|
|
found = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
t.Errorf("couldn't find expected deployment in impacted deployment list")
|
|
}
|
|
|
|
}
|
|
|
|
func TestTrackedImages(t *testing.T) {
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-1",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{types.KeelPolicyLabel: "all"},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1",
|
|
},
|
|
},
|
|
ImagePullSecrets: []v1.LocalObjectReference{
|
|
{
|
|
Name: "very-secret",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
imgs, err := provider.TrackedImages()
|
|
if err != nil {
|
|
t.Errorf("failed to get image: %s", err)
|
|
}
|
|
if len(imgs) != 1 {
|
|
t.Errorf("expected to find 1 image, got: %d", len(imgs))
|
|
}
|
|
|
|
if imgs[0].Secrets[0] != "very-secret" {
|
|
t.Errorf("could not find image pull secret")
|
|
}
|
|
}
|
|
|
|
func TestTrackedImagesWithSecrets(t *testing.T) {
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-1",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{
|
|
types.KeelPolicyLabel: "all",
|
|
types.KeelImagePullSecretAnnotation: "foo-bar",
|
|
},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1",
|
|
},
|
|
},
|
|
ImagePullSecrets: []v1.LocalObjectReference{
|
|
{
|
|
Name: "very-secret",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
imgs, err := provider.TrackedImages()
|
|
if err != nil {
|
|
t.Errorf("failed to get image: %s", err)
|
|
}
|
|
if len(imgs) != 1 {
|
|
t.Errorf("expected to find 1 image, got: %d", len(imgs))
|
|
}
|
|
|
|
if imgs[0].Secrets[0] != "foo-bar" {
|
|
t.Errorf("expected foo-bar, got: %s", imgs[0].Secrets[0])
|
|
}
|
|
if imgs[0].Secrets[1] != "very-secret" {
|
|
t.Errorf("expected very-secret, got: %s", imgs[0].Secrets[1])
|
|
}
|
|
}
|
|
|
|
func TestTrackedInitImagesWithSecrets(t *testing.T) {
|
|
fp := &fakeImplementer{}
|
|
fp.namespaces = &v1.NamespaceList{
|
|
Items: []v1.Namespace{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{Name: "xxxx"},
|
|
v1.NamespaceSpec{},
|
|
v1.NamespaceStatus{},
|
|
},
|
|
},
|
|
}
|
|
deps := []*apps_v1.Deployment{
|
|
{
|
|
meta_v1.TypeMeta{},
|
|
meta_v1.ObjectMeta{
|
|
Name: "dep-1",
|
|
Namespace: "xxxx",
|
|
Labels: map[string]string{
|
|
types.KeelPolicyLabel: "all",
|
|
types.KeelImagePullSecretAnnotation: "foo-bar",
|
|
types.KeelInitContainerAnnotation: "true",
|
|
},
|
|
},
|
|
apps_v1.DeploymentSpec{
|
|
Template: v1.PodTemplateSpec{
|
|
Spec: v1.PodSpec{
|
|
ImagePullSecrets: []v1.LocalObjectReference{
|
|
{
|
|
Name: "very-secret",
|
|
},
|
|
},
|
|
InitContainers: []v1.Container{
|
|
{
|
|
Image: "gcr.io/v2-namespace/hello-world:1.1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
apps_v1.DeploymentStatus{},
|
|
},
|
|
}
|
|
|
|
grs := MustParseGRS(deps)
|
|
grc := &k8s.GenericResourceCache{}
|
|
grc.Add(grs...)
|
|
|
|
approver, teardown := approver()
|
|
defer teardown()
|
|
provider, err := NewProvider(fp, &fakeSender{}, approver, grc)
|
|
if err != nil {
|
|
t.Fatalf("failed to get provider: %s", err)
|
|
}
|
|
|
|
imgs, err := provider.TrackedImages()
|
|
if err != nil {
|
|
t.Errorf("failed to get image: %s", err)
|
|
}
|
|
if len(imgs) != 1 {
|
|
t.Errorf("expected to find 1 image, got: %d", len(imgs))
|
|
}
|
|
|
|
if imgs[0].Secrets[0] != "foo-bar" {
|
|
t.Errorf("expected foo-bar, got: %s", imgs[0].Secrets[0])
|
|
}
|
|
if imgs[0].Secrets[1] != "very-secret" {
|
|
t.Errorf("expected very-secret, got: %s", imgs[0].Secrets[1])
|
|
}
|
|
}
|