// BuildNumber returns the given build number. func BuildNumber(build *buildapi.Build) (int64, error) { annotations := build.GetAnnotations() if stringNumber, ok := annotations[buildapi.BuildNumberAnnotation]; ok { return strconv.ParseInt(stringNumber, 10, 64) } return 0, fmt.Errorf("build %s/%s does not have %s annotation", build.Namespace, build.Name, buildapi.BuildNumberAnnotation) }
func convert_v1_BuildStatus_To_api_Build(in *BuildStatus, out *newer.Build, s conversion.Scope) error { out.Cancelled = in.Cancelled out.CompletionTimestamp = in.CompletionTimestamp if err := s.Convert(&in.Config, &out.Config, 0); err != nil { return err } out.Duration = in.Duration out.Message = in.Message out.StartTimestamp = in.StartTimestamp return nil }
func setDuration(build *api.Build) { if build.StartTimestamp == nil { build.Duration = time.Duration(0) } else { completionTimestamp := build.CompletionTimestamp if completionTimestamp == nil { dummy := util.Now() completionTimestamp = &dummy } build.Duration = completionTimestamp.Rfc3339Copy().Time.Sub(build.StartTimestamp.Rfc3339Copy().Time) } }
//setBuildAnnotationAndLabel set annotations and label info of this build func setBuildAnnotationAndLabel(bcCopy *buildapi.BuildConfig, build *buildapi.Build) { if build.Annotations == nil { build.Annotations = make(map[string]string) } //bcCopy.Status.LastVersion has been increased build.Annotations[buildapi.BuildNumberAnnotation] = strconv.FormatInt(bcCopy.Status.LastVersion, 10) build.Annotations[buildapi.BuildConfigAnnotation] = bcCopy.Name if build.Labels == nil { build.Labels = make(map[string]string) } build.Labels[buildapi.BuildConfigLabelDeprecated] = buildapi.LabelValue(bcCopy.Name) build.Labels[buildapi.BuildConfigLabel] = buildapi.LabelValue(bcCopy.Name) build.Labels[buildapi.BuildRunPolicyLabel] = string(bcCopy.Spec.RunPolicy) }
// BuildRunPolicy returns the scheduling policy for the build based on the // "queued" label. func BuildRunPolicy(build *buildapi.Build) buildapi.BuildRunPolicy { labels := build.GetLabels() if value, found := labels[buildapi.BuildRunPolicyLabel]; found { switch value { case "Parallel": return buildapi.BuildRunPolicyParallel case "Serial": return buildapi.BuildRunPolicySerial case "SerialLatestOnly": return buildapi.BuildRunPolicySerialLatestOnly } } glog.V(5).Infof("Build %s/%s does not have start policy label set, using default (Serial)", build.Namespace, build.Name) return buildapi.BuildRunPolicySerial }
func updateBuildRevision(c client.BuildInterface, build *api.Build, sourceInfo *git.SourceInfo) { if build.Spec.Revision != nil { return } build.Spec.Revision = &api.SourceRevision{ Git: &api.GitSourceRevision{ Commit: sourceInfo.CommitID, Message: sourceInfo.Message, Author: api.SourceControlUser{ Name: sourceInfo.AuthorName, Email: sourceInfo.AuthorEmail, }, Committer: api.SourceControlUser{ Name: sourceInfo.CommitterName, Email: sourceInfo.CommitterEmail, }, }, } // Reset ResourceVersion to avoid a conflict with other updates to the build build.ResourceVersion = "" glog.V(4).Infof("Setting build revision to %#v", build.Spec.Revision.Git) _, err := c.UpdateDetails(build) if err != nil { glog.Warningf("An error occurred saving build revision: %v", err) } }
// Determine the next status of a build given its current state and the state // of its associated pod. // TODO: improve handling of illegal state transitions func (bc *BuildController) synchronize(build *api.Build) (api.BuildStatus, error) { glog.Infof("Syncing build %s", build.ID) switch build.Status { case api.BuildNew: build.PodID = "build-" + string(build.Input.Type) + "-" + build.ID // TODO: better naming return api.BuildPending, nil case api.BuildPending: buildStrategy, ok := bc.buildStrategies[build.Input.Type] if !ok { return api.BuildError, fmt.Errorf("No build type for %s", build.Input.Type) } podSpec := buildStrategy.CreateBuildPod(build, bc.dockerRegistry) glog.Infof("Attempting to create pod: %#v", podSpec) _, err := bc.kubeClient.CreatePod(*podSpec) // TODO: strongly typed error checking if err != nil { if strings.Index(err.Error(), "already exists") != -1 { return build.Status, err // no transition, already handled by someone else } return api.BuildFailed, err } return api.BuildRunning, nil case api.BuildRunning: if timedOut := hasTimeoutElapsed(build, bc.timeout); timedOut { return api.BuildFailed, fmt.Errorf("Build timed out") } pod, err := bc.kubeClient.GetPod(build.PodID) if err != nil { return build.Status, fmt.Errorf("Error retrieving pod for build ID %v: %#v", build.ID, err) } // pod is still running if pod.CurrentState.Status != kubeapi.PodTerminated { return build.Status, nil } var nextStatus = api.BuildComplete // check the exit codes of all the containers in the pod for _, info := range pod.CurrentState.Info { if info.State.ExitCode != 0 { nextStatus = api.BuildFailed } } return nextStatus, nil case api.BuildComplete, api.BuildFailed, api.BuildError: return build.Status, nil default: return api.BuildError, fmt.Errorf("Invalid build status: %s", build.Status) } }
// CancelBuild updates a build status to Cancelled, after its associated pod is deleted. func (bc *BuildPodController) CancelBuild(build *buildapi.Build, pod *kapi.Pod) error { if !isBuildCancellable(build) { glog.V(4).Infof("Build %s/%s can be cancelled only if it has pending/running status, not %s.", build.Namespace, build.Name, build.Status) return nil } err := bc.PodManager.DeletePod(build.Namespace, pod) if err != nil && !errors.IsNotFound(err) { return err } glog.V(4).Infof("Build %s/%s is about to be cancelled", build.Namespace, build.Name) build.Status = buildapi.BuildStatusCancelled now := util.Now() build.CompletionTimestamp = &now if err := bc.BuildUpdater.Update(build.Namespace, build); err != nil { return err } glog.V(4).Infof("Build %s/%s was successfully cancelled.", build.Namespace, build.Name) return nil }
func withStatus(build *buildapi.Build, status buildapi.BuildStatus) *buildapi.Build { build.Status = status return build }
func withCreated(build *buildapi.Build, creationTimestamp util.Time) *buildapi.Build { build.CreationTimestamp = creationTimestamp return build }
// nextBuildPhase updates build with any appropriate changes, or returns an error if // the change cannot occur. When returning nil, be sure to set build.Status and optionally // build.Message. func (bc *BuildController) nextBuildPhase(build *buildapi.Build) error { // If a cancelling event was triggered for the build, update build status. if build.Status.Cancelled { glog.V(4).Infof("Cancelling build %s/%s.", build.Namespace, build.Name) build.Status.Phase = buildapi.BuildPhaseCancelled build.Status.Reason = "" build.Status.Message = "" return nil } // these builds are processed/updated/etc by the jenkins sync plugin if build.Spec.Strategy.JenkinsPipelineStrategy != nil { glog.V(4).Infof("Ignoring build with jenkins pipeline strategy") return nil } // Set the output Docker image reference. ref, err := bc.resolveOutputDockerImageReference(build) if err != nil { build.Status.Reason = buildapi.StatusReasonInvalidOutputReference return err } build.Status.OutputDockerImageReference = ref // Make a copy to avoid mutating the build from this point on. copy, err := kapi.Scheme.Copy(build) if err != nil { return fmt.Errorf("unable to copy build: %v", err) } buildCopy := copy.(*buildapi.Build) // TODO(rhcarvalho) // The S2I and Docker builders expect build.Spec.Output.To to contain a // resolved reference to a Docker image. Since build.Spec is immutable, we // change a copy (that is never persisted) and pass it to // bc.BuildStrategy.CreateBuildPod. We should make the builders use // build.Status.OutputDockerImageReference, what will make copying the build // unnecessary. if build.Spec.Output.To != nil && len(build.Spec.Output.To.Name) != 0 { buildCopy.Spec.Output.To = &kapi.ObjectReference{ Kind: "DockerImage", Name: ref, } } // Invoke the strategy to get a build pod. podSpec, err := bc.BuildStrategy.CreateBuildPod(buildCopy) if err != nil { build.Status.Reason = buildapi.StatusReasonCannotCreateBuildPodSpec if strategy.IsFatal(err) { return strategy.FatalError(fmt.Sprintf("failed to create a build pod spec for build %s/%s: %v", build.Namespace, build.Name, err)) } return fmt.Errorf("failed to create a build pod spec for build %s/%s: %v", build.Namespace, build.Name, err) } glog.V(4).Infof("Pod %s for build %s/%s is about to be created", podSpec.Name, build.Namespace, build.Name) if _, err := bc.PodManager.CreatePod(build.Namespace, podSpec); err != nil { if errors.IsAlreadyExists(err) { bc.Recorder.Eventf(build, kapi.EventTypeWarning, "failedCreate", "Pod already exists: %s/%s", podSpec.Namespace, podSpec.Name) glog.V(4).Infof("Build pod already existed: %#v", podSpec) return nil } // Log an event if the pod is not created (most likely due to quota denial). bc.Recorder.Eventf(build, kapi.EventTypeWarning, "FailedCreate", "Error creating: %v", err) build.Status.Reason = buildapi.StatusReasonCannotCreateBuildPod return fmt.Errorf("failed to create build pod: %v", err) } if build.Annotations == nil { build.Annotations = make(map[string]string) } build.Annotations[buildapi.BuildPodNameAnnotation] = podSpec.Name glog.V(4).Infof("Created pod for build: %#v", podSpec) // Set the build phase, which will be persisted. build.Status.Phase = buildapi.BuildPhasePending build.Status.Reason = "" build.Status.Message = "" return nil }
// nextBuildStatus updates build with any appropriate changes, or returns an error if // the change cannot occur. When returning nil, be sure to set build.Status and optionally // build.Message. func (bc *BuildController) nextBuildStatus(build *buildapi.Build) error { // If a cancelling event was triggered for the build, update build status. if build.Cancelled { glog.V(4).Infof("Cancelling Build %s/%s.", build.Namespace, build.Name) build.Status = buildapi.BuildStatusCancelled return nil } // lookup the destination from the referenced image repository spec := build.Parameters.Output.DockerImageReference if ref := build.Parameters.Output.To; ref != nil { // TODO: security, ensure that the reference image stream is actually visible namespace := ref.Namespace if len(namespace) == 0 { namespace = build.Namespace } repo, err := bc.ImageStreamClient.GetImageStream(namespace, ref.Name) if err != nil { if errors.IsNotFound(err) { return fmt.Errorf("the referenced output ImageStream %s/%s does not exist", namespace, ref.Name) } return fmt.Errorf("the referenced output ImageStream %s/%s could not be found by Build %s/%s: %v", namespace, ref.Name, build.Namespace, build.Name, err) } if len(repo.Status.DockerImageRepository) == 0 { e := fmt.Errorf("the ImageStream %s/%s cannot be used as the output for Build %s/%s because the integrated Docker registry is not configured, or the user forgot to set a valid external registry", namespace, ref.Name, build.Namespace, build.Name) bc.Recorder.Eventf(build, "invalidOutput", "Error starting build: %v", e) return e } spec = repo.Status.DockerImageRepository } // set the expected build parameters, which will be saved if no error occurs build.Status = buildapi.BuildStatusPending // Make a copy to avoid mutating the build from this point on copy, err := kapi.Scheme.Copy(build) if err != nil { return fmt.Errorf("unable to copy Build: %v", err) } buildCopy := copy.(*buildapi.Build) // override DockerImageReference in the strategy for the copy we send to the build pod buildCopy.Parameters.Output.DockerImageReference = spec buildCopy.Parameters.Output.To = nil // invoke the strategy to get a build pod podSpec, err := bc.BuildStrategy.CreateBuildPod(buildCopy) if err != nil { return fmt.Errorf("the strategy failed to create a build pod for Build %s/%s: %v", build.Namespace, build.Name, err) } glog.V(4).Infof("Pod %s for Build %s/%s is about to be created", podSpec.Name, build.Namespace, build.Name) if _, err := bc.PodManager.CreatePod(build.Namespace, podSpec); err != nil { if errors.IsAlreadyExists(err) { glog.V(4).Infof("Build pod already existed: %#v", podSpec) return nil } // log an event if the pod is not created (most likely due to quota denial) bc.Recorder.Eventf(build, "failedCreate", "Error creating: %v", err) return fmt.Errorf("failed to create pod for Build %s/%s: %v", build.Namespace, build.Name, err) } glog.V(4).Infof("Created pod for Build: %#v", podSpec) return nil }
func updateBuild(t *testing.T, buildInterface client.BuildInterface, build *buildapi.Build) (*buildapi.Build, error) { build.Labels = map[string]string{"updated": "true"} return buildInterface.Update(build) }
func setBuildPodNameAnnotation(build *buildapi.Build, podName string) { if build.Annotations == nil { build.Annotations = map[string]string{} } build.Annotations[buildapi.BuildPodNameAnnotation] = podName }