// NewReplicationController returns the ReplicationController and a set of all the NodeIDs covered by the ReplicationController func NewReplicationController(g osgraph.Graph, rcNode *kubegraph.ReplicationControllerNode) (ReplicationController, IntSet) { covered := IntSet{} covered.Insert(rcNode.ID()) rcView := ReplicationController{} rcView.RC = rcNode for _, uncastPodNode := range g.PredecessorNodesByEdgeKind(rcNode, kubeedges.ManagedByRCEdgeKind) { podNode := uncastPodNode.(*kubegraph.PodNode) covered.Insert(podNode.ID()) rcView.OwnedPods = append(rcView.OwnedPods, podNode) // check to see if this pod is managed by more than one RC uncastOwningRCs := g.SuccessorNodesByEdgeKind(podNode, kubeedges.ManagedByRCEdgeKind) if len(uncastOwningRCs) > 1 { for _, uncastOwningRC := range uncastOwningRCs { if uncastOwningRC.ID() == rcNode.ID() { continue } conflictingRC := uncastOwningRC.(*kubegraph.ReplicationControllerNode) rcView.ConflictingRCs = append(rcView.ConflictingRCs, conflictingRC) conflictingPods, ok := rcView.ConflictingRCIDToPods[conflictingRC.ID()] if !ok { conflictingPods = []*kubegraph.PodNode{} } conflictingPods = append(conflictingPods, podNode) rcView.ConflictingRCIDToPods[conflictingRC.ID()] = conflictingPods } } } return rcView, covered }
// NewServiceGroup returns the ServiceGroup and a set of all the NodeIDs covered by the service service func NewServiceGroup(g osgraph.Graph, serviceNode *kubegraph.ServiceNode) (ServiceGroup, IntSet) { covered := IntSet{} covered.Insert(serviceNode.ID()) service := ServiceGroup{} service.Service = serviceNode for _, uncastServiceFulfiller := range g.PredecessorNodesByEdgeKind(serviceNode, kubeedges.ExposedThroughServiceEdgeKind) { container := osgraph.GetTopLevelContainerNode(g, uncastServiceFulfiller) switch castContainer := container.(type) { case *deploygraph.DeploymentConfigNode: service.FulfillingDCs = append(service.FulfillingDCs, castContainer) case *kubegraph.ReplicationControllerNode: service.FulfillingRCs = append(service.FulfillingRCs, castContainer) case *kubegraph.PodNode: service.FulfillingPods = append(service.FulfillingPods, castContainer) default: util.HandleError(fmt.Errorf("unrecognized container: %v", castContainer)) } } // add the DCPipelines for all the DCs that fulfill the service for _, fulfillingDC := range service.FulfillingDCs { dcPipeline, dcCovers := NewDeploymentConfigPipeline(g, fulfillingDC) covered.Insert(dcCovers.List()...) service.DeploymentConfigPipelines = append(service.DeploymentConfigPipelines, dcPipeline) } return service, covered }
// NewImagePipelineFromImageTagLocation returns the ImagePipeline and all the nodes contributing to it func NewImagePipelineFromImageTagLocation(g osgraph.Graph, node graph.Node, imageTagLocation ImageTagLocation) (ImagePipeline, IntSet) { covered := IntSet{} covered.Insert(node.ID()) flow := ImagePipeline{} flow.Image = imageTagLocation for _, input := range g.PredecessorNodesByEdgeKind(node, buildedges.BuildOutputEdgeKind) { covered.Insert(input.ID()) build := input.(*buildgraph.BuildConfigNode) if flow.Build != nil { // report this as an error (unexpected duplicate input build) } if build.BuildConfig == nil { // report this as as a missing build / broken link break } base, src, coveredInputs, _ := findBuildInputs(g, build) covered.Insert(coveredInputs.List()...) flow.BaseImage = base flow.Source = src flow.Build = build flow.LastSuccessfulBuild, flow.LastUnsuccessfulBuild, flow.ActiveBuilds = buildedges.RelevantBuilds(g, flow.Build) } for _, input := range g.SuccessorNodesByEdgeKind(node, imageedges.ReferencedImageStreamGraphEdgeKind) { covered.Insert(input.ID()) imageStreamNode := input.(*imagegraph.ImageStreamNode) flow.DestinationResolved = (len(imageStreamNode.Status.DockerImageRepository) != 0) } return flow, covered }
// NewDeploymentConfigPipeline returns the DeploymentConfigPipeline and a set of all the NodeIDs covered by the DeploymentConfigPipeline func NewDeploymentConfigPipeline(g osgraph.Graph, dcNode *deploygraph.DeploymentConfigNode) (DeploymentConfigPipeline, IntSet) { covered := IntSet{} covered.Insert(dcNode.ID()) dcPipeline := DeploymentConfigPipeline{} dcPipeline.Deployment = dcNode // for everything that can trigger a deployment, create an image pipeline and add it to the list for _, istNode := range g.PredecessorNodesByEdgeKind(dcNode, deployedges.TriggersDeploymentEdgeKind) { imagePipeline, covers := NewImagePipelineFromImageTagLocation(g, istNode, istNode.(ImageTagLocation)) covered.Insert(covers.List()...) dcPipeline.Images = append(dcPipeline.Images, imagePipeline) } // for image that we use, create an image pipeline and add it to the list for _, tagNode := range g.PredecessorNodesByEdgeKind(dcNode, deployedges.UsedInDeploymentEdgeKind) { imagePipeline, covers := NewImagePipelineFromImageTagLocation(g, tagNode, tagNode.(ImageTagLocation)) covered.Insert(covers.List()...) dcPipeline.Images = append(dcPipeline.Images, imagePipeline) } dcPipeline.ActiveDeployment, dcPipeline.InactiveDeployments = deployedges.RelevantDeployments(g, dcNode) return dcPipeline, covered }
// BuildConfigsForTag returns the buildConfig that points to the provided imageStreamTag. func BuildConfigsForTag(g osgraph.Graph, istag graph.Node) []*buildgraph.BuildConfigNode { bcs := []*buildgraph.BuildConfigNode{} for _, bcNode := range g.PredecessorNodesByEdgeKind(istag, BuildOutputEdgeKind) { bcs = append(bcs, bcNode.(*buildgraph.BuildConfigNode)) } return bcs }
// NewImagePipelineFromImageTagLocation returns the ImagePipeline and all the nodes contributing to it func NewImagePipelineFromImageTagLocation(g osgraph.Graph, node graph.Node, imageTagLocation ImageTagLocation) (ImagePipeline, IntSet) { covered := IntSet{} covered.Insert(node.ID()) flow := ImagePipeline{} flow.Image = imageTagLocation for _, input := range g.PredecessorNodesByEdgeKind(node, buildedges.BuildOutputEdgeKind) { covered.Insert(input.ID()) build := input.(*buildgraph.BuildConfigNode) if flow.Build != nil { // report this as an error (unexpected duplicate input build) } if build.BuildConfig == nil { // report this as as a missing build / broken link break } base, src, coveredInputs, _ := findBuildInputs(g, build) covered.Insert(coveredInputs.List()...) flow.Build = build flow.BaseImage = base flow.Source = src } return flow, covered }
// FindDeploymentConfigTriggerErrors checks for possible failures in deployment config // image change triggers. // // Precedence of failures: // 1. The image stream of the tag of interest does not exist. // 2. The image stream tag does not exist but a build config points to it. // 3. The image stream tag does not exist. func FindDeploymentConfigTriggerErrors(g osgraph.Graph) []osgraph.Marker { markers := []osgraph.Marker{} dc: for _, uncastDcNode := range g.NodesByKind(deploygraph.DeploymentConfigNodeKind) { for _, uncastIstNode := range g.PredecessorNodesByEdgeKind(uncastDcNode, deployedges.TriggersDeploymentEdgeKind) { if istNode := uncastIstNode.(*imagegraph.ImageStreamTagNode); !istNode.Found() { dcNode := uncastDcNode.(*deploygraph.DeploymentConfigNode) // 1. Image stream for tag of interest does not exist if isNode, exists := doesImageStreamExist(g, uncastIstNode); !exists { markers = append(markers, osgraph.Marker{ Node: uncastDcNode, RelatedNodes: []graph.Node{uncastIstNode, isNode}, Severity: osgraph.WarningSeverity, Key: MissingImageStreamWarning, Message: fmt.Sprintf("The image trigger for %s will have no effect because %s does not exist.", dcNode.ResourceString(), isNode.(*imagegraph.ImageStreamNode).ResourceString()), }) continue dc } // 2. Build config points to image stream tag of interest if bcNode, points := buildPointsToTag(g, uncastIstNode); points { markers = append(markers, osgraph.Marker{ Node: uncastDcNode, RelatedNodes: []graph.Node{uncastIstNode, bcNode}, Severity: osgraph.InfoSeverity, Key: ImageStreamTagNotAvailableInfo, Message: fmt.Sprintf("The image trigger for %s will have no effect because %s does not exist but %s points to %s.", dcNode.ResourceString(), istNode.ResourceString(), bcNode.(*buildgraph.BuildConfigNode).ResourceString(), istNode.ResourceString()), }) continue dc } // 3. Image stream tag of interest does not exist markers = append(markers, osgraph.Marker{ Node: uncastDcNode, RelatedNodes: []graph.Node{uncastIstNode}, Severity: osgraph.WarningSeverity, Key: MissingImageStreamTagWarning, Message: fmt.Sprintf("The image trigger for %s will have no effect because %s does not exist.", dcNode.ResourceString(), istNode.ResourceString()), }) continue dc } } } return markers }
// getImageStreamTagSuggestion will return the appropriate marker Suggestion for when a BuildConfig is missing its input ImageStreamTag; in particular, // it will determine whether or not another BuildConfig can produce the aforementioned ImageStreamTag func getImageStreamTagSuggestion(g osgraph.Graph, f osgraph.Namer, tagNode *imagegraph.ImageStreamTagNode) osgraph.Suggestion { bcs := []string{} for _, bcNode := range g.PredecessorNodesByEdgeKind(tagNode, buildedges.BuildOutputEdgeKind) { bcs = append(bcs, f.ResourceName(bcNode)) } if len(bcs) == 1 { return osgraph.Suggestion(fmt.Sprintf("oc start-build %s", bcs[0])) } if len(bcs) > 0 { return osgraph.Suggestion(fmt.Sprintf("`oc start-build` with one of these: %s.", strings.Join(bcs[:], ","))) } return osgraph.Suggestion(fmt.Sprintf("%s needs to be imported.", f.ResourceName(tagNode))) }
// NewPetSet returns the PetSet and a set of all the NodeIDs covered by the PetSet func NewPetSet(g osgraph.Graph, node *kubegraph.PetSetNode) (PetSet, IntSet) { covered := IntSet{} covered.Insert(node.ID()) view := PetSet{} view.PetSet = node for _, uncastPodNode := range g.PredecessorNodesByEdgeKind(node, kubeedges.ManagedByControllerEdgeKind) { podNode := uncastPodNode.(*kubegraph.PodNode) covered.Insert(podNode.ID()) view.OwnedPods = append(view.OwnedPods, podNode) } return view, covered }
// FindMissingInputImageStreams checks all build configs and confirms that their From element exists // // Precedence of failures: // 1. A build config's input points to an image stream that does not exist // 2. A build config's input uses an image stream tag reference in an existing image stream, but no images within the image stream have that tag assigned // 3. A build config's input uses an image stream image reference in an exisiting image stream, but no images within the image stream have the supplied image hexadecimal ID func FindMissingInputImageStreams(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker { markers := []osgraph.Marker{} for _, bcNode := range g.NodesByKind(buildgraph.BuildConfigNodeKind) { for _, bcInputNode := range g.PredecessorNodesByEdgeKind(bcNode, buildedges.BuildInputImageEdgeKind) { switch bcInputNode.(type) { case *imagegraph.ImageStreamTagNode: for _, uncastImageStreamNode := range g.SuccessorNodesByEdgeKind(bcInputNode, imageedges.ReferencedImageStreamGraphEdgeKind) { imageStreamNode := uncastImageStreamNode.(*imagegraph.ImageStreamNode) // note, BuildConfig.Spec.BuildSpec.Strategy.[Docker|Source|Custom]Stragegy.From Input of ImageStream has been converted to ImageStreamTag on the vX to api conversion // prior to our reaching this point in the code; so there is not need to check for that type vs. ImageStreamTag or ImageStreamImage; tagNode, _ := bcInputNode.(*imagegraph.ImageStreamTagNode) imageStream := imageStreamNode.Object().(*imageapi.ImageStream) if _, ok := imageStream.Status.Tags[tagNode.ImageTag()]; !ok { markers = append(markers, getImageStreamTagMarker(g, f, bcInputNode, imageStreamNode, tagNode, bcNode)) } } case *imagegraph.ImageStreamImageNode: for _, uncastImageStreamNode := range g.SuccessorNodesByEdgeKind(bcInputNode, imageedges.ReferencedImageStreamImageGraphEdgeKind) { imageStreamNode := uncastImageStreamNode.(*imagegraph.ImageStreamNode) imageNode, _ := bcInputNode.(*imagegraph.ImageStreamImageNode) imageStream := imageStreamNode.Object().(*imageapi.ImageStream) found, imageID := validImageStreamImage(imageNode, imageStream) if !found { markers = append(markers, getImageStreamImageMarker(g, f, bcNode, bcInputNode, imageStreamNode, imageNode, imageStream, imageID)) } } } } } return markers }
// FindDeploymentConfigTriggerErrors checks for possible failures in deployment config // image change triggers. // // Precedence of failures: // 1. The image stream for the tag of interest does not exist. // 2. The image stream tag does not exist. func FindDeploymentConfigTriggerErrors(g osgraph.Graph) []osgraph.Marker { markers := []osgraph.Marker{} dc: for _, uncastDcNode := range g.NodesByKind(deploygraph.DeploymentConfigNodeKind) { for _, uncastIstNode := range g.PredecessorNodesByEdgeKind(uncastDcNode, deployedges.TriggersDeploymentEdgeKind) { if istNode := uncastIstNode.(*imagegraph.ImageStreamTagNode); !istNode.Found() { dcNode := uncastDcNode.(*deploygraph.DeploymentConfigNode) // The image stream for the tag of interest does not exist. // TODO: Suggest `oc create imagestream` once we have that. if isNode, exists := doesImageStreamExist(g, uncastIstNode); !exists { markers = append(markers, osgraph.Marker{ Node: uncastDcNode, RelatedNodes: []graph.Node{uncastIstNode, isNode}, Severity: osgraph.ErrorSeverity, Key: MissingImageStreamErr, Message: fmt.Sprintf("The image trigger for %s will have no effect because %s does not exist.", dcNode.ResourceString(), isNode.(*imagegraph.ImageStreamNode).ResourceString()), }) continue dc } // The image stream tag of interest does not exist. markers = append(markers, osgraph.Marker{ Node: uncastDcNode, RelatedNodes: []graph.Node{uncastIstNode}, Severity: osgraph.WarningSeverity, Key: MissingImageStreamTagWarning, Message: fmt.Sprintf("The image trigger for %s will have no effect until %s is imported or created by a build.", dcNode.ResourceString(), istNode.ResourceString()), }) continue dc } } } return markers }
func findBuildInputs(g osgraph.Graph, bcNode *buildgraph.BuildConfigNode) (base ImageTagLocation, source SourceLocation, covered IntSet, err error) { covered = IntSet{} // find inputs to the build for _, input := range g.PredecessorNodesByEdgeKind(bcNode, buildedges.BuildInputEdgeKind) { if source != nil { // report this as an error (unexpected duplicate source) } covered.Insert(input.ID()) source = input.(SourceLocation) } for _, input := range g.PredecessorNodesByEdgeKind(bcNode, buildedges.BuildInputImageEdgeKind) { if base != nil { // report this as an error (unexpected duplicate input build) } covered.Insert(input.ID()) base = input.(ImageTagLocation) } return }
func FindDuelingReplicationControllers(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker { markers := []osgraph.Marker{} for _, uncastRCNode := range g.NodesByKind(kubegraph.ReplicationControllerNodeKind) { rcNode := uncastRCNode.(*kubegraph.ReplicationControllerNode) for _, uncastPodNode := range g.PredecessorNodesByEdgeKind(rcNode, kubeedges.ManagedByRCEdgeKind) { podNode := uncastPodNode.(*kubegraph.PodNode) // check to see if this pod is managed by more than one RC uncastOwningRCs := g.SuccessorNodesByEdgeKind(podNode, kubeedges.ManagedByRCEdgeKind) if len(uncastOwningRCs) > 1 { involvedRCNames := []string{} relatedNodes := []graph.Node{uncastPodNode} for _, uncastOwningRC := range uncastOwningRCs { if uncastOwningRC.ID() == rcNode.ID() { continue } owningRC := uncastOwningRC.(*kubegraph.ReplicationControllerNode) involvedRCNames = append(involvedRCNames, f.ResourceName(owningRC)) relatedNodes = append(relatedNodes, uncastOwningRC) } markers = append(markers, osgraph.Marker{ Node: rcNode, RelatedNodes: relatedNodes, Severity: osgraph.WarningSeverity, Key: DuelingReplicationControllerWarning, Message: fmt.Sprintf("%s is competing for %s with %s", f.ResourceName(rcNode), f.ResourceName(podNode), strings.Join(involvedRCNames, ", ")), }) } } } return markers }
// ictMarker inspects the image change triggers for the provided deploymentconfig and returns // a marker in case of the following two scenarios: // // 1. The image stream pointed by the dc trigger doen not exist. // 2. The image stream tag pointed by the dc trigger does not exist and there is no build in // flight that could push to the tag. func ictMarker(g osgraph.Graph, f osgraph.Namer, dcNode *deploygraph.DeploymentConfigNode) *osgraph.Marker { for _, uncastIstNode := range g.PredecessorNodesByEdgeKind(dcNode, deployedges.TriggersDeploymentEdgeKind) { if istNode := uncastIstNode.(*imagegraph.ImageStreamTagNode); !istNode.Found() { // The image stream for the tag of interest does not exist. if isNode, exists := doesImageStreamExist(g, uncastIstNode); !exists { return &osgraph.Marker{ Node: dcNode, RelatedNodes: []graph.Node{uncastIstNode, isNode}, Severity: osgraph.ErrorSeverity, Key: MissingImageStreamErr, Message: fmt.Sprintf("The image trigger for %s will have no effect because %s does not exist.", f.ResourceName(dcNode), f.ResourceName(isNode)), // TODO: Suggest `oc create imagestream` once we have that. } } for _, bcNode := range buildedges.BuildConfigsForTag(g, istNode) { // Avoid warning for the dc image trigger in case there is a build in flight. if latestBuild := buildedges.GetLatestBuild(g, bcNode); latestBuild != nil && !buildutil.IsBuildComplete(latestBuild.Build) { return nil } } // The image stream tag of interest does not exist. return &osgraph.Marker{ Node: dcNode, RelatedNodes: []graph.Node{uncastIstNode}, Severity: osgraph.WarningSeverity, Key: MissingImageStreamTagWarning, Message: fmt.Sprintf("The image trigger for %s will have no effect until %s is imported or created by a build.", f.ResourceName(dcNode), f.ResourceName(istNode)), } } } return nil }
// buildPointsToTag returns the buildConfig that points to the provided imageStreamTag. func buildPointsToTag(g osgraph.Graph, istag graph.Node) (*buildgraph.BuildConfigNode, bool) { for _, bcNode := range g.PredecessorNodesByEdgeKind(istag, buildedges.BuildOutputEdgeKind) { return bcNode.(*buildgraph.BuildConfigNode), true } return nil, false }
// BuildConfigForTag returns the buildConfig that points to the provided imageStreamTag. // TODO: Handle multiple buildconfigs pointing to the same tag. func BuildConfigForTag(g osgraph.Graph, istag graph.Node) *buildgraph.BuildConfigNode { for _, bcNode := range g.PredecessorNodesByEdgeKind(istag, BuildOutputEdgeKind) { return bcNode.(*buildgraph.BuildConfigNode) } return nil }
// FindMissingInputImageStreams checks all build configs and confirms that their From element exists // // Precedence of failures: // 1. A build config's input points to an image stream that does not exist // 2. A build config's input uses an image stream tag reference in an existing image stream, but no images within the image stream have that tag assigned // 3. A build config's input uses an image stream image reference in an exisiting image stream, but no images within the image stream have the supplied image hexadecimal ID func FindMissingInputImageStreams(g osgraph.Graph, f osgraph.Namer) []osgraph.Marker { markers := []osgraph.Marker{} for _, bcNode := range g.NodesByKind(buildgraph.BuildConfigNodeKind) { for _, bcInputNode := range g.PredecessorNodesByEdgeKind(bcNode, buildedges.BuildInputImageEdgeKind) { switch bcInputNode.(type) { case *imagegraph.ImageStreamTagNode: for _, uncastImageStreamNode := range g.SuccessorNodesByEdgeKind(bcInputNode, imageedges.ReferencedImageStreamGraphEdgeKind) { imageStreamNode := uncastImageStreamNode.(*imagegraph.ImageStreamNode) // note, BuildConfig.Spec.BuildSpec.Strategy.[Docker|Source|Custom]Stragegy.From Input of ImageStream has been converted to ImageStreamTag on the vX to api conversion // prior to our reaching this point in the code; so there is not need to check for that type vs. ImageStreamTag or ImageStreamImage; tagNode, _ := bcInputNode.(*imagegraph.ImageStreamTagNode) imageStream := imageStreamNode.Object().(*imageapi.ImageStream) if _, ok := imageStream.Status.Tags[tagNode.ImageTag()]; !ok { markers = append(markers, osgraph.Marker{ Node: bcNode, RelatedNodes: []graph.Node{bcInputNode, imageStreamNode}, Severity: osgraph.WarningSeverity, Key: MissingImageStreamTagWarning, Message: fmt.Sprintf("%s builds from %s, but the image stream tag does not exist.", f.ResourceName(bcNode), f.ResourceName(bcInputNode)), Suggestion: osgraph.Suggestion(fmt.Sprintf("examine analysis of build config outputs from this command and see if they build %s", f.ResourceName(bcInputNode))), }) } } case *imagegraph.ImageStreamImageNode: for _, uncastImageStreamNode := range g.SuccessorNodesByEdgeKind(bcInputNode, imageedges.ReferencedImageStreamImageGraphEdgeKind) { imageStreamNode := uncastImageStreamNode.(*imagegraph.ImageStreamNode) imageNode, _ := bcInputNode.(*imagegraph.ImageStreamImageNode) imageStream := imageStreamNode.Object().(*imageapi.ImageStream) found, imageID, suggestion := validImageStreamImage(imageNode, imageStream) if !found { markers = append(markers, osgraph.Marker{ Node: bcNode, RelatedNodes: []graph.Node{bcInputNode, imageStreamNode}, Severity: osgraph.WarningSeverity, Key: MissingImageStreamImageWarning, Message: fmt.Sprintf("%s builds from %s, but the image stream image does not exist.", f.ResourceName(bcNode), f.ResourceName(bcInputNode)), Suggestion: osgraph.Suggestion(fmt.Sprintf(suggestion, imageID, f.ResourceName(imageStreamNode))), }) } } } } } return markers }