func TestPrintEventsResultSorted(t *testing.T) { // Arrange printer := NewHumanReadablePrinter(false /* noHeaders */) obj := api.EventList{ Items: []api.Event{ { Source: api.EventSource{Component: "kubelet"}, Message: "Item 1", Timestamp: util.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), }, { Source: api.EventSource{Component: "scheduler"}, Message: "Item 2", Timestamp: util.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)), }, { Source: api.EventSource{Component: "kubelet"}, Message: "Item 3", Timestamp: util.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)), }, }, } buffer := &bytes.Buffer{} // Act err := printer.PrintObj(&obj, buffer) // Assert if err != nil { t.Fatalf("An error occurred printing the EventList: %#v", err) } out := buffer.String() VerifyDatesInOrder(out, "\n" /* rowDelimiter */, " " /* columnDelimiter */, t) }
func TestPodDescribeResultsSorted(t *testing.T) { // Arrange fake := &client.Fake{ EventsList: api.EventList{ Items: []api.Event{ { Source: api.EventSource{Component: "kubelet"}, Message: "Item 1", Timestamp: util.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), }, { Source: api.EventSource{Component: "scheduler"}, Message: "Item 2", Timestamp: util.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)), }, { Source: api.EventSource{Component: "kubelet"}, Message: "Item 3", Timestamp: util.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)), }, }, }, } c := &describeClient{T: t, Namespace: "foo", Fake: fake} d := PodDescriber{c} // Act out, err := d.Describe("foo", "bar") // Assert if err != nil { t.Errorf("unexpected error: %v", err) } VerifyDatesInOrder(out, "\n" /* rowDelimiter */, "\t" /* columnDelimiter */, t) }
func TestSortableEvents(t *testing.T) { // Arrange list := SortableEvents([]api.Event{ { Source: api.EventSource{Component: "kubelet"}, Message: "Item 1", Timestamp: util.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), }, { Source: api.EventSource{Component: "scheduler"}, Message: "Item 2", Timestamp: util.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)), }, { Source: api.EventSource{Component: "kubelet"}, Message: "Item 3", Timestamp: util.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)), }, }) // Act sort.Sort(list) // Assert if list[0].Message != "Item 2" || list[1].Message != "Item 3" || list[2].Message != "Item 1" { t.Fatal("List is not sorted by time. List: ", list) } }
// TestSort verifies that builds are sorted by most recently created func TestSort(t *testing.T) { present := util.Now() past := util.NewTime(present.Time.Add(-1 * time.Minute)) builds := []*buildapi.Build{ { ObjectMeta: kapi.ObjectMeta{ Name: "past", CreationTimestamp: past, }, }, { ObjectMeta: kapi.ObjectMeta{ Name: "present", CreationTimestamp: present, }, }, } sort.Sort(sortableBuilds(builds)) if builds[0].Name != "present" { t.Errorf("Unexpected sort order") } if builds[1].Name != "past" { t.Errorf("Unexpected sort order") } }
// NewFilterBeforePredicate is a function that returns true if the build was created before the current time minus specified duration func NewFilterBeforePredicate(d time.Duration) FilterPredicate { now := util.Now() before := util.NewTime(now.Time.Add(-1 * d)) return func(build *buildapi.Build) bool { return build.CreationTimestamp.Before(before) } }
func TestFillPodStatus(t *testing.T) { pod := makePod(api.NamespaceDefault, "foo", "machine", "bar") expectedIP := "1.2.3.4" expectedTime, _ := time.Parse("2013-Feb-03", "2013-Feb-03") config := podCacheTestConfig{ kubeletContainerInfo: api.PodInfo{ "net": { State: api.ContainerState{ Running: &api.ContainerStateRunning{ StartedAt: util.NewTime(expectedTime), }, }, RestartCount: 1, PodIP: expectedIP, }, }, nodes: []api.Node{*makeNode("machine")}, pods: []api.Pod{*pod}, } cache := config.Construct() err := cache.updatePodStatus(&config.pods[0]) if err != nil { t.Fatalf("Unexpected error: %+v", err) } status, err := cache.GetPodStatus(pod.Namespace, pod.Name) if e, a := config.kubeletContainerInfo, status.Info; !reflect.DeepEqual(e, a) { t.Errorf("Expected: %+v, Got %+v", e, a) } if status.PodIP != expectedIP { t.Errorf("Expected %s, Got %s\n%+v", expectedIP, status.PodIP, status) } }
// TestSort verifies that builds are sorted by most recently created func TestSort(t *testing.T) { present := util.Now() past := util.NewTime(present.Time.Add(-1 * time.Minute)) controllers := []*kapi.ReplicationController{ { ObjectMeta: kapi.ObjectMeta{ Name: "past", CreationTimestamp: past, }, }, { ObjectMeta: kapi.ObjectMeta{ Name: "present", CreationTimestamp: present, }, }, } sort.Sort(sortableReplicationControllers(controllers)) if controllers[0].Name != "present" { t.Errorf("Unexpected sort order") } if controllers[1].Name != "past" { t.Errorf("Unexpected sort order") } }
// NewFilterBeforePredicate is a function that returns true if the build was created before the current time minus specified duration func NewFilterBeforePredicate(d time.Duration) FilterPredicate { now := util.Now() before := util.NewTime(now.Time.Add(-1 * d)) return func(item *kapi.ReplicationController) bool { return item.CreationTimestamp.Before(before) } }
func TestFilterBeforePredicate(t *testing.T) { youngerThan := time.Hour now := util.Now() old := util.NewTime(now.Time.Add(-1 * youngerThan)) builds := []*buildapi.Build{ { ObjectMeta: kapi.ObjectMeta{ Name: "old", CreationTimestamp: old, }, }, { ObjectMeta: kapi.ObjectMeta{ Name: "new", CreationTimestamp: now, }, }, } filter := &andFilter{ filterPredicates: []FilterPredicate{NewFilterBeforePredicate(youngerThan)}, } result := filter.Filter(builds) if len(result) != 1 { t.Errorf("Unexpected number of results") } if expected, actual := "old", result[0].Name; expected != actual { t.Errorf("expected %v, actual %v", expected, actual) } }
func TestBuildDecorator(t *testing.T) { build := &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{Name: "buildid", Namespace: "default"}, Parameters: buildapi.BuildParameters{ Source: buildapi.BuildSource{ Type: buildapi.BuildSourceGit, Git: &buildapi.GitBuildSource{ URI: "http://github.com/my/repository", }, ContextDir: "context", }, Strategy: buildapi.BuildStrategy{ Type: buildapi.DockerBuildStrategyType, DockerStrategy: &buildapi.DockerBuildStrategy{}, }, Output: buildapi.BuildOutput{ DockerImageReference: "repository/data", }, }, Status: buildapi.BuildStatusNew, } now := util.Now() startTime := util.NewTime(now.Time.Add(-1 * time.Minute)) build.StartTimestamp = &startTime err := Decorator(build) if err != nil { t.Errorf("Unexpected error decorating build") } if build.Duration <= 0 { t.Errorf("Build duration should be greater than zero") } }
func agedImage(id, ref string, ageInMinutes int64) imageapi.Image { image := imageWithLayers(id, ref, "tarsum.dev+sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "tarsum.dev+sha256:b194de3772ebbcdc8f244f663669799ac1cb141834b7cb8b69100285d357a2b0", "tarsum.dev+sha256:c937c4bb1c1a21cc6d94340812262c6472092028972ae69b551b1a70d4276171", "tarsum.dev+sha256:2aaacc362ac6be2b9e9ae8c6029f6f616bb50aec63746521858e47841b90fabd", "tarsum.dev+sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ) if ageInMinutes >= 0 { image.CreationTimestamp = util.NewTime(util.Now().Add(time.Duration(-1*ageInMinutes) * time.Minute)) } return image }
func init() { err := kapi.Scheme.AddConversionFuncs( // Convert docker client object to internal object, but only when this package is included func(in *docker.ImagePre012, out *newer.DockerImage, s conversion.Scope) error { if err := s.Convert(in.Config, &out.Config, conversion.AllowDifferentFieldTypeNames); err != nil { return err } if err := s.Convert(&in.ContainerConfig, &out.ContainerConfig, conversion.AllowDifferentFieldTypeNames); err != nil { return err } out.ID = in.ID out.Parent = in.Parent out.Comment = in.Comment out.Created = util.NewTime(in.Created) out.Container = in.Container out.DockerVersion = in.DockerVersion out.Author = in.Author out.Architecture = in.Architecture out.Size = in.Size return nil }, func(in *newer.DockerImage, out *docker.ImagePre012, s conversion.Scope) error { if err := s.Convert(&in.Config, &out.Config, conversion.AllowDifferentFieldTypeNames); err != nil { return err } if err := s.Convert(&in.ContainerConfig, &out.ContainerConfig, conversion.AllowDifferentFieldTypeNames); err != nil { return err } out.ID = in.ID out.Parent = in.Parent out.Comment = in.Comment out.Created = in.Created.Time out.Container = in.Container out.DockerVersion = in.DockerVersion out.Author = in.Author out.Architecture = in.Architecture out.Size = in.Size return nil }, ) if err != nil { // If one of the conversion functions is malformed, detect it immediately. panic(err) } }
func TestFilterBeforePredicate(t *testing.T) { youngerThan := time.Hour now := util.Now() old := util.NewTime(now.Time.Add(-1 * youngerThan)) items := []*kapi.ReplicationController{} items = append(items, withCreated(mockDeployment("a", "old", nil), old)) items = append(items, withCreated(mockDeployment("a", "new", nil), now)) filter := &andFilter{ filterPredicates: []FilterPredicate{NewFilterBeforePredicate(youngerThan)}, } result := filter.Filter(items) if len(result) != 1 { t.Errorf("Unexpected number of results") } if expected, actual := "old", result[0].Name; expected != actual { t.Errorf("expected %v, actual %v", expected, actual) } }
func agedPod(namespace, name string, phase kapi.PodPhase, ageInMinutes int64, containerImages ...string) kapi.Pod { pod := kapi.Pod{ ObjectMeta: kapi.ObjectMeta{ Namespace: namespace, Name: name, }, Spec: podSpec(containerImages...), Status: kapi.PodStatus{ Phase: phase, }, } if ageInMinutes >= 0 { pod.CreationTimestamp = util.NewTime(util.Now().Add(time.Duration(-1*ageInMinutes) * time.Minute)) } return pod }
func agedStream(registry, namespace, name string, ageInMinutes int64, tags map[string]imageapi.TagEventList) imageapi.ImageStream { stream := imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Namespace: namespace, Name: name, }, Status: imageapi.ImageStreamStatus{ DockerImageRepository: fmt.Sprintf("%s/%s/%s", registry, namespace, name), Tags: tags, }, } if ageInMinutes >= 0 { stream.CreationTimestamp = util.NewTime(util.Now().Add(time.Duration(-1*ageInMinutes) * time.Minute)) } return stream }
func TestNewStatusPreservesPodStartTime(t *testing.T) { syncer := newTestStatusManager() pod := &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "12345678", Name: "foo", Namespace: "new", }, Status: api.PodStatus{}, } now := util.Now() startTime := util.NewTime(now.Time.Add(-1 * time.Minute)) pod.Status.StartTime = &startTime syncer.SetPodStatus(pod, getRandomPodStatus()) status, _ := syncer.GetPodStatus(kubecontainer.GetPodFullName(pod)) if !status.StartTime.Time.Equal(startTime.Time) { t.Errorf("Unexpected start time, expected %v, actual %v", startTime, status.StartTime) } }
func TestSortByCreationTimestamp(t *testing.T) { present := util.Now() past := util.NewTime(present.Add(-time.Minute)) builds := []Build{ { ObjectMeta: kapi.ObjectMeta{ Name: "present", CreationTimestamp: present, }, }, { ObjectMeta: kapi.ObjectMeta{ Name: "past", CreationTimestamp: past, }, }, } sort.Sort(ByCreationTimestamp(builds)) if [2]string{builds[0].Name, builds[1].Name} != [2]string{"past", "present"} { t.Errorf("Unexpected sort order") } }
func TestPruneTask(t *testing.T) { buildStatusOptions := []buildapi.BuildStatus{ buildapi.BuildStatusCancelled, buildapi.BuildStatusComplete, buildapi.BuildStatusError, buildapi.BuildStatusFailed, buildapi.BuildStatusNew, buildapi.BuildStatusPending, buildapi.BuildStatusRunning, } buildStatusFilter := []buildapi.BuildStatus{ buildapi.BuildStatusCancelled, buildapi.BuildStatusComplete, buildapi.BuildStatusError, buildapi.BuildStatusFailed, } buildStatusFilterSet := util.StringSet{} for _, buildStatus := range buildStatusFilter { buildStatusFilterSet.Insert(string(buildStatus)) } for _, orphans := range []bool{true, false} { for _, buildStatusOption := range buildStatusOptions { keepYoungerThan := time.Hour now := util.Now() old := util.NewTime(now.Time.Add(-1 * keepYoungerThan)) buildConfigs := []*buildapi.BuildConfig{} builds := []*buildapi.Build{} buildConfig := mockBuildConfig("a", "build-config") buildConfigs = append(buildConfigs, buildConfig) builds = append(builds, withCreated(withStatus(mockBuild("a", "build-1", buildConfig), buildStatusOption), now)) builds = append(builds, withCreated(withStatus(mockBuild("a", "build-2", buildConfig), buildStatusOption), old)) builds = append(builds, withCreated(withStatus(mockBuild("a", "orphan-build-1", nil), buildStatusOption), now)) builds = append(builds, withCreated(withStatus(mockBuild("a", "orphan-build-2", nil), buildStatusOption), old)) keepComplete := 1 keepFailed := 1 expectedValues := util.StringSet{} filter := &andFilter{ filterPredicates: []FilterPredicate{NewFilterBeforePredicate(keepYoungerThan)}, } dataSet := NewDataSet(buildConfigs, filter.Filter(builds)) resolver := NewPerBuildConfigResolver(dataSet, keepComplete, keepFailed) if orphans { resolver = &mergeResolver{ resolvers: []Resolver{resolver, NewOrphanBuildResolver(dataSet, buildStatusFilter)}, } } expectedBuilds, err := resolver.Resolve() for _, build := range expectedBuilds { expectedValues.Insert(build.Name) } recorder := &mockPruneRecorder{set: util.StringSet{}} task := NewPruneTasker(buildConfigs, builds, keepYoungerThan, orphans, keepComplete, keepFailed, recorder.Handler) err = task.PruneTask() if err != nil { t.Errorf("Unexpected error %v", err) } recorder.Verify(t, expectedValues) } } }
defer GinkgoRecover() if p.Status.Phase == api.PodRunning { if _, found := watchTimes[p.Name]; !found { watchTimes[p.Name] = util.Now() createTimes[p.Name] = p.CreationTimestamp nodes[p.Name] = p.Spec.NodeName var startTime util.Time for _, cs := range p.Status.ContainerStatuses { if cs.State.Running != nil { if startTime.Before(cs.State.Running.StartedAt) { startTime = cs.State.Running.StartedAt } } } if startTime != util.NewTime(time.Time{}) { runTimes[p.Name] = startTime } else { Failf("Pod %v is reported to be running, but none of its containers is", p.Name) } } } } additionalPodsPrefix = "density-latency-pod-" + string(util.NewUUID()) _, controller := framework.NewInformer( &cache.ListWatch{ ListFunc: func() (runtime.Object, error) { return c.Pods(ns).List(labels.SelectorFromSet(labels.Set{"name": additionalPodsPrefix}), fields.Everything()) }, WatchFunc: func(rv string) (watch.Interface, error) {
func TestDescribeContainers(t *testing.T) { testCases := []struct { input api.ContainerStatus expectedElements []string }{ // Running state. { input: api.ContainerStatus{ Name: "test", State: api.ContainerState{ Running: &api.ContainerStateRunning{ StartedAt: util.NewTime(time.Now()), }, }, Ready: true, RestartCount: 7, Image: "image", }, expectedElements: []string{"test", "State", "Running", "Ready", "True", "Restart Count", "7", "Image", "image", "Started"}, }, // Waiting state. { input: api.ContainerStatus{ Name: "test", State: api.ContainerState{ Waiting: &api.ContainerStateWaiting{ Reason: "potato", }, }, Ready: true, RestartCount: 7, Image: "image", }, expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "Reason", "potato"}, }, // Terminated state. { input: api.ContainerStatus{ Name: "test", State: api.ContainerState{ Terminated: &api.ContainerStateTerminated{ StartedAt: util.NewTime(time.Now()), FinishedAt: util.NewTime(time.Now()), Reason: "potato", ExitCode: 2, }, }, Ready: true, RestartCount: 7, Image: "image", }, expectedElements: []string{"test", "State", "Terminated", "Ready", "True", "Restart Count", "7", "Image", "image", "Reason", "potato", "Started", "Finished", "Exit Code", "2"}, }, // No state defaults to waiting. { input: api.ContainerStatus{ Name: "test", Ready: true, RestartCount: 7, Image: "image", }, expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image"}, }, } for i, testCase := range testCases { out := new(bytes.Buffer) describeContainers([]api.ContainerStatus{testCase.input}, out) output := out.String() for _, expected := range testCase.expectedElements { if !strings.Contains(output, expected) { t.Errorf("Test case %d: expected to find %q in output: %q", i, expected, output) } } } }
func inspectContainer(client DockerInterface, dockerID, containerName, tPath string) (*api.ContainerStatus, error) { inspectResult, err := client.InspectContainer(dockerID) if err != nil { return nil, err } if inspectResult == nil { // Why did we not get an error? return &api.ContainerStatus{}, nil } glog.V(3).Infof("Container inspect result: %+v", *inspectResult) containerStatus := api.ContainerStatus{ Image: inspectResult.Config.Image, ContainerID: "docker://" + dockerID, } waiting := true if inspectResult.State.Running { containerStatus.State.Running = &api.ContainerStateRunning{ StartedAt: util.NewTime(inspectResult.State.StartedAt), } if containerName == "net" && inspectResult.NetworkSettings != nil { containerStatus.PodIP = inspectResult.NetworkSettings.IPAddress } waiting = false } else if !inspectResult.State.FinishedAt.IsZero() { reason := "" // Note: An application might handle OOMKilled gracefully. // In that case, the container is oom killed, but the exit // code could be 0. if inspectResult.State.OOMKilled { reason = "OOM Killed" } else { reason = inspectResult.State.Error } containerStatus.State.Termination = &api.ContainerStateTerminated{ ExitCode: inspectResult.State.ExitCode, Reason: reason, StartedAt: util.NewTime(inspectResult.State.StartedAt), FinishedAt: util.NewTime(inspectResult.State.FinishedAt), } if tPath != "" { path, found := inspectResult.Volumes[tPath] if found { data, err := ioutil.ReadFile(path) if err != nil { glog.Errorf("Error on reading termination-log %s: %v", path, err) } else { containerStatus.State.Termination.Message = string(data) } } } waiting = false } if waiting { // TODO(dchen1107): Separate issue docker/docker#8294 was filed // TODO(dchen1107): Need to figure out why we are still waiting // Check any issue to run container containerStatus.State.Waiting = &api.ContainerStateWaiting{ Reason: ErrContainerCannotRun.Error(), } } return &containerStatus, nil }
func TestPruneTask(t *testing.T) { deploymentStatusOptions := []deployapi.DeploymentStatus{ deployapi.DeploymentStatusComplete, deployapi.DeploymentStatusFailed, deployapi.DeploymentStatusNew, deployapi.DeploymentStatusPending, deployapi.DeploymentStatusRunning, } deploymentStatusFilter := []deployapi.DeploymentStatus{ deployapi.DeploymentStatusComplete, deployapi.DeploymentStatusFailed, } deploymentStatusFilterSet := util.StringSet{} for _, deploymentStatus := range deploymentStatusFilter { deploymentStatusFilterSet.Insert(string(deploymentStatus)) } for _, orphans := range []bool{true, false} { for _, deploymentStatusOption := range deploymentStatusOptions { keepYoungerThan := time.Hour now := util.Now() old := util.NewTime(now.Time.Add(-1 * keepYoungerThan)) deploymentConfigs := []*deployapi.DeploymentConfig{} deployments := []*kapi.ReplicationController{} deploymentConfig := mockDeploymentConfig("a", "deployment-config") deploymentConfigs = append(deploymentConfigs, deploymentConfig) deployments = append(deployments, withCreated(withStatus(mockDeployment("a", "build-1", deploymentConfig), deploymentStatusOption), now)) deployments = append(deployments, withCreated(withStatus(mockDeployment("a", "build-2", deploymentConfig), deploymentStatusOption), old)) deployments = append(deployments, withSize(withCreated(withStatus(mockDeployment("a", "build-3-with-replicas", deploymentConfig), deploymentStatusOption), old), 4)) deployments = append(deployments, withCreated(withStatus(mockDeployment("a", "orphan-build-1", nil), deploymentStatusOption), now)) deployments = append(deployments, withCreated(withStatus(mockDeployment("a", "orphan-build-2", nil), deploymentStatusOption), old)) deployments = append(deployments, withSize(withCreated(withStatus(mockDeployment("a", "orphan-build-3-with-replicas", nil), deploymentStatusOption), old), 4)) keepComplete := 1 keepFailed := 1 expectedValues := util.StringSet{} filter := &andFilter{ filterPredicates: []FilterPredicate{ FilterDeploymentsPredicate, FilterZeroReplicaSize, NewFilterBeforePredicate(keepYoungerThan), }, } dataSet := NewDataSet(deploymentConfigs, filter.Filter(deployments)) resolver := NewPerDeploymentConfigResolver(dataSet, keepComplete, keepFailed) if orphans { resolver = &mergeResolver{ resolvers: []Resolver{resolver, NewOrphanDeploymentResolver(dataSet, deploymentStatusFilter)}, } } expectedDeployments, err := resolver.Resolve() for _, item := range expectedDeployments { expectedValues.Insert(item.Name) } recorder := &mockPruneRecorder{set: util.StringSet{}} task := NewPruneTasker(deploymentConfigs, deployments, keepYoungerThan, orphans, keepComplete, keepFailed, recorder.Handler) err = task.PruneTask() if err != nil { t.Errorf("Unexpected error %v", err) } recorder.Verify(t, expectedValues) } } }
func TestPerDeploymentConfigResolver(t *testing.T) { deploymentStatusOptions := []deployapi.DeploymentStatus{ deployapi.DeploymentStatusComplete, deployapi.DeploymentStatusFailed, deployapi.DeploymentStatusNew, deployapi.DeploymentStatusPending, deployapi.DeploymentStatusRunning, } deploymentConfigs := []*deployapi.DeploymentConfig{ mockDeploymentConfig("a", "deployment-config-1"), mockDeploymentConfig("b", "deployment-config-2"), } deploymentsPerStatus := 100 deployments := []*kapi.ReplicationController{} for _, deploymentConfig := range deploymentConfigs { for _, deploymentStatusOption := range deploymentStatusOptions { for i := 0; i < deploymentsPerStatus; i++ { deployment := withStatus(mockDeployment(deploymentConfig.Namespace, fmt.Sprintf("%v-%v-%v", deploymentConfig.Name, deploymentStatusOption, i), deploymentConfig), deploymentStatusOption) deployments = append(deployments, deployment) } } } now := util.Now() for i := range deployments { creationTimestamp := util.NewTime(now.Time.Add(-1 * time.Duration(i) * time.Hour)) deployments[i].CreationTimestamp = creationTimestamp } // test number to keep at varying ranges for keep := 0; keep < deploymentsPerStatus*2; keep++ { dataSet := NewDataSet(deploymentConfigs, deployments) expectedNames := util.StringSet{} deploymentCompleteStatusFilterSet := util.NewStringSet(string(deployapi.DeploymentStatusComplete)) deploymentFailedStatusFilterSet := util.NewStringSet(string(deployapi.DeploymentStatusFailed)) for _, deploymentConfig := range deploymentConfigs { deploymentItems, err := dataSet.ListDeploymentsByDeploymentConfig(deploymentConfig) if err != nil { t.Errorf("Unexpected err %v", err) } completedDeployments, failedDeployments := []*kapi.ReplicationController{}, []*kapi.ReplicationController{} for _, deployment := range deploymentItems { status := deployment.Annotations[deployapi.DeploymentStatusAnnotation] if deploymentCompleteStatusFilterSet.Has(status) { completedDeployments = append(completedDeployments, deployment) } else if deploymentFailedStatusFilterSet.Has(status) { failedDeployments = append(failedDeployments, deployment) } } sort.Sort(sortableReplicationControllers(completedDeployments)) sort.Sort(sortableReplicationControllers(failedDeployments)) purgeCompleted := []*kapi.ReplicationController{} purgeFailed := []*kapi.ReplicationController{} if keep >= 0 && keep < len(completedDeployments) { purgeCompleted = completedDeployments[keep:] } if keep >= 0 && keep < len(failedDeployments) { purgeFailed = failedDeployments[keep:] } for _, deployment := range purgeCompleted { expectedNames.Insert(deployment.Name) } for _, deployment := range purgeFailed { expectedNames.Insert(deployment.Name) } } resolver := NewPerDeploymentConfigResolver(dataSet, keep, keep) results, err := resolver.Resolve() if err != nil { t.Errorf("Unexpected error %v", err) } foundNames := util.StringSet{} for _, result := range results { foundNames.Insert(result.Name) } if len(foundNames) != len(expectedNames) || !expectedNames.HasAll(foundNames.List()...) { expectedValues := expectedNames.List() actualValues := foundNames.List() sort.Strings(expectedValues) sort.Strings(actualValues) t.Errorf("keep %v\n, expected \n\t%v\n, actual \n\t%v\n", keep, expectedValues, actualValues) } } }
func TestGraph(t *testing.T) { g := osgraph.New() now := time.Now() builds := []buildapi.Build{ { ObjectMeta: kapi.ObjectMeta{ Name: "build1-1-abc", Labels: map[string]string{buildapi.BuildConfigLabel: "build1"}, CreationTimestamp: util.NewTime(now.Add(-10 * time.Second)), }, Status: buildapi.BuildStatus{ Phase: buildapi.BuildPhaseFailed, }, }, { ObjectMeta: kapi.ObjectMeta{ Name: "build1-2-abc", Labels: map[string]string{buildapi.BuildConfigLabel: "build1"}, CreationTimestamp: util.NewTime(now.Add(-5 * time.Second)), }, Status: buildapi.BuildStatus{ Phase: buildapi.BuildPhaseComplete, }, }, { ObjectMeta: kapi.ObjectMeta{ Name: "build1-3-abc", Labels: map[string]string{buildapi.BuildConfigLabel: "build1"}, CreationTimestamp: util.NewTime(now.Add(-15 * time.Second)), }, Status: buildapi.BuildStatus{ Phase: buildapi.BuildPhasePending, }, }, } for i := range builds { buildgraph.EnsureBuildNode(g, &builds[i]) } buildgraph.EnsureBuildConfigNode(g, &buildapi.BuildConfig{ ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "build1"}, Spec: buildapi.BuildConfigSpec{ Triggers: []buildapi.BuildTriggerPolicy{ { ImageChange: &buildapi.ImageChangeTrigger{}, }, }, BuildSpec: buildapi.BuildSpec{ Strategy: buildapi.BuildStrategy{ Type: buildapi.SourceBuildStrategyType, SourceStrategy: &buildapi.SourceBuildStrategy{ From: kapi.ObjectReference{Kind: "ImageStreamTag", Name: "test:base-image"}, }, }, Output: buildapi.BuildOutput{ To: &kapi.ObjectReference{Kind: "ImageStreamTag", Name: "other:tag1"}, }, }, }, }) bcTestNode := buildgraph.EnsureBuildConfigNode(g, &buildapi.BuildConfig{ ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "test"}, Spec: buildapi.BuildConfigSpec{ BuildSpec: buildapi.BuildSpec{ Output: buildapi.BuildOutput{ To: &kapi.ObjectReference{Kind: "ImageStreamTag", Name: "other:base-image"}, }, }, }, }) buildgraph.EnsureBuildConfigNode(g, &buildapi.BuildConfig{ ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "build2"}, Spec: buildapi.BuildConfigSpec{ BuildSpec: buildapi.BuildSpec{ Output: buildapi.BuildOutput{ To: &kapi.ObjectReference{Kind: "DockerImage", Name: "mycustom/repo/image:tag2"}, }, }, }, }) kubegraph.EnsureServiceNode(g, &kapi.Service{ ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "svc-is-ignored"}, Spec: kapi.ServiceSpec{ Selector: nil, }, }) kubegraph.EnsureServiceNode(g, &kapi.Service{ ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "svc1"}, Spec: kapi.ServiceSpec{ Selector: map[string]string{ "deploymentconfig": "deploy1", }, }, }) kubegraph.EnsureServiceNode(g, &kapi.Service{ ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "svc2"}, Spec: kapi.ServiceSpec{ Selector: map[string]string{ "deploymentconfig": "deploy1", "env": "prod", }, }, }) deploygraph.EnsureDeploymentConfigNode(g, &deployapi.DeploymentConfig{ ObjectMeta: kapi.ObjectMeta{Namespace: "other", Name: "deploy1"}, Triggers: []deployapi.DeploymentTriggerPolicy{ { ImageChangeParams: &deployapi.DeploymentTriggerImageChangeParams{ From: kapi.ObjectReference{Namespace: "default", Name: "other"}, ContainerNames: []string{"1", "2"}, Tag: "tag1", }, }, }, Template: deployapi.DeploymentTemplate{ ControllerTemplate: kapi.ReplicationControllerSpec{ Template: &kapi.PodTemplateSpec{ ObjectMeta: kapi.ObjectMeta{ Labels: map[string]string{ "deploymentconfig": "deploy1", "env": "prod", }, }, Spec: kapi.PodSpec{ Containers: []kapi.Container{ { Name: "1", Image: "mycustom/repo/image", }, { Name: "2", Image: "mycustom/repo/image2", }, { Name: "3", Image: "mycustom/repo/image3", }, }, }, }, }, }, }) deploygraph.EnsureDeploymentConfigNode(g, &deployapi.DeploymentConfig{ ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "deploy2"}, Template: deployapi.DeploymentTemplate{ ControllerTemplate: kapi.ReplicationControllerSpec{ Template: &kapi.PodTemplateSpec{ ObjectMeta: kapi.ObjectMeta{ Labels: map[string]string{ "deploymentconfig": "deploy2", "env": "dev", }, }, Spec: kapi.PodSpec{ Containers: []kapi.Container{ { Name: "1", Image: "someother/image:v1", }, }, }, }, }, }, }) kubeedges.AddAllExposedPodTemplateSpecEdges(g) buildedges.AddAllInputOutputEdges(g) buildedges.AddAllBuildEdges(g) deployedges.AddAllTriggerEdges(g) deployedges.AddAllDeploymentEdges(g) t.Log(g) for _, edge := range g.EdgeList() { if g.EdgeKind(edge) == osgraph.UnknownEdgeKind { t.Errorf("edge reported unknown kind: %#v", edge) } } // imagestreamtag default/other:base-image istID := 0 for _, node := range g.NodeList() { if g.Name(node) == "ImageStreamTag|default/other:base-image" { istID = node.ID() break } } edge := g.EdgeBetween(concrete.Node(bcTestNode.ID()), concrete.Node(istID)) if edge == nil { t.Fatalf("failed to find edge between %d and %d", bcTestNode.ID(), istID) } if len(g.SubgraphWithNodes([]graph.Node{edge.Head(), edge.Tail()}, osgraph.ExistingDirectEdge).EdgeList()) != 1 { t.Fatalf("expected one edge") } if len(g.SubgraphWithNodes([]graph.Node{edge.Tail(), edge.Head()}, osgraph.ExistingDirectEdge).EdgeList()) != 1 { t.Fatalf("expected one edge") } if e := g.EdgeBetween(concrete.Node(bcTestNode.ID()), concrete.Node(istID)); e == nil { t.Errorf("expected edge for %d-%d", bcTestNode.ID(), istID) } if e := g.EdgeBetween(concrete.Node(istID), concrete.Node(bcTestNode.ID())); e == nil { t.Errorf("expected edge for %d-%d", bcTestNode.ID(), istID) } coveredNodes := IntSet{} serviceGroups, coveredByServiceGroups := AllServiceGroups(g, coveredNodes) coveredNodes.Insert(coveredByServiceGroups.List()...) bareDCPipelines, coveredByDCs := AllDeploymentConfigPipelines(g, coveredNodes) coveredNodes.Insert(coveredByDCs.List()...) if len(bareDCPipelines) != 1 { t.Fatalf("unexpected pipelines: %#v", bareDCPipelines) } if len(coveredNodes) != 10 { t.Fatalf("unexpected covered nodes: %#v", coveredNodes) } for _, bareDCPipeline := range bareDCPipelines { t.Logf("from %s", bareDCPipeline.Deployment.Name) for _, path := range bareDCPipeline.Images { t.Logf(" %v", path) } } if len(serviceGroups) != 3 { t.Errorf("unexpected service groups: %#v", serviceGroups) } for _, serviceGroup := range serviceGroups { t.Logf("service %s", serviceGroup.Service.Name) indent := " " for _, deployment := range serviceGroup.DeploymentConfigPipelines { t.Logf("%sdeployment %s", indent, deployment.Deployment.Name) for _, image := range deployment.Images { t.Logf("%s image %s", indent, image.Image.ImageSpec()) if image.Build != nil { if image.LastSuccessfulBuild != nil { t.Logf("%s built at %s", indent, image.LastSuccessfulBuild.Build.CreationTimestamp) } else if image.LastUnsuccessfulBuild != nil { t.Logf("%s build %s at %s", indent, image.LastUnsuccessfulBuild.Build.Status, image.LastUnsuccessfulBuild.Build.CreationTimestamp) } for _, b := range image.ActiveBuilds { t.Logf("%s build %s %s", indent, b.Build.Name, b.Build.Status) } } } } } }
func (dm *DockerManager) inspectContainer(dockerID, containerName, tPath string) *containerStatusResult { result := containerStatusResult{api.ContainerStatus{}, "", nil} inspectResult, err := dm.client.InspectContainer(dockerID) if err != nil { result.err = err return &result } if inspectResult == nil { // Why did we not get an error? return &result } glog.V(3).Infof("Container inspect result: %+v", *inspectResult) result.status = api.ContainerStatus{ Name: containerName, Image: inspectResult.Config.Image, ImageID: DockerPrefix + inspectResult.Image, ContainerID: DockerPrefix + dockerID, } if inspectResult.State.Running { result.status.State.Running = &api.ContainerStateRunning{ StartedAt: util.NewTime(inspectResult.State.StartedAt), } if containerName == PodInfraContainerName && inspectResult.NetworkSettings != nil { result.ip = inspectResult.NetworkSettings.IPAddress } } else if !inspectResult.State.FinishedAt.IsZero() { reason := "" // Note: An application might handle OOMKilled gracefully. // In that case, the container is oom killed, but the exit // code could be 0. if inspectResult.State.OOMKilled { reason = "OOM Killed" } else { reason = inspectResult.State.Error } result.status.State.Termination = &api.ContainerStateTerminated{ ExitCode: inspectResult.State.ExitCode, Reason: reason, StartedAt: util.NewTime(inspectResult.State.StartedAt), FinishedAt: util.NewTime(inspectResult.State.FinishedAt), ContainerID: DockerPrefix + dockerID, } if tPath != "" { path, found := inspectResult.Volumes[tPath] if found { data, err := ioutil.ReadFile(path) if err != nil { glog.Errorf("Error on reading termination-log %s: %v", path, err) } else { result.status.State.Termination.Message = string(data) } } } } else { // TODO(dchen1107): Separate issue docker/docker#8294 was filed result.status.State.Waiting = &api.ContainerStateWaiting{ Reason: ErrContainerCannotRun.Error(), } } return &result }
func TestPrintHumanReadableWithNamespace(t *testing.T) { namespaceName := "testnamespace" name := "test" table := []struct { obj runtime.Object printNamespace bool }{ { obj: &api.Pod{ ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, }, printNamespace: true, }, { obj: &api.ReplicationController{ ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, Spec: api.ReplicationControllerSpec{ Replicas: 2, Template: &api.PodTemplateSpec{ ObjectMeta: api.ObjectMeta{ Labels: map[string]string{ "name": "foo", "type": "production", }, }, Spec: api.PodSpec{ Containers: []api.Container{ { Image: "foo/bar", TerminationMessagePath: api.TerminationMessagePathDefault, ImagePullPolicy: api.PullIfNotPresent, }, }, RestartPolicy: api.RestartPolicyAlways, DNSPolicy: api.DNSDefault, NodeSelector: map[string]string{ "baz": "blah", }, }, }, }, }, printNamespace: true, }, { obj: &api.Service{ ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, Spec: api.ServiceSpec{ ClusterIP: "1.2.3.4", Ports: []api.ServicePort{ { Port: 80, Protocol: "TCP", }, }, }, Status: api.ServiceStatus{ LoadBalancer: api.LoadBalancerStatus{ Ingress: []api.LoadBalancerIngress{ { IP: "2.3.4.5", }, }, }, }, }, printNamespace: true, }, { obj: &api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, Subsets: []api.EndpointSubset{{ Addresses: []api.EndpointAddress{{IP: "127.0.0.1"}, {IP: "localhost"}}, Ports: []api.EndpointPort{{Port: 8080}}, }, }}, printNamespace: true, }, { obj: &api.Namespace{ ObjectMeta: api.ObjectMeta{Name: name}, }, printNamespace: false, }, { obj: &api.Secret{ ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, }, printNamespace: true, }, { obj: &api.ServiceAccount{ ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, Secrets: []api.ObjectReference{}, }, printNamespace: true, }, { obj: &api.Node{ ObjectMeta: api.ObjectMeta{Name: name}, Status: api.NodeStatus{}, }, printNamespace: false, }, { obj: &api.PersistentVolume{ ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, Spec: api.PersistentVolumeSpec{}, }, printNamespace: true, }, { obj: &api.PersistentVolumeClaim{ ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, Spec: api.PersistentVolumeClaimSpec{}, }, printNamespace: true, }, { obj: &api.Event{ ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, Source: api.EventSource{Component: "kubelet"}, Message: "Item 1", FirstTimestamp: util.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), LastTimestamp: util.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), Count: 1, }, printNamespace: false, }, { obj: &api.LimitRange{ ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, }, printNamespace: true, }, { obj: &api.ResourceQuota{ ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, }, printNamespace: true, }, { obj: &api.ComponentStatus{ Conditions: []api.ComponentCondition{ {Type: api.ComponentHealthy, Status: api.ConditionTrue, Message: "ok", Error: ""}, }, }, printNamespace: false, }, } printer := NewHumanReadablePrinter(false, false) for _, test := range table { buffer := &bytes.Buffer{} err := printer.PrintObj(test.obj, buffer) if err != nil { t.Fatalf("An error occurred printing object: %#v", err) } matched := contains(strings.Fields(buffer.String()), fmt.Sprintf("%s/%s", namespaceName, name)) if matched { t.Errorf("Expect printing object not to contain namespace: %v", test.obj) } } printer = NewHumanReadablePrinter(false, true) for _, test := range table { buffer := &bytes.Buffer{} err := printer.PrintObj(test.obj, buffer) if err != nil { t.Fatalf("An error occurred printing object: %#v", err) } matched := contains(strings.Fields(buffer.String()), fmt.Sprintf("%s/%s", namespaceName, name)) if test.printNamespace && !matched { t.Errorf("Expect printing object to contain namespace: %v", test.obj) } else if !test.printNamespace && matched { t.Errorf("Expect printing object not to contain namespace: %v", test.obj) } } }
func TestPerBuildConfigResolver(t *testing.T) { buildStatusOptions := []buildapi.BuildStatus{ buildapi.BuildStatusCancelled, buildapi.BuildStatusComplete, buildapi.BuildStatusError, buildapi.BuildStatusFailed, buildapi.BuildStatusNew, buildapi.BuildStatusPending, buildapi.BuildStatusRunning, } buildConfigs := []*buildapi.BuildConfig{ mockBuildConfig("a", "build-config-1"), mockBuildConfig("b", "build-config-2"), } buildsPerStatus := 100 builds := []*buildapi.Build{} for _, buildConfig := range buildConfigs { for _, buildStatusOption := range buildStatusOptions { for i := 0; i < buildsPerStatus; i++ { build := withStatus(mockBuild(buildConfig.Namespace, fmt.Sprintf("%v-%v-%v", buildConfig.Name, buildStatusOption, i), buildConfig), buildStatusOption) builds = append(builds, build) } } } now := util.Now() for i := range builds { creationTimestamp := util.NewTime(now.Time.Add(-1 * time.Duration(i) * time.Hour)) builds[i].CreationTimestamp = creationTimestamp } // test number to keep at varying ranges for keep := 0; keep < buildsPerStatus*2; keep++ { dataSet := NewDataSet(buildConfigs, builds) expectedNames := util.StringSet{} buildCompleteStatusFilterSet := util.NewStringSet(string(buildapi.BuildStatusComplete)) buildFailedStatusFilterSet := util.NewStringSet(string(buildapi.BuildStatusCancelled), string(buildapi.BuildStatusError), string(buildapi.BuildStatusFailed)) for _, buildConfig := range buildConfigs { buildItems, err := dataSet.ListBuildsByBuildConfig(buildConfig) if err != nil { t.Errorf("Unexpected err %v", err) } completedBuilds, failedBuilds := []*buildapi.Build{}, []*buildapi.Build{} for _, build := range buildItems { if buildCompleteStatusFilterSet.Has(string(build.Status)) { completedBuilds = append(completedBuilds, build) } else if buildFailedStatusFilterSet.Has(string(build.Status)) { failedBuilds = append(failedBuilds, build) } } sort.Sort(sortableBuilds(completedBuilds)) sort.Sort(sortableBuilds(failedBuilds)) purgeCompleted := []*buildapi.Build{} purgeFailed := []*buildapi.Build{} if keep >= 0 && keep < len(completedBuilds) { purgeCompleted = completedBuilds[keep:] } if keep >= 0 && keep < len(failedBuilds) { purgeFailed = failedBuilds[keep:] } for _, build := range purgeCompleted { expectedNames.Insert(build.Name) } for _, build := range purgeFailed { expectedNames.Insert(build.Name) } } resolver := NewPerBuildConfigResolver(dataSet, keep, keep) results, err := resolver.Resolve() if err != nil { t.Errorf("Unexpected error %v", err) } foundNames := util.StringSet{} for _, result := range results { foundNames.Insert(result.Name) } if len(foundNames) != len(expectedNames) || !expectedNames.HasAll(foundNames.List()...) { expectedValues := expectedNames.List() actualValues := foundNames.List() sort.Strings(expectedValues) sort.Strings(actualValues) t.Errorf("keep %v\n, expected \n\t%v\n, actual \n\t%v\n", keep, expectedValues, actualValues) } } }
func TestGraph(t *testing.T) { g := osgraph.New() now := time.Now() builds := []buildapi.Build{ { ObjectMeta: kapi.ObjectMeta{ Name: "build1-1-abc", Labels: map[string]string{buildapi.BuildConfigLabel: "build1"}, CreationTimestamp: util.NewTime(now.Add(-10 * time.Second)), }, Status: buildapi.BuildStatusFailed, }, { ObjectMeta: kapi.ObjectMeta{ Name: "build1-2-abc", Labels: map[string]string{buildapi.BuildConfigLabel: "build1"}, CreationTimestamp: util.NewTime(now.Add(-5 * time.Second)), }, Status: buildapi.BuildStatusComplete, }, { ObjectMeta: kapi.ObjectMeta{ Name: "build1-3-abc", Labels: map[string]string{buildapi.BuildConfigLabel: "build1"}, CreationTimestamp: util.NewTime(now.Add(-15 * time.Second)), }, Status: buildapi.BuildStatusPending, }, } bc1Node := buildgraph.EnsureBuildConfigNode(g, &buildapi.BuildConfig{ ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "build1"}, Triggers: []buildapi.BuildTriggerPolicy{ { ImageChange: &buildapi.ImageChangeTrigger{}, }, }, Parameters: buildapi.BuildParameters{ Strategy: buildapi.BuildStrategy{ Type: buildapi.SourceBuildStrategyType, SourceStrategy: &buildapi.SourceBuildStrategy{ From: kapi.ObjectReference{Kind: "ImageStreamTag", Name: "test:base-image"}, }, }, Output: buildapi.BuildOutput{ To: &kapi.ObjectReference{Name: "other"}, Tag: "tag1", }, }, }) buildedges.JoinBuilds(bc1Node, builds) bcTestNode := buildgraph.EnsureBuildConfigNode(g, &buildapi.BuildConfig{ ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "test"}, Parameters: buildapi.BuildParameters{ Output: buildapi.BuildOutput{ To: &kapi.ObjectReference{Name: "other"}, Tag: "base-image", }, }, }) buildgraph.EnsureBuildConfigNode(g, &buildapi.BuildConfig{ ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "build2"}, Parameters: buildapi.BuildParameters{ Output: buildapi.BuildOutput{ DockerImageReference: "mycustom/repo/image", Tag: "tag2", }, }, }) kubegraph.EnsureServiceNode(g, &kapi.Service{ ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "svc-is-ignored"}, Spec: kapi.ServiceSpec{ Selector: nil, }, }) kubegraph.EnsureServiceNode(g, &kapi.Service{ ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "svc1"}, Spec: kapi.ServiceSpec{ Selector: map[string]string{ "deploymentconfig": "deploy1", }, }, }) kubegraph.EnsureServiceNode(g, &kapi.Service{ ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "svc2"}, Spec: kapi.ServiceSpec{ Selector: map[string]string{ "deploymentconfig": "deploy1", "env": "prod", }, }, }) deploygraph.EnsureDeploymentConfigNode(g, &deployapi.DeploymentConfig{ ObjectMeta: kapi.ObjectMeta{Namespace: "other", Name: "deploy1"}, Triggers: []deployapi.DeploymentTriggerPolicy{ { ImageChangeParams: &deployapi.DeploymentTriggerImageChangeParams{ From: kapi.ObjectReference{Namespace: "default", Name: "other"}, ContainerNames: []string{"1", "2"}, Tag: "tag1", }, }, }, Template: deployapi.DeploymentTemplate{ ControllerTemplate: kapi.ReplicationControllerSpec{ Template: &kapi.PodTemplateSpec{ ObjectMeta: kapi.ObjectMeta{ Labels: map[string]string{ "deploymentconfig": "deploy1", "env": "prod", }, }, Spec: kapi.PodSpec{ Containers: []kapi.Container{ { Name: "1", Image: "mycustom/repo/image", }, { Name: "2", Image: "mycustom/repo/image2", }, { Name: "3", Image: "mycustom/repo/image3", }, }, }, }, }, }, }) deploygraph.EnsureDeploymentConfigNode(g, &deployapi.DeploymentConfig{ ObjectMeta: kapi.ObjectMeta{Namespace: "default", Name: "deploy2"}, Template: deployapi.DeploymentTemplate{ ControllerTemplate: kapi.ReplicationControllerSpec{ Template: &kapi.PodTemplateSpec{ ObjectMeta: kapi.ObjectMeta{ Labels: map[string]string{ "deploymentconfig": "deploy2", "env": "dev", }, }, Spec: kapi.PodSpec{ Containers: []kapi.Container{ { Name: "1", Image: "someother/image:v1", }, }, }, }, }, }, }) kubeedges.AddAllExposedPodTemplateSpecEdges(g) buildedges.AddAllInputOutputEdges(g) deployedges.AddAllTriggerEdges(g) t.Log(g) ir, dc, bc, other := 0, 0, 0, 0 for _, node := range g.NodeList() { switch g.Object(node).(type) { case *deployapi.DeploymentConfig: if g.Kind(node) != deploygraph.DeploymentConfigNodeKind { t.Fatalf("unexpected kind: %v", g.Kind(node)) } dc++ case *buildapi.BuildConfig: if g.Kind(node) != buildgraph.BuildConfigNodeKind { t.Fatalf("unexpected kind: %v", g.Kind(node)) } bc++ case *imageapi.ImageStream: // TODO resolve this check for 2 kinds, since both have the same object type if g.Kind(node) != imagegraph.ImageStreamNodeKind && g.Kind(node) != imagegraph.ImageStreamTagNodeKind { t.Fatalf("unexpected kind: %v", g.Kind(node)) } ir++ default: other++ } } if dc != 2 || bc != 3 || ir != 3 || other != 12 { t.Errorf("unexpected nodes: %d %d %d %d", dc, bc, ir, other) } for _, edge := range g.EdgeList() { if g.EdgeKind(edge) == osgraph.UnknownEdgeKind { t.Errorf("edge reported unknown kind: %#v", edge) } } // imagestreamtag default/other:base-image istID := 0 for _, node := range g.NodeList() { if g.Name(node) == "<imagestreamtag default/other:base-image>" { istID = node.ID() break } } edge := g.EdgeBetween(concrete.Node(bcTestNode.ID()), concrete.Node(istID)) if edge == nil { t.Fatalf("failed to find edge between %d and %d", bcTestNode.ID(), istID) } if len(g.SubgraphWithNodes([]graph.Node{edge.Head(), edge.Tail()}, osgraph.ExistingDirectEdge).EdgeList()) != 1 { t.Fatalf("expected one edge") } if len(g.SubgraphWithNodes([]graph.Node{edge.Tail(), edge.Head()}, osgraph.ExistingDirectEdge).EdgeList()) != 1 { t.Fatalf("expected one edge") } if e := g.EdgeBetween(concrete.Node(bcTestNode.ID()), concrete.Node(istID)); e == nil { t.Errorf("expected edge for %d-%d", bcTestNode.ID(), istID) } if e := g.EdgeBetween(concrete.Node(istID), concrete.Node(bcTestNode.ID())); e == nil { t.Errorf("expected edge for %d-%d", bcTestNode.ID(), istID) } pipelines, covered := DeploymentPipelines(g) if len(pipelines) != 2 { t.Fatalf("unexpected pipelines: %#v", pipelines) } if len(covered) != 7 { t.Fatalf("unexpected covered nodes: %#v", covered) } for from, images := range pipelines { t.Logf("from %s", from.Name) for _, path := range images { t.Logf(" %v", path) } } serviceGroups := ServiceAndDeploymentGroups(g) if len(serviceGroups) != 5 { t.Errorf("unexpected service groups: %#v", serviceGroups) } if len(serviceGroups[3].Builds) != 1 { t.Fatalf("unexpected final group: %#v", serviceGroups[2]) } for _, group := range serviceGroups { dcs := len(group.Deployments) svcs := len(group.Services) for _, svc := range group.Services { t.Logf("service %s", svc.Service.Name) } indent := "" if svcs > 0 { indent = " " } for _, deployment := range group.Deployments { t.Logf("%sdeployment %s", indent, deployment.Deployment.Name) for _, image := range deployment.Images { t.Logf("%s image %s", indent, image.Image.ImageSpec()) if image.Build != nil { if image.Build.LastSuccessfulBuild != nil { t.Logf("%s built at %s", indent, image.Build.LastSuccessfulBuild.CreationTimestamp) } else if image.Build.LastUnsuccessfulBuild != nil { t.Logf("%s build %s at %s", indent, image.Build.LastUnsuccessfulBuild.Status, image.Build.LastSuccessfulBuild.CreationTimestamp) } for _, b := range image.Build.ActiveBuilds { t.Logf("%s build %s %s", indent, b.Name, b.Status) } } } } if dcs != 0 || svcs != 0 { continue } for _, build := range group.Builds { if build.Image != nil { if build.Build != nil { t.Logf("%s <- build %s (%d)", build.Image.ImageSpec(), build.Build.Name, build.Image.ID()) } else { t.Logf("%s (%d)", build.Image.ImageSpec(), build.Image.ID()) } } else { t.Logf("build %s (%d)", build.Build.Name, build.Build.ID()) t.Errorf("expected build %d to have an image edge", build.Build.ID()) } } } }
func TestDescribeContainers(t *testing.T) { testCases := []struct { container api.Container status api.ContainerStatus expectedElements []string }{ // Running state. { container: api.Container{Name: "test", Image: "image"}, status: api.ContainerStatus{ Name: "test", State: api.ContainerState{ Running: &api.ContainerStateRunning{ StartedAt: util.NewTime(time.Now()), }, }, Ready: true, RestartCount: 7, }, expectedElements: []string{"test", "State", "Running", "Ready", "True", "Restart Count", "7", "Image", "image", "Started"}, }, // Waiting state. { container: api.Container{Name: "test", Image: "image"}, status: api.ContainerStatus{ Name: "test", State: api.ContainerState{ Waiting: &api.ContainerStateWaiting{ Reason: "potato", }, }, Ready: true, RestartCount: 7, }, expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "Reason", "potato"}, }, // Terminated state. { container: api.Container{Name: "test", Image: "image"}, status: api.ContainerStatus{ Name: "test", State: api.ContainerState{ Terminated: &api.ContainerStateTerminated{ StartedAt: util.NewTime(time.Now()), FinishedAt: util.NewTime(time.Now()), Reason: "potato", ExitCode: 2, }, }, Ready: true, RestartCount: 7, }, expectedElements: []string{"test", "State", "Terminated", "Ready", "True", "Restart Count", "7", "Image", "image", "Reason", "potato", "Started", "Finished", "Exit Code", "2"}, }, // No state defaults to waiting. { container: api.Container{Name: "test", Image: "image"}, status: api.ContainerStatus{ Name: "test", Ready: true, RestartCount: 7, }, expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image"}, }, //env { container: api.Container{Name: "test", Image: "image", Env: []api.EnvVar{{Name: "envname", Value: "xyz"}}}, status: api.ContainerStatus{ Name: "test", Ready: true, RestartCount: 7, }, expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz"}, }, // Using limits. { container: api.Container{ Name: "test", Image: "image", Resources: api.ResourceRequirements{ Limits: api.ResourceList{ api.ResourceName(api.ResourceCPU): resource.MustParse("1000"), api.ResourceName(api.ResourceMemory): resource.MustParse("4G"), api.ResourceName(api.ResourceStorage): resource.MustParse("20G"), }, }, }, status: api.ContainerStatus{ Name: "test", Ready: true, RestartCount: 7, }, expectedElements: []string{"cpu", "1k", "memory", "4G", "storage", "20G"}, }, } for i, testCase := range testCases { out := new(bytes.Buffer) pod := api.Pod{ Spec: api.PodSpec{ Containers: []api.Container{testCase.container}, }, Status: api.PodStatus{ ContainerStatuses: []api.ContainerStatus{testCase.status}, }, } describeContainers(&pod, out) output := out.String() for _, expected := range testCase.expectedElements { if !strings.Contains(output, expected) { t.Errorf("Test case %d: expected to find %q in output: %q", i, expected, output) } } } }