package controller import ( "testing" kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" deployapi "github.com/openshift/origin/pkg/deploy/api" deployapitest "github.com/openshift/origin/pkg/deploy/api/test" imageapi "github.com/openshift/origin/pkg/image/api" ) const ( nonDefaultNamespace = "nondefaultnamespace" ) func TestUnregisteredContainer(t *testing.T) { controller := &ImageChangeController{ DeploymentConfigClient: &ImageChangeControllerDeploymentConfigClientImpl{ UpdateDeploymentConfigFunc: func(namespace string, config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) { t.Fatalf("unexpected deployment config update") return nil, nil }, GenerateDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) { t.Fatalf("unexpected generator call") return nil, nil }, ListDeploymentConfigsFunc: func() ([]*deployapi.DeploymentConfig, error) { config := deployapitest.OkDeploymentConfig(1) config.Triggers[0].ImageChangeParams.ContainerNames = []string{"container-3"} return []*deployapi.DeploymentConfig{config}, nil }, }, } // verify no-op err := controller.HandleImageRepo(tagUpdate()) if err != nil { t.Fatalf("unexpected err: %v", err) } } func TestImageChangeForNonAutomaticTag(t *testing.T) { controller := &ImageChangeController{ DeploymentConfigClient: &ImageChangeControllerDeploymentConfigClientImpl{ UpdateDeploymentConfigFunc: func(namespace string, config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) { t.Fatalf("unexpected deployment config update") return nil, nil }, GenerateDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) { t.Fatalf("unexpected generator call") return nil, nil }, ListDeploymentConfigsFunc: func() ([]*deployapi.DeploymentConfig, error) { config := deployapitest.OkDeploymentConfig(1) config.Triggers[0].ImageChangeParams.Automatic = false return []*deployapi.DeploymentConfig{config}, nil }, }, } // verify no-op err := controller.HandleImageRepo(tagUpdate()) if err != nil { t.Fatalf("unexpected err: %v", err) } } func TestImageChangeForUnregisteredTag(t *testing.T) { controller := &ImageChangeController{ DeploymentConfigClient: &ImageChangeControllerDeploymentConfigClientImpl{ UpdateDeploymentConfigFunc: func(namespace string, config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) { t.Fatalf("unexpected deployment config update") return nil, nil }, GenerateDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) { t.Fatalf("unexpected generator call") return nil, nil }, ListDeploymentConfigsFunc: func() ([]*deployapi.DeploymentConfig, error) { return []*deployapi.DeploymentConfig{imageChangeDeploymentConfig()}, nil }, }, } // verify no-op imageRepo := tagUpdate() imageRepo.Tags = map[string]string{ "unknown-tag": "ref-1", } err := controller.HandleImageRepo(imageRepo) if err != nil { t.Fatalf("unexpected err: %v", err) } } func TestImageChangeMatchScenarios(t *testing.T) { params := map[string]*deployapi.DeploymentTriggerImageChangeParams{ "params.1": { Automatic: true, ContainerNames: []string{"container-1"}, From: kapi.ObjectReference{Namespace: kapi.NamespaceDefault, Name: "repoA"}, Tag: "test-tag", }, "params.2": { Automatic: true, ContainerNames: []string{"container-1"}, From: kapi.ObjectReference{Name: "repoA"}, Tag: "test-tag", }, "params.3": { Automatic: true, ContainerNames: []string{"container-1"}, RepositoryName: "registry:8080/openshift/test-image", Tag: "test-tag", }, } updates := map[string]*imageapi.ImageRepository{ "repo.1": { ObjectMeta: kapi.ObjectMeta{Name: "repoA", Namespace: kapi.NamespaceDefault}, Status: imageapi.ImageRepositoryStatus{"registry:8080/openshift/test-image"}, Tags: map[string]string{"test-tag": "ref-2"}, }, "repo.2": { ObjectMeta: kapi.ObjectMeta{Name: "repoB", Namespace: kapi.NamespaceDefault}, Status: imageapi.ImageRepositoryStatus{"registry:8080/openshift/test-image"}, Tags: map[string]string{"test-tag": "ref-3"}, }, "repo.3": { ObjectMeta: kapi.ObjectMeta{Name: "repoC", Namespace: kapi.NamespaceDefault}, Status: imageapi.ImageRepositoryStatus{"registry:8080/openshift/test-image-B"}, Tags: map[string]string{"test-tag": "ref-2"}, }, "repo.4": { ObjectMeta: kapi.ObjectMeta{Name: "repoA", Namespace: kapi.NamespaceDefault}, Tags: map[string]string{"test-tag": "ref-2"}, }, } scenarios := []struct { param string repo string matches bool causes []string }{ {"params.1", "repo.1", true, []string{"registry:8080/openshift/test-image:ref-2"}}, {"params.1", "repo.2", false, []string{}}, {"params.1", "repo.3", false, []string{}}, // This case relies on a brittle assumption that we'll sometimes has an empty // imageRepo.Status.DockerImageRepository, but we'll still feed it through the // generator anyway (which could return a config with no diffs). {"params.1", "repo.4", true, []string{}}, {"params.2", "repo.1", true, []string{"registry:8080/openshift/test-image:ref-2"}}, {"params.2", "repo.2", false, []string{}}, {"params.2", "repo.3", false, []string{}}, // Same as params.1 -> repo.4, see above {"params.2", "repo.4", true, []string{}}, {"params.3", "repo.1", true, []string{"registry:8080/openshift/test-image"}}, {"params.3", "repo.2", true, []string{"registry:8080/openshift/test-image"}}, {"params.3", "repo.3", false, []string{}}, {"params.3", "repo.4", false, []string{}}, } for _, s := range scenarios { config := imageChangeDeploymentConfig() config.Namespace = kapi.NamespaceDefault config.Triggers = []deployapi.DeploymentTriggerPolicy{ { Type: deployapi.DeploymentTriggerOnImageChange, ImageChangeParams: params[s.param], }, } updated := false generated := false controller := &ImageChangeController{ DeploymentConfigClient: &ImageChangeControllerDeploymentConfigClientImpl{ UpdateDeploymentConfigFunc: func(namespace string, config *deployapi.DeploymentConfig) (*deployapi.DeploymentConfig, error) { if !s.matches { t.Fatalf("unexpected deployment config update for scenario: %v", s) } updated = true return config, nil }, GenerateDeploymentConfigFunc: func(namespace, name string) (*deployapi.DeploymentConfig, error) { if !s.matches { t.Fatalf("unexpected generator call for scenario: %v", s) } generated = true return config, nil }, ListDeploymentConfigsFunc: func() ([]*deployapi.DeploymentConfig, error) { return []*deployapi.DeploymentConfig{config}, nil }, }, } t.Logf("running scenario: %v", s) err := controller.HandleImageRepo(updates[s.repo]) if err != nil { t.Fatalf("unexpected error for scenario %v: %v", s, err) } // assert updates/generations occurred if s.matches && !updated { t.Fatalf("expected update for scenario: %v", s) } if s.matches && !generated { t.Fatalf("expected generation for scenario: %v", s) } // assert causes are correct relative to expected updates if updated { if e, a := len(s.causes), len(config.Details.Causes); e != a { t.Fatalf("expected cause length %d, got %d", e, a) } for i, cause := range config.Details.Causes { if e, a := s.causes[i], cause.ImageTrigger.RepositoryName; e != a { t.Fatalf("expected cause repositoryName %s, got %s", e, a) } } } else { if config.Details != nil && len(config.Details.Causes) != 0 { t.Fatalf("expected cause length 0, got %d", len(config.Details.Causes)) } } } } // Utilities and convenience methods func originalImageRepo() *imageapi.ImageRepository { return &imageapi.ImageRepository{ ObjectMeta: kapi.ObjectMeta{Name: "test-image-repo", Namespace: nonDefaultNamespace}, DockerImageRepository: "registry:8080/openshift/test-image", Tags: map[string]string{ "test-tag": "ref-1", }, } } func unregisteredTagUpdate() *imageapi.ImageRepository { return &imageapi.ImageRepository{ ObjectMeta: kapi.ObjectMeta{Name: "test-image-repo", Namespace: nonDefaultNamespace}, DockerImageRepository: "registry:8080/openshift/test-image", Tags: map[string]string{ "test-tag": "ref-1", "other-test-tag": "ref-x", }, } } func tagUpdate() *imageapi.ImageRepository { return &imageapi.ImageRepository{ ObjectMeta: kapi.ObjectMeta{Name: "test-image-repo", Namespace: nonDefaultNamespace}, DockerImageRepository: "registry:8080/openshift/test-image", Tags: map[string]string{ "test-tag": "ref-2", }, } } func imageChangeDeploymentConfig() *deployapi.DeploymentConfig { return &deployapi.DeploymentConfig{ ObjectMeta: kapi.ObjectMeta{Name: "image-change-deploy-config"}, Triggers: []deployapi.DeploymentTriggerPolicy{ { Type: deployapi.DeploymentTriggerOnImageChange, ImageChangeParams: &deployapi.DeploymentTriggerImageChangeParams{ Automatic: true, ContainerNames: []string{"container-1"}, RepositoryName: "registry:8080/openshift/test-image", Tag: "test-tag", }, }, }, Template: deployapi.DeploymentTemplate{ Strategy: deployapi.DeploymentStrategy{ Type: deployapi.DeploymentStrategyTypeRecreate, }, ControllerTemplate: kapi.ReplicationControllerSpec{ Replicas: 1, Selector: map[string]string{ "name": "test-pod", }, Template: &kapi.PodTemplateSpec{ ObjectMeta: kapi.ObjectMeta{ Labels: map[string]string{ "name": "test-pod", }, }, Spec: kapi.PodSpec{ Containers: []kapi.Container{ { Name: "container-1", Image: "registry:8080/openshift/test-image:ref-1", }, }, }, }, }, }, } } func regeneratedConfig(namespace string) *deployapi.DeploymentConfig { return &deployapi.DeploymentConfig{ ObjectMeta: kapi.ObjectMeta{Name: "image-change-deploy-config", Namespace: namespace}, Triggers: []deployapi.DeploymentTriggerPolicy{ { Type: deployapi.DeploymentTriggerOnImageChange, ImageChangeParams: &deployapi.DeploymentTriggerImageChangeParams{ Automatic: true, ContainerNames: []string{"container-1"}, RepositoryName: "registry:8080/openshift/test-image", Tag: "test-tag", }, }, }, Template: deployapi.DeploymentTemplate{ Strategy: deployapi.DeploymentStrategy{ Type: deployapi.DeploymentStrategyTypeRecreate, }, ControllerTemplate: kapi.ReplicationControllerSpec{ Replicas: 1, Selector: map[string]string{ "name": "test-pod", }, Template: &kapi.PodTemplateSpec{ ObjectMeta: kapi.ObjectMeta{ Labels: map[string]string{ "name": "test-pod", }, }, Spec: kapi.PodSpec{ Containers: []kapi.Container{ { Name: "container-1", Image: "registry:8080/openshift/test-image:ref-2", }, }, }, }, }, }, } } func unregisteredConfig() *deployapi.DeploymentConfig { d := imageChangeDeploymentConfig() d.Triggers[0].ImageChangeParams.ContainerNames = []string{"container-3"} return d }