prow/cmd/build/controller.go
func reconcile(c reconciler, key string) error {
	ctx, namespace, name, err := fromKey(key)
	if err != nil {
		runtime.HandleError(err)
		return nil
	}

	if err := c.terminateDupProwJobs(ctx, namespace); err != nil {
		logrus.WithError(err).Warn("Cannot terminate duplicated prow jobs")
	}

	var wantBuild bool

	pj, err := c.getProwJob(name)
	switch {
	case apierrors.IsNotFound(err):
		// Do not want build
	case err != nil:
		return fmt.Errorf("get prowjob: %v", err)
	case pj.Spec.Agent != prowjobv1.KnativeBuildAgent:
		// Do not want a build for this job
	case pjutil.ClusterToCtx(pj.Spec.Cluster) != ctx:
		// Build is in wrong cluster, we do not want this build
		logrus.Warnf("%s found in context %s not %s", key, ctx, pjutil.ClusterToCtx(pj.Spec.Cluster))
	case pj.DeletionTimestamp == nil:
		wantBuild = true
	}

	var haveBuild bool

	b, err := c.getBuild(ctx, namespace, name)
	switch {
	case apierrors.IsNotFound(err):
		// Do not have a build
	case err != nil:
		return fmt.Errorf("get build %s: %v", key, err)
	case b.DeletionTimestamp == nil:
		haveBuild = true
	}

	var newBuildID bool
	// Should we create or delete this build?
	switch {
	case !wantBuild:
		if !haveBuild {
			if pj != nil && pj.Spec.Agent == prowjobv1.KnativeBuildAgent {
				logrus.Infof("Observed deleted %s", key)
			}
			return nil
		}
		switch v, ok := b.Labels[kube.CreatedByProw]; {
		case !ok, v != "true": // Not controlled by this
			return nil
		}
		logrus.Infof("Delete builds/%s", key)
		if err = c.deleteBuild(ctx, namespace, name); err != nil {
			return fmt.Errorf("delete build: %v", err)
		}
		return nil
	case finalState(pj.Status.State):
		logrus.Infof("Observed finished %s", key)
		return nil
	case wantBuild && pj.Spec.BuildSpec == nil:
		return errors.New("nil BuildSpec")
	case wantBuild && !haveBuild:
		id, url, err := c.buildID(*pj)
		if err != nil {
			return fmt.Errorf("failed to get build id: %v", err)
		}
		pj.Status.BuildID = id
		pj.Status.URL = url
		newBuildID = true
		if b, err = makeBuild(*pj, c.defaultBuildTimeout()); err != nil {
			return fmt.Errorf("make build: %v", err)
		}
		logrus.Infof("Create builds/%s", key)
		if b, err = c.createBuild(ctx, namespace, b); err != nil {
			return fmt.Errorf("create build: %v", err)
		}
	}

	// Ensure prowjob status is correct
	haveState := pj.Status.State
	haveMsg := pj.Status.Description
	wantState, wantMsg := prowJobStatus(b.Status)
	if newBuildID || haveState != wantState || haveMsg != wantMsg {
		npj := pj.DeepCopy()
		if npj.Status.StartTime.IsZero() {
			npj.Status.StartTime = c.now()
		}
		if npj.Status.CompletionTime.IsZero() && finalState(wantState) {
			now := c.now()
			npj.Status.CompletionTime = &now
		}
		npj.Status.State = wantState
		npj.Status.Description = wantMsg
		logrus.Infof("Update prowjobs/%s", key)
		if _, err = c.updateProwJob(npj); err != nil {
			return fmt.Errorf("update prow status: %v", err)
		}
	}
	return nil
}
func reconcile(c reconciler, key string) error {
	ctx, namespace, name, err := fromKey(key)
	if err != nil {
		runtime.HandleError(err)
		return nil
	}

	if err := c.terminateDupProwJobs(ctx, namespace); err != nil {
		logrus.WithError(err).Warn("Cannot terminate duplicated prow jobs")
	}

	var wantBuild bool

	pj, err := c.getProwJob(name)
	switch {
	case apierrors.IsNotFound(err):
		// Do not want build
	case err != nil:
		return fmt.Errorf("get prowjob: %v", err)
	case pj.Spec.Agent != prowjobv1.KnativeBuildAgent:
		// Do not want a build for this job
	case pjutil.ClusterToCtx(pj.Spec.Cluster) != ctx:
		// Build is in wrong cluster, we do not want this build
		logrus.Warnf("%s found in context %s not %s", key, ctx, pjutil.ClusterToCtx(pj.Spec.Cluster))
	case pj.DeletionTimestamp == nil:
		wantBuild = true
	}

	var haveBuild bool

	b, err := c.getBuild(ctx, namespace, name)
	switch {
	case apierrors.IsNotFound(err):
		// Do not have a build
	case err != nil:
		return fmt.Errorf("get build %s: %v", key, err)
	case b.DeletionTimestamp == nil:
		haveBuild = true
	}

	var newBuildID bool
	// Should we create or delete this build?
	switch {
	case !wantBuild:
		if !haveBuild {
			if pj != nil && pj.Spec.Agent == prowjobv1.KnativeBuildAgent {
				logrus.Infof("Observed deleted %s", key)
			}
			return nil
		}
		switch v, ok := b.Labels[kube.CreatedByProw]; {
		case !ok, v != "true": // Not controlled by this
			return nil
		}
		logrus.Infof("Delete builds/%s", key)
		if err = c.deleteBuild(ctx, namespace, name); err != nil {
			return fmt.Errorf("delete build: %v", err)
		}
		return nil
	case finalState(pj.Status.State):
		logrus.Infof("Observed finished %s", key)
		return nil
	case wantBuild && pj.Spec.BuildSpec == nil:
		return errors.New("nil BuildSpec")
	case wantBuild && !haveBuild:
		id, url, err := c.buildID(*pj)
		if err != nil {
			return fmt.Errorf("failed to get build id: %v", err)
		}
		pj.Status.BuildID = id
		pj.Status.URL = url
		newBuildID = true
		if b, err = makeBuild(*pj, c.defaultBuildTimeout()); err != nil {
			return fmt.Errorf("make build: %v", err)
		}
		logrus.Infof("Create builds/%s", key)
		if b, err = c.createBuild(ctx, namespace, b); err != nil {
			return fmt.Errorf("create build: %v", err)
		}
	}

	// Ensure prowjob status is correct
	haveState := pj.Status.State
	haveMsg := pj.Status.Description
	wantState, wantMsg := prowJobStatus(b.Status)
	if newBuildID || haveState != wantState || haveMsg != wantMsg {
		npj := pj.DeepCopy()
		if npj.Status.StartTime.IsZero() {
			npj.Status.StartTime = c.now()
		}
		if npj.Status.CompletionTime.IsZero() && finalState(wantState) {
			now := c.now()
			npj.Status.CompletionTime = &now
		}
		npj.Status.State = wantState
		npj.Status.Description = wantMsg
		logrus.Infof("Update prowjobs/%s", key)
		if _, err = c.updateProwJob(npj); err != nil {
			return fmt.Errorf("update prow status: %v", err)
		}
	}
	return nil
}
// reconcile ensures a knative-build prowjob has a corresponding build, updating the prowjob's status as the build progresses.