sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/crier/reporters/criercommonlib/updatereportstatus.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package criercommonlib contains shared lib used by reporters
    18  package criercommonlib
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"time"
    24  
    25  	"github.com/sirupsen/logrus"
    26  	"k8s.io/apimachinery/pkg/types"
    27  	"k8s.io/apimachinery/pkg/util/wait"
    28  	"k8s.io/client-go/util/retry"
    29  
    30  	ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
    31  
    32  	prowv1 "sigs.k8s.io/prow/pkg/apis/prowjobs/v1"
    33  )
    34  
    35  func updateReportState(ctx context.Context, pj *prowv1.ProwJob, log *logrus.Entry, reportedState prowv1.ProwJobState, pjclientset ctrlruntimeclient.Client, reporterName string) error {
    36  	// update pj report status
    37  	newpj := pj.DeepCopy()
    38  	// we set omitempty on PrevReportStates, so here we need to init it if is nil
    39  	if newpj.Status.PrevReportStates == nil {
    40  		newpj.Status.PrevReportStates = map[string]prowv1.ProwJobState{}
    41  	}
    42  	newpj.Status.PrevReportStates[reporterName] = reportedState
    43  
    44  	if err := pjclientset.Patch(ctx, newpj, ctrlruntimeclient.MergeFrom(pj)); err != nil {
    45  		return fmt.Errorf("failed to patch: %w", err)
    46  	}
    47  
    48  	// Block until the update is in the lister to make sure that events from another controller
    49  	// that also does reporting dont trigger another report because our lister doesn't yet contain
    50  	// the updated Status
    51  	name := types.NamespacedName{Namespace: pj.Namespace, Name: pj.Name}
    52  	if err := wait.Poll(100*time.Millisecond, 10*time.Second, func() (bool, error) {
    53  		if err := pjclientset.Get(ctx, name, pj); err != nil {
    54  			return false, err
    55  		}
    56  		if pj.Status.PrevReportStates != nil &&
    57  			pj.Status.PrevReportStates[reporterName] == reportedState {
    58  			return true, nil
    59  		}
    60  		return false, nil
    61  	}); err != nil {
    62  		return fmt.Errorf("failed to wait for updated report status to be in lister: %w", err)
    63  	}
    64  	return nil
    65  }
    66  
    67  func UpdateReportStateWithRetries(ctx context.Context, pj *prowv1.ProwJob, log *logrus.Entry, pjclientset ctrlruntimeclient.Client, reporterName string) error {
    68  	reportState := pj.Status.State
    69  	log = log.WithFields(logrus.Fields{
    70  		"prowjob":   pj.Name,
    71  		"jobName":   pj.Spec.Job,
    72  		"jobStatus": reportState,
    73  	})
    74  	// We have to retry here, if we return we lose the information that we already reported this job.
    75  	if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
    76  		// Get it first, this is very cheap
    77  		name := types.NamespacedName{Namespace: pj.Namespace, Name: pj.Name}
    78  		if err := pjclientset.Get(ctx, name, pj); err != nil {
    79  			return err
    80  		}
    81  		// Must not wrap until we have kube 1.19, otherwise the RetryOnConflict won't recognize conflicts
    82  		// correctly
    83  		return updateReportState(ctx, pj, log, reportState, pjclientset, reporterName)
    84  	}); err != nil {
    85  		// Very subpar, we will report again. But even if we didn't do that now, we would do so
    86  		// latest when crier gets restarted. In an ideal world, all reporters are idempotent and
    87  		// reporting has no cost.
    88  		return fmt.Errorf("failed to update report state on prowjob: %w", err)
    89  	}
    90  
    91  	log.Info("Successfully updated report state on prowjob")
    92  	return nil
    93  }