func TestDCRCSpecNode(t *testing.T) { g := osgraph.New() dc := &deployapi.DeploymentConfig{} dc.Namespace = "ns" dc.Name = "foo" dcNode := EnsureDeploymentConfigNode(g, dc) if len(g.NodeList()) != 2 { t.Errorf("expected 2 nodes, got %v", g.NodeList()) } if len(g.EdgeList()) != 1 { t.Errorf("expected 2 edge, got %v", g.EdgeList()) } edge := g.EdgeList()[0] if !g.EdgeKinds(edge).Has(osgraph.ContainsEdgeKind) { t.Errorf("expected %v, got %v", osgraph.ContainsEdgeKind, g.EdgeKinds(edge)) } if edge.Head().ID() != dcNode.ID() { t.Errorf("expected %v, got %v", dcNode.ID(), edge.Head()) } }
func TestPodSpecNode(t *testing.T) { g := osgraph.New() pod := &kapi.Pod{} pod.Namespace = "ns" pod.Name = "foo" pod.Spec.NodeName = "any-host" podNode := EnsurePodNode(g, pod) if len(g.Nodes()) != 2 { t.Errorf("expected 2 nodes, got %v", g.Nodes()) } if len(g.Edges()) != 1 { t.Errorf("expected 1 edge, got %v", g.Edges()) } edge := g.Edges()[0] if !g.EdgeKinds(edge).Has(osgraph.ContainsEdgeKind) { t.Errorf("expected %v, got %v", osgraph.ContainsEdgeKind, g.EdgeKinds(edge)) } if edge.From().ID() != podNode.ID() { t.Errorf("expected %v, got %v", podNode.ID(), edge.From()) } }
// MakeGraph will create the graph of all build configurations and the image streams // they point to via image change triggers in the provided namespace(s) func (d *ChainDescriber) MakeGraph() (osgraph.Graph, error) { g := osgraph.New() loaders := []GraphLoader{} for namespace := range d.namespaces { glog.V(4).Infof("Loading build configurations from %q", namespace) loaders = append(loaders, &bcLoader{namespace: namespace, lister: d.c}) } loadingFuncs := []func() error{} for _, loader := range loaders { loadingFuncs = append(loadingFuncs, loader.Load) } if errs := parallel.Run(loadingFuncs...); len(errs) > 0 { return g, utilerrors.NewAggregate(errs) } for _, loader := range loaders { loader.AddToGraph(g) } buildedges.AddAllInputOutputEdges(g) return g, nil }
func TestHPADCEdges(t *testing.T) { hpa := &autoscaling.HorizontalPodAutoscaler{} hpa.Namespace = "test-ns" hpa.Name = "test-hpa" hpa.Spec = autoscaling.HorizontalPodAutoscalerSpec{ ScaleTargetRef: autoscaling.CrossVersionObjectReference{ Name: "test-dc", Kind: "DeploymentConfig", }, } dc := &deployapi.DeploymentConfig{} dc.Name = "test-dc" dc.Namespace = "test-ns" g := osgraph.New() hpaNode := kubegraph.EnsureHorizontalPodAutoscalerNode(g, hpa) dcNode := deploygraph.EnsureDeploymentConfigNode(g, dc) AddHPAScaleRefEdges(g) if edge := g.Edge(hpaNode, dcNode); edge == nil { t.Fatalf("edge between HPA and DC missing") } else { if !g.EdgeKinds(edge).Has(ScalingEdgeKind) { t.Errorf("expected edge to have kind %v, got %v", ScalingEdgeKind, edge) } } }
/* NewImageRegistryPruner creates a new ImageRegistryPruner. Images younger than keepYoungerThan and images referenced by image streams and/or pods younger than keepYoungerThan are preserved. All other images are candidates for pruning. For example, if keepYoungerThan is 60m, and an ImageStream is only 59 minutes old, none of the images it references are eligible for pruning. keepTagRevisions is the number of revisions per tag in an image stream's status.tags that are preserved and ineligible for pruning. Any revision older than keepTagRevisions is eligible for pruning. images, streams, pods, rcs, bcs, builds, and dcs are the resources used to run the pruning algorithm. These should be the full list for each type from the cluster; otherwise, the pruning algorithm might result in incorrect calculations and premature pruning. The ImagePruner performs the following logic: remove any image containing the annotation openshift.io/image.managed=true that was created at least *n* minutes ago and is *not* currently referenced by: - any pod created less than *n* minutes ago - any image stream created less than *n* minutes ago - any running pods - any pending pods - any replication controllers - any deployment configs - any build configs - any builds - the n most recent tag revisions in an image stream's status.tags When removing an image, remove all references to the image from all ImageStreams having a reference to the image in `status.tags`. Also automatically remove any image layer that is no longer referenced by any images. */ func NewImageRegistryPruner(options ImageRegistryPrunerOptions) ImageRegistryPruner { g := graph.New() glog.V(1).Infof("Creating image pruner with keepYoungerThan=%v, keepTagRevisions=%d", options.KeepYoungerThan, options.KeepTagRevisions) algorithm := pruneAlgorithm{ keepYoungerThan: options.KeepYoungerThan, keepTagRevisions: options.KeepTagRevisions, } addImagesToGraph(g, options.Images, algorithm) addImageStreamsToGraph(g, options.Streams, algorithm) addPodsToGraph(g, options.Pods, algorithm) addReplicationControllersToGraph(g, options.RCs) addBuildConfigsToGraph(g, options.BCs) addBuildsToGraph(g, options.Builds) addDeploymentConfigsToGraph(g, options.DCs) var rp registryPinger if options.DryRun { rp = &dryRunRegistryPinger{} } else { rp = &defaultRegistryPinger{options.RegistryClient} } return &imageRegistryPruner{ g: g, algorithm: algorithm, registryPinger: rp, registryClient: options.RegistryClient, registryURL: options.RegistryURL, } }
// imagesTop generates Image information from a graph and returns this as a list // of imageInfo array. func (o TopImagesOptions) imagesTop() []Info { g := graph.New() addImagesToGraph(g, o.Images) addImageStreamsToGraph(g, o.Streams) addPodsToGraph(g, o.Pods) markParentsInGraph(g) infos := []Info{} imageNodes := getImageNodes(g.Nodes()) for _, in := range imageNodes { image := in.Image istags := getImageStreamTags(g, in) parents := getImageParents(g, in) usage := getImageUsage(g, in) metadata := len(image.DockerImageManifest) != 0 && len(image.DockerImageLayers) != 0 storage := getStorage(image) infos = append(infos, imageInfo{ Image: image.Name, ImageStreamTags: istags, Parents: parents, Usage: usage, Metadata: metadata, Storage: storage, }) } return infos }
func TestHPARCEdges(t *testing.T) { hpa := &autoscaling.HorizontalPodAutoscaler{} hpa.Namespace = "test-ns" hpa.Name = "test-hpa" hpa.Spec = autoscaling.HorizontalPodAutoscalerSpec{ ScaleTargetRef: autoscaling.CrossVersionObjectReference{ Name: "test-rc", Kind: "ReplicationController", }, } rc := &kapi.ReplicationController{} rc.Name = "test-rc" rc.Namespace = "test-ns" g := osgraph.New() hpaNode := kubegraph.EnsureHorizontalPodAutoscalerNode(g, hpa) rcNode := kubegraph.EnsureReplicationControllerNode(g, rc) AddHPAScaleRefEdges(g) if edge := g.Edge(hpaNode, rcNode); edge == nil { t.Fatalf("edge between HPA and RC missing") } else { if !g.EdgeKinds(edge).Has(ScalingEdgeKind) { t.Errorf("expected edge to have kind %v, got %v", ScalingEdgeKind, edge) } } }
func (d *ProjectStatusDescriber) MakeGraph(namespace string) (osgraph.Graph, sets.String, error) { g := osgraph.New() loaders := []GraphLoader{ &serviceLoader{namespace: namespace, lister: d.K}, &serviceAccountLoader{namespace: namespace, lister: d.K}, &secretLoader{namespace: namespace, lister: d.K}, &rcLoader{namespace: namespace, lister: d.K}, &podLoader{namespace: namespace, lister: d.K}, // TODO check swagger for feature enablement and selectively add bcLoader and buildLoader // then remove errors.TolerateNotFoundError method. &bcLoader{namespace: namespace, lister: d.C}, &buildLoader{namespace: namespace, lister: d.C}, &isLoader{namespace: namespace, lister: d.C}, &dcLoader{namespace: namespace, lister: d.C}, &routeLoader{namespace: namespace, lister: d.C}, } loadingFuncs := []func() error{} for _, loader := range loaders { loadingFuncs = append(loadingFuncs, loader.Load) } forbiddenResources := sets.String{} if errs := parallel.Run(loadingFuncs...); len(errs) > 0 { actualErrors := []error{} for _, err := range errs { if kapierrors.IsForbidden(err) { forbiddenErr := err.(*kapierrors.StatusError) if (forbiddenErr.Status().Details != nil) && (len(forbiddenErr.Status().Details.Kind) > 0) { forbiddenResources.Insert(forbiddenErr.Status().Details.Kind) } continue } actualErrors = append(actualErrors, err) } if len(actualErrors) > 0 { return g, forbiddenResources, utilerrors.NewAggregate(actualErrors) } } for _, loader := range loaders { loader.AddToGraph(g) } kubeedges.AddAllExposedPodTemplateSpecEdges(g) kubeedges.AddAllExposedPodEdges(g) kubeedges.AddAllManagedByRCPodEdges(g) kubeedges.AddAllRequestedServiceAccountEdges(g) kubeedges.AddAllMountableSecretEdges(g) kubeedges.AddAllMountedSecretEdges(g) buildedges.AddAllInputOutputEdges(g) buildedges.AddAllBuildEdges(g) deployedges.AddAllTriggerEdges(g) deployedges.AddAllDeploymentEdges(g) imageedges.AddAllImageStreamRefEdges(g) routeedges.AddAllRouteEdges(g) return g, forbiddenResources, nil }
func (d *ProjectStatusDescriber) MakeGraph(namespace string) (osgraph.Graph, error) { g := osgraph.New() svcs, err := d.K.Services(namespace).List(labels.Everything()) if err != nil { return g, err } iss, err := d.C.ImageStreams(namespace).List(labels.Everything(), fields.Everything()) if err != nil { return g, err } bcs, err := d.C.BuildConfigs(namespace).List(labels.Everything(), fields.Everything()) if err != nil { return g, err } dcs, err := d.C.DeploymentConfigs(namespace).List(labels.Everything(), fields.Everything()) if err != nil { return g, err } builds := &buildapi.BuildList{} if len(bcs.Items) > 0 { if b, err := d.C.Builds(namespace).List(labels.Everything(), fields.Everything()); err == nil { builds = b } } rcs, err := d.K.ReplicationControllers(namespace).List(labels.Everything()) if err != nil { rcs = &kapi.ReplicationControllerList{} } for i := range iss.Items { imagegraph.EnsureImageStreamNode(g, &iss.Items[i]) imagegraph.EnsureAllImageStreamTagNodes(g, &iss.Items[i]) } for i := range bcs.Items { build := buildgraph.EnsureBuildConfigNode(g, &bcs.Items[i]) buildedges.AddInputOutputEdges(g, build) buildedges.JoinBuilds(build, builds.Items) } for i := range dcs.Items { deploy := deploygraph.EnsureDeploymentConfigNode(g, &dcs.Items[i]) deployedges.AddTriggerEdges(g, deploy) deployedges.JoinDeployments(deploy, rcs.Items) } for i := range svcs.Items { service := kubegraph.EnsureServiceNode(g, &svcs.Items[i]) kubeedges.AddExposedPodTemplateSpecEdges(g, service) } imageedges.AddAllImageStreamRefEdges(g) return g, nil }
func TestReplicationControllerSpecNode(t *testing.T) { g := osgraph.New() rc := &kapi.ReplicationController{} rc.Namespace = "ns" rc.Name = "foo" rc.Spec.Template = &kapi.PodTemplateSpec{} rcNode := EnsureReplicationControllerNode(g, rc) if len(g.Nodes()) != 4 { t.Errorf("expected 4 nodes, got %v", g.Nodes()) } if len(g.Edges()) != 3 { t.Errorf("expected 3 edge, got %v", g.Edges()) } rcEdges := g.OutboundEdges(rcNode) if len(rcEdges) != 1 { t.Fatalf("expected 1 edge, got %v for \n%v", rcEdges, g) } if !g.EdgeKinds(rcEdges[0]).Has(osgraph.ContainsEdgeKind) { t.Errorf("expected %v, got %v", osgraph.ContainsEdgeKind, rcEdges[0]) } uncastRCSpec := rcEdges[0].To() rcSpec, ok := uncastRCSpec.(*ReplicationControllerSpecNode) if !ok { t.Fatalf("expected rcSpec, got %v", uncastRCSpec) } rcSpecEdges := g.OutboundEdges(rcSpec) if len(rcSpecEdges) != 1 { t.Fatalf("expected 1 edge, got %v", rcSpecEdges) } if !g.EdgeKinds(rcSpecEdges[0]).Has(osgraph.ContainsEdgeKind) { t.Errorf("expected %v, got %v", osgraph.ContainsEdgeKind, rcSpecEdges[0]) } uncastPTSpec := rcSpecEdges[0].To() ptSpec, ok := uncastPTSpec.(*PodTemplateSpecNode) if !ok { t.Fatalf("expected ptspec, got %v", uncastPTSpec) } ptSpecEdges := g.OutboundEdges(ptSpec) if len(ptSpecEdges) != 1 { t.Fatalf("expected 1 edge, got %v", ptSpecEdges) } if !g.EdgeKinds(ptSpecEdges[0]).Has(osgraph.ContainsEdgeKind) { t.Errorf("expected %v, got %v", osgraph.ContainsEdgeKind, ptSpecEdges[0]) } }
// NewPruner creates a Pruner. // // Images younger than keepYoungerThan and images referenced by image streams // and/or pods younger than keepYoungerThan are preserved. All other images are // candidates for pruning. For example, if keepYoungerThan is 60m, and an // ImageStream is only 59 minutes old, none of the images it references are // eligible for pruning. // // keepTagRevisions is the number of revisions per tag in an image stream's // status.tags that are preserved and ineligible for pruning. Any revision older // than keepTagRevisions is eligible for pruning. // // pruneOverSizeLimit is a boolean flag speyfing that all images exceeding limits // defined in their namespace will be considered for pruning. Important to note is // the fact that this flag does not work in any combination with the keep* flags. // // images, streams, pods, rcs, bcs, builds, and dcs are the resources used to run // the pruning algorithm. These should be the full list for each type from the // cluster; otherwise, the pruning algorithm might result in incorrect // calculations and premature pruning. // // The ImageDeleter performs the following logic: // // remove any image that was created at least *n* minutes ago and is *not* // currently referenced by: // // - any pod created less than *n* minutes ago // - any image stream created less than *n* minutes ago // - any running pods // - any pending pods // - any replication controllers // - any deployment configs // - any build configs // - any builds // - the n most recent tag revisions in an image stream's status.tags // // including only images with the annotation openshift.io/image.managed=true // unless allImages is true. // // When removing an image, remove all references to the image from all // ImageStreams having a reference to the image in `status.tags`. // // Also automatically remove any image layer that is no longer referenced by any // images. func NewPruner(options PrunerOptions) Pruner { keepTagRevisions := "<nil>" if options.KeepTagRevisions != nil { keepTagRevisions = fmt.Sprintf("%d", *options.KeepTagRevisions) } pruneOverSizeLimit := "<nil>" if options.PruneOverSizeLimit != nil { pruneOverSizeLimit = fmt.Sprintf("%v", *options.PruneOverSizeLimit) } glog.V(1).Infof("Creating image pruner with keepYoungerThan=%v, keepTagRevisions=%s, pruneOverSizeLimit=%s, allImages=%t", options.KeepYoungerThan, keepTagRevisions, pruneOverSizeLimit, options.AllImages) algorithm := pruneAlgorithm{} if options.KeepYoungerThan != nil { algorithm.keepYoungerThan = *options.KeepYoungerThan } if options.KeepTagRevisions != nil { algorithm.keepTagRevisions = *options.KeepTagRevisions } if options.PruneOverSizeLimit != nil { algorithm.pruneOverSizeLimit = *options.PruneOverSizeLimit } if options.AllImages != nil { algorithm.allImages = *options.AllImages } algorithm.namespace = options.Namespace g := graph.New() addImagesToGraph(g, options.Images, algorithm) addImageStreamsToGraph(g, options.Streams, options.LimitRanges, algorithm) addPodsToGraph(g, options.Pods, algorithm) addReplicationControllersToGraph(g, options.RCs) addBuildConfigsToGraph(g, options.BCs) addBuildsToGraph(g, options.Builds) addDeploymentConfigsToGraph(g, options.DCs) var rp registryPinger if options.DryRun { rp = &dryRunRegistryPinger{} } else { rp = &defaultRegistryPinger{options.RegistryClient} } return &pruner{ g: g, algorithm: algorithm, registryPinger: rp, registryClient: options.RegistryClient, registryURL: options.RegistryURL, } }
func TestNamespaceEdgeMatching(t *testing.T) { g := osgraph.New() fn := func(namespace string, g osgraph.Interface) { pod := &kapi.Pod{} pod.Namespace = namespace pod.Name = "the-pod" pod.Labels = map[string]string{"a": "1"} kubegraph.EnsurePodNode(g, pod) rc := &kapi.ReplicationController{} rc.Namespace = namespace rc.Name = "the-rc" rc.Spec.Selector = map[string]string{"a": "1"} kubegraph.EnsureReplicationControllerNode(g, rc) p := &kapps.PetSet{} p.Namespace = namespace p.Name = "the-petset" p.Spec.Selector = &unversioned.LabelSelector{ MatchLabels: map[string]string{"a": "1"}, } kubegraph.EnsurePetSetNode(g, p) svc := &kapi.Service{} svc.Namespace = namespace svc.Name = "the-svc" svc.Spec.Selector = map[string]string{"a": "1"} kubegraph.EnsureServiceNode(g, svc) } fn("ns", g) fn("other", g) AddAllExposedPodEdges(g) AddAllExposedPodTemplateSpecEdges(g) AddAllManagedByControllerPodEdges(g) for _, edge := range g.Edges() { nsTo, err := namespaceFor(edge.To()) if err != nil { t.Fatal(err) } nsFrom, err := namespaceFor(edge.From()) if err != nil { t.Fatal(err) } if nsFrom != nsTo { t.Errorf("edge %#v crosses namespace: %s %s", edge, nsFrom, nsTo) } } }
func TestDCPodTemplateSpecNode(t *testing.T) { g := osgraph.New() dc := &deployapi.DeploymentConfig{} dc.Namespace = "ns" dc.Name = "foo" dc.Spec.Template = test.OkPodTemplate() _ = EnsureDeploymentConfigNode(g, dc) edges := g.Edges() if len(edges) != 2 { t.Errorf("expected 2 edges, got %d", len(edges)) return } for i := range edges { if !g.EdgeKinds(edges[i]).Has(osgraph.ContainsEdgeKind) { t.Errorf("expected %v, got %v", osgraph.ContainsEdgeKind, g.EdgeKinds(edges[i])) return } } nodes := g.Nodes() if len(nodes) != 3 { t.Errorf("expected 3 nodes, got %d", len(nodes)) return } sorted, err := topo.Sort(g) if err != nil { t.Errorf("unexpected error: %v", err) return } // Just to be sure if len(sorted) != 3 { t.Errorf("expected 3 nodes, got %d", len(sorted)) return } if _, ok := sorted[0].(*DeploymentConfigNode); !ok { t.Errorf("expected first node to be a DeploymentConfigNode") return } if _, ok := sorted[1].(*kubetypes.PodTemplateSpecNode); !ok { t.Errorf("expected second node to be a PodTemplateSpecNode") return } if _, ok := sorted[2].(*kubetypes.PodSpecNode); !ok { t.Errorf("expected third node to be a PodSpecNode") } }
// Describe returns the description of the latest deployments for a config func (d *LatestDeploymentsDescriber) Describe(namespace, name string) (string, error) { var f formatter config, err := d.client.getDeploymentConfig(namespace, name) if err != nil { return "", err } var deployments []kapi.ReplicationController if d.count == -1 || d.count > 1 { list, err := d.client.listDeployments(namespace, labels.Everything()) if err != nil && !kerrors.IsNotFound(err) { return "", err } deployments = list.Items } else { deploymentName := deployutil.LatestDeploymentNameForConfig(config) deployment, err := d.client.getDeployment(config.Namespace, deploymentName) if err != nil && !kerrors.IsNotFound(err) { return "", err } if deployment != nil { deployments = []kapi.ReplicationController{*deployment} } } g := graph.New() dcNode := deploygraph.EnsureDeploymentConfigNode(g, config) for i := range deployments { kubegraph.EnsureReplicationControllerNode(g, &deployments[i]) } deployedges.AddTriggerEdges(g, dcNode) deployedges.AddDeploymentEdges(g, dcNode) activeDeployment, inactiveDeployments := deployedges.RelevantDeployments(g, dcNode) return tabbedString(func(out *tabwriter.Writer) error { descriptions := describeDeployments(f, dcNode, activeDeployment, inactiveDeployments, d.count) for i, description := range descriptions { descriptions[i] = fmt.Sprintf("%v %v", name, description) } printLines(out, "", 0, descriptions...) return nil }) }
func TestSecretEdges(t *testing.T) { sa := &kapi.ServiceAccount{} sa.Namespace = "ns" sa.Name = "shultz" sa.Secrets = []kapi.ObjectReference{{Name: "i-know-nothing"}, {Name: "missing"}} secret1 := &kapi.Secret{} secret1.Namespace = "ns" secret1.Name = "i-know-nothing" pod := &kapi.Pod{} pod.Namespace = "ns" pod.Name = "the-pod" pod.Spec.Volumes = []kapi.Volume{{Name: "rose", VolumeSource: kapi.VolumeSource{Secret: &kapi.SecretVolumeSource{SecretName: "i-know-nothing"}}}} g := osgraph.New() saNode := kubegraph.EnsureServiceAccountNode(g, sa) secretNode := kubegraph.EnsureSecretNode(g, secret1) podNode := kubegraph.EnsurePodNode(g, pod) AddAllMountableSecretEdges(g) AddAllMountedSecretEdges(g) if edge := g.Edge(saNode, secretNode); edge == nil { t.Errorf("edge missing") } else { if !g.EdgeKinds(edge).Has(MountableSecretEdgeKind) { t.Errorf("expected %v, got %v", MountableSecretEdgeKind, edge) } } podSpecNodes := g.SuccessorNodesByNodeAndEdgeKind(podNode, kubegraph.PodSpecNodeKind, osgraph.ContainsEdgeKind) if len(podSpecNodes) != 1 { t.Fatalf("wrong number of podspecs: %v", podSpecNodes) } if edge := g.Edge(podSpecNodes[0], secretNode); edge == nil { t.Errorf("edge missing") } else { if !g.EdgeKinds(edge).Has(MountedSecretEdgeKind) { t.Errorf("expected %v, got %v", MountedSecretEdgeKind, edge) } } }
// imageStreamsTop generates ImageStream information from a graph and // returns this as a list of imageStreamInfo array. func (o TopImageStreamsOptions) imageStreamsTop() []Info { g := graph.New() addImagesToGraph(g, o.Images) addImageStreamsToGraph(g, o.Streams) infos := []Info{} streamNodes := getImageStreamNodes(g.Nodes()) for _, sn := range streamNodes { storage, images, layers := getImageStreamSize(g, sn) infos = append(infos, imageStreamInfo{ ImageStream: fmt.Sprintf("%s/%s", sn.ImageStream.Namespace, sn.ImageStream.Name), Storage: storage, Images: images, Layers: layers, }) } return infos }
func (d *ProjectStatusDescriber) MakeGraph(namespace string) (osgraph.Graph, error) { g := osgraph.New() loaders := []GraphLoader{ &serviceLoader{namespace: namespace, lister: d.K}, &serviceAccountLoader{namespace: namespace, lister: d.K}, &secretLoader{namespace: namespace, lister: d.K}, &rcLoader{namespace: namespace, lister: d.K}, &podLoader{namespace: namespace, lister: d.K}, &bcLoader{namespace: namespace, lister: d.C}, &buildLoader{namespace: namespace, lister: d.C}, &isLoader{namespace: namespace, lister: d.C}, &dcLoader{namespace: namespace, lister: d.C}, } loadingFuncs := []func() error{} for _, loader := range loaders { loadingFuncs = append(loadingFuncs, loader.Load) } if errs := parallel.Run(loadingFuncs...); len(errs) > 0 { return g, utilerrors.NewAggregate(errs) } for _, loader := range loaders { loader.AddToGraph(g) } kubeedges.AddAllExposedPodTemplateSpecEdges(g) kubeedges.AddAllExposedPodEdges(g) kubeedges.AddAllManagedByRCPodEdges(g) kubeedges.AddAllRequestedServiceAccountEdges(g) kubeedges.AddAllMountableSecretEdges(g) kubeedges.AddAllMountedSecretEdges(g) buildedges.AddAllInputOutputEdges(g) buildedges.AddAllBuildEdges(g) deployedges.AddAllTriggerEdges(g) deployedges.AddAllDeploymentEdges(g) imageedges.AddAllImageStreamRefEdges(g) return g, nil }
// DescendentNodesByNodeKind starts at the root navigates down the root. Every edge is checked against the edgeChecker // to determine whether or not to follow it. The nodes at the tail end of every chased edge are then checked against the // the targetNodeKind. Matches are added to the return and every checked node then has its edges checked: lather, rinse, repeat func DescendentNodesByNodeKind(g osgraph.Graph, visitedNodes graphview.IntSet, node graph.Node, targetNodeKind string, edgeChecker osgraph.EdgeFunc) []graph.Node { if visitedNodes.Has(node.ID()) { return []graph.Node{} } visitedNodes.Insert(node.ID()) ret := []graph.Node{} for _, successor := range g.From(node) { edge := g.Edge(node, successor) if edgeChecker(osgraph.New(), node, successor, g.EdgeKinds(edge)) { if g.Kind(successor) == targetNodeKind { ret = append(ret, successor) } ret = append(ret, DescendentNodesByNodeKind(g, visitedNodes, successor, targetNodeKind, edgeChecker)...) } } return ret }
func (d *ProjectStatusDescriber) MakeGraph(namespace string) (osgraph.Graph, error) { g := osgraph.New() loadingFuncs := []GraphLoadingFunc{loadServices, loadBuildConfigs, loadImageStreams, loadDeploymentConfigs, loadBuilds, loadReplicationControllers} listingWaitGroup := sync.WaitGroup{} graphLock := sync.Mutex{} errorChannel := make(chan error, len(loadingFuncs)) for _, loadingFunc := range loadingFuncs { listingWaitGroup.Add(1) go func(loadingFunc GraphLoadingFunc) { defer listingWaitGroup.Done() if err := loadingFunc(g, graphLock, namespace, d.K, d.C); err != nil { errorChannel <- err } }(loadingFunc) } listingWaitGroup.Wait() close(errorChannel) // if we had an error. Aggregate them and return them errlist := []error{} for err := range errorChannel { errlist = append(errlist, err) } if len(errlist) > 0 { return g, utilerrors.NewAggregate(errlist) } kubeedges.AddAllExposedPodTemplateSpecEdges(g) buildedges.AddAllInputOutputEdges(g) buildedges.AddAllBuildEdges(g) deployedges.AddAllTriggerEdges(g) deployedges.AddAllDeploymentEdges(g) imageedges.AddAllImageStreamRefEdges(g) return g, nil }
// Describe returns the description of the latest deployments for a config func (d *LatestDeploymentsDescriber) Describe(namespace, name string) (string, error) { config, err := d.client.getDeploymentConfig(namespace, name) if err != nil { return "", err } var deployments []kapi.ReplicationController if d.count == -1 || d.count > 1 { list, err := d.client.listDeployments(namespace, labels.Everything()) if err != nil && !kerrors.IsNotFound(err) { return "", err } deployments = list.Items } else { deploymentName := deployutil.LatestDeploymentNameForConfig(config) deployment, err := d.client.getDeployment(config.Namespace, deploymentName) if err != nil && !kerrors.IsNotFound(err) { return "", err } if deployment != nil { deployments = []kapi.ReplicationController{*deployment} } } g := graph.New() deploy := graph.DeploymentConfig(g, config) if len(deployments) > 0 { graph.JoinDeployments(deploy.(*graph.DeploymentConfigNode), deployments) } return tabbedString(func(out *tabwriter.Writer) error { node := deploy.(*graph.DeploymentConfigNode) descriptions := describeDeployments(node, d.count) for i, description := range descriptions { descriptions[i] = fmt.Sprintf("%v %v", name, description) } printLines(out, "", 0, descriptions...) return nil }) }
func TestNamespaceEdgeMatching(t *testing.T) { g := osgraph.New() fn := func(namespace string, g osgraph.Interface) { bc := &api.BuildConfig{} bc.Namespace = namespace bc.Name = "the-bc" nodes.EnsureBuildConfigNode(g, bc) b := &api.Build{} b.Namespace = namespace b.Name = "the-build" b.Labels = map[string]string{api.BuildConfigLabel: "the-bc"} b.Annotations = map[string]string{api.BuildConfigAnnotation: "the-bc"} nodes.EnsureBuildNode(g, b) } fn("ns", g) fn("other", g) AddAllBuildEdges(g) if len(g.Edges()) != 2 { t.Fatal(g) } for _, edge := range g.Edges() { nsTo, err := namespaceFor(edge.To()) if err != nil { t.Fatal(err) } nsFrom, err := namespaceFor(edge.From()) if err != nil { t.Fatal(err) } if nsFrom != nsTo { t.Errorf("edge %#v crosses namespace: %s %s", edge, nsFrom, nsTo) } } }
func BuildGraph(path string) (osgraph.Graph, []runtime.Object, error) { g := osgraph.New() objs := []runtime.Object{} abspath, err := filepath.Abs(path) if err != nil { return g, objs, err } mapper := latest.RESTMapper typer := kapi.Scheme clientMapper := resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) { return nil, nil }) r := resource.NewBuilder(mapper, typer, clientMapper). FilenameParam(abspath). Flatten(). Do() if r.Err() != nil { return g, objs, r.Err() } infos, err := r.Infos() if err != nil { return g, objs, err } for _, info := range infos { objs = append(objs, info.Object) if err := EnsureNode(g, info.Object); err != nil { return g, objs, err } } return g, objs, nil }
func BuildGraph(path string) (osgraph.Graph, []runtime.Object, error) { g := osgraph.New() objs := []runtime.Object{} abspath, err := filepath.Abs(path) if err != nil { return g, objs, err } mapper := registered.RESTMapper() typer := kapi.Scheme clientMapper := resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) { return nil, nil }) r := resource.NewBuilder(mapper, typer, clientMapper, kapi.Codecs.UniversalDecoder()). FilenameParam(false, &resource.FilenameOptions{Recursive: false, Filenames: []string{abspath}}). Flatten(). Do() if r.Err() != nil { return g, objs, r.Err() } infos, err := r.Infos() if err != nil { return g, objs, err } for _, info := range infos { objs = append(objs, info.Object) if err := EnsureNode(g, info.Object); err != nil { return g, objs, err } } return g, objs, nil }
func TestNamespaceEdgeMatching(t *testing.T) { g := osgraph.New() fn := func(namespace string, g osgraph.Interface) { dc := &api.DeploymentConfig{} dc.Namespace = namespace dc.Name = "the-dc" dc.Spec.Selector = map[string]string{"a": "1"} nodes.EnsureDeploymentConfigNode(g, dc) rc := &kapi.ReplicationController{} rc.Namespace = namespace rc.Name = "the-rc" rc.Annotations = map[string]string{api.DeploymentConfigAnnotation: "the-dc"} kubegraph.EnsureReplicationControllerNode(g, rc) } fn("ns", g) fn("other", g) AddAllDeploymentEdges(g) if len(g.Edges()) != 4 { t.Fatal(g) } for _, edge := range g.Edges() { nsTo, err := namespaceFor(edge.To()) if err != nil { t.Fatal(err) } nsFrom, err := namespaceFor(edge.From()) if err != nil { t.Fatal(err) } if nsFrom != nsTo { t.Errorf("edge %#v crosses namespace: %s %s", edge, nsFrom, nsTo) } } }
/* NewImagePruner creates a new ImagePruner. Images younger than keepYoungerThan and images referenced by image streams and/or pods younger than keepYoungerThan are preserved. All other images are candidates for pruning. For example, if keepYoungerThan is 60m, and an ImageStream is only 59 minutes old, none of the images it references are eligible for pruning. tagRevisionsToKeep is the number of revisions per tag in an image stream's status.tags that are preserved and ineligible for pruning. Any revision older than tagRevisionsToKeep is eligible for pruning. images, streams, pods, rcs, bcs, builds, and dcs are the resources used to run the pruning algorithm. These should be the full list for each type from the cluster; otherwise, the pruning algorithm might result in incorrect calculations and premature pruning. The ImagePruner performs the following logic: remove any image contaning the annotation openshift.io/image.managed=true that was created at least *n* minutes ago and is *not* currently referenced by: - any pod created less than *n* minutes ago - any image stream created less than *n* minutes ago - any running pods - any pending pods - any replication controllers - any deployment configs - any build configs - any builds - the n most recent tag revisions in an image stream's status.tags When removing an image, remove all references to the image from all ImageStreams having a reference to the image in `status.tags`. Also automatically remove any image layer that is no longer referenced by any images. */ func NewImagePruner(keepYoungerThan time.Duration, tagRevisionsToKeep int, images *imageapi.ImageList, streams *imageapi.ImageStreamList, pods *kapi.PodList, rcs *kapi.ReplicationControllerList, bcs *buildapi.BuildConfigList, builds *buildapi.BuildList, dcs *deployapi.DeploymentConfigList) ImagePruner { g := graph.New() glog.V(1).Infof("Creating image pruner with keepYoungerThan=%v, tagRevisionsToKeep=%d", keepYoungerThan, tagRevisionsToKeep) algorithm := pruneAlgorithm{ keepYoungerThan: keepYoungerThan, tagRevisionsToKeep: tagRevisionsToKeep, } addImagesToGraph(g, images, algorithm) addImageStreamsToGraph(g, streams, algorithm) addPodsToGraph(g, pods, algorithm) addReplicationControllersToGraph(g, rcs) addBuildConfigsToGraph(g, bcs) addBuildsToGraph(g, builds) addDeploymentConfigsToGraph(g, dcs) return &imagePruner{ g: g, algorithm: algorithm, } }
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()) } } } }
// Describe returns the description of a project func (d *ProjectStatusDescriber) Describe(namespace, name string) (string, error) { project, err := d.C.Projects().Get(namespace) if err != nil { return "", err } svcs, err := d.K.Services(namespace).List(labels.Everything()) if err != nil { return "", err } bcs, err := d.C.BuildConfigs(namespace).List(labels.Everything(), fields.Everything()) if err != nil { return "", err } dcs, err := d.C.DeploymentConfigs(namespace).List(labels.Everything(), fields.Everything()) if err != nil { return "", err } builds := &buildapi.BuildList{} if len(bcs.Items) > 0 { if b, err := d.C.Builds(namespace).List(labels.Everything(), fields.Everything()); err == nil { builds = b } } rcs, err := d.K.ReplicationControllers(namespace).List(labels.Everything()) if err != nil { rcs = &kapi.ReplicationControllerList{} } g := graph.New() for i := range bcs.Items { build := buildgraph.EnsureBuildConfigNode(g, &bcs.Items[i]) buildedges.AddInputOutputEdges(g, build) buildedges.JoinBuilds(build, builds.Items) } for i := range dcs.Items { deploy := deploygraph.EnsureDeploymentConfigNode(g, &dcs.Items[i]) deployedges.AddTriggerEdges(g, deploy) deployedges.JoinDeployments(deploy, rcs.Items) } for i := range svcs.Items { service := kubegraph.EnsureServiceNode(g, &svcs.Items[i]) kubeedges.AddExposedPodTemplateSpecEdges(g, service) } groups := graphveneers.ServiceAndDeploymentGroups(g) return tabbedString(func(out *tabwriter.Writer) error { indent := " " fmt.Fprintf(out, "In project %s\n", projectapi.DisplayNameAndNameForProject(project)) for _, group := range groups { if len(group.Builds) != 0 { for _, build := range group.Builds { fmt.Fprintln(out) printLines(out, indent, 0, describeStandaloneBuildGroup(build, namespace)...) printLines(out, indent, 1, describeAdditionalBuildDetail(build.Build, true)...) } continue } if len(group.Services) == 0 { for _, deploy := range group.Deployments { fmt.Fprintln(out) printLines(out, indent, 0, describeDeploymentInServiceGroup(deploy)...) } continue } fmt.Fprintln(out) for _, svc := range group.Services { printLines(out, indent, 0, describeServiceInServiceGroup(svc)...) } for _, deploy := range group.Deployments { printLines(out, indent, 1, describeDeploymentInServiceGroup(deploy)...) } } if len(groups) == 0 { fmt.Fprintln(out, "\nYou have no Services, DeploymentConfigs, or BuildConfigs. 'oc new-app' can be used to create applications from scratch from existing Docker images and templates.") } else { fmt.Fprintln(out, "\nTo see more information about a Service or DeploymentConfig, use 'oc describe service <name>' or 'oc describe dc <name>'.") fmt.Fprintln(out, "You can use 'oc get all' to see lists of each of the types described above.") } return nil }) }
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.Edges() { if g.EdgeKinds(edge).Has(osgraph.UnknownEdgeKind) { t.Errorf("edge reported unknown kind: %#v", edge) } } // imagestreamtag default/other:base-image istID := 0 for _, node := range g.Nodes() { if g.Name(node) == "ImageStreamTag|default/other:base-image" { istID = node.ID() break } } edge := g.Edge(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.From(), edge.To()}, osgraph.ExistingDirectEdge).Edges()) != 1 { t.Fatalf("expected one edge") } if len(g.SubgraphWithNodes([]graph.Node{edge.To(), edge.From()}, osgraph.ExistingDirectEdge).Edges()) != 1 { t.Fatalf("expected one edge") } if e := g.Edge(concrete.Node(bcTestNode.ID()), concrete.Node(istID)); 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) } } } } } }