sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/statusreconciler/status.go (about)

     1  /*
     2  Copyright 2018 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 statusreconciler
    18  
    19  import (
    20  	"context"
    21  	stdio "io"
    22  	"time"
    23  
    24  	"github.com/sirupsen/logrus"
    25  	"sigs.k8s.io/yaml"
    26  
    27  	"sigs.k8s.io/prow/pkg/config"
    28  	configflagutil "sigs.k8s.io/prow/pkg/flagutil/config"
    29  	"sigs.k8s.io/prow/pkg/io"
    30  )
    31  
    32  type storedState struct {
    33  	config.Config
    34  }
    35  
    36  type statusClient interface {
    37  	Load() (chan config.Delta, error)
    38  	Save() error
    39  }
    40  
    41  // opener has methods to read and write paths
    42  type opener interface {
    43  	Reader(ctx context.Context, path string) (io.ReadCloser, error)
    44  	Writer(ctx context.Context, path string, opts ...io.WriterOptions) (io.WriteCloser, error)
    45  }
    46  
    47  type statusController struct {
    48  	logger     *logrus.Entry
    49  	opener     opener
    50  	statusURI  string
    51  	configOpts configflagutil.ConfigOptions
    52  
    53  	storedState
    54  	config.Agent
    55  }
    56  
    57  func (s *statusController) Load() (chan config.Delta, error) {
    58  	s.Agent = config.Agent{}
    59  	state, err := s.loadState()
    60  	if err == nil {
    61  		s.Agent.Set(&state.Config)
    62  	}
    63  	changes := make(chan config.Delta)
    64  	s.Agent.Subscribe(changes)
    65  
    66  	if _, err := s.configOpts.ConfigAgent(&s.Agent); err != nil {
    67  		s.logger.WithError(err).Error("Error starting config agent.")
    68  		return nil, err
    69  	}
    70  	return changes, nil
    71  }
    72  
    73  func (s *statusController) Save() error {
    74  	if s.statusURI == "" {
    75  		return nil
    76  	}
    77  	entry := s.logger.WithField("path", s.statusURI)
    78  	current := s.Agent.Config()
    79  	buf, err := yaml.Marshal(current)
    80  	if err != nil {
    81  		entry.WithError(err).Warn("Cannot marshal state")
    82  		return err
    83  	}
    84  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    85  	defer cancel()
    86  	writer, err := s.opener.Writer(ctx, s.statusURI)
    87  	if err != nil {
    88  		entry.WithError(err).Warn("Cannot open state writer")
    89  		return err
    90  	}
    91  	if _, err = writer.Write(buf); err != nil {
    92  		entry.WithError(err).Warn("Cannot write state")
    93  		io.LogClose(writer)
    94  		return err
    95  	}
    96  	if err := writer.Close(); err != nil {
    97  		entry.WithError(err).Warn("Failed to close written state")
    98  	}
    99  	entry.Debug("Saved status state")
   100  	return nil
   101  }
   102  
   103  func (s *statusController) loadState() (storedState, error) {
   104  	var state storedState
   105  	if s.statusURI == "" {
   106  		s.logger.Debug("No stored state configured")
   107  		return state, nil
   108  	}
   109  	entry := s.logger.WithField("path", s.statusURI)
   110  	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   111  	defer cancel()
   112  	reader, err := s.opener.Reader(ctx, s.statusURI)
   113  	if err != nil {
   114  		entry.WithError(err).Warn("Cannot open stored state")
   115  		return state, err
   116  	}
   117  	defer io.LogClose(reader)
   118  
   119  	buf, err := stdio.ReadAll(reader)
   120  	if err != nil {
   121  		entry.WithError(err).Warn("Cannot read stored state")
   122  		return state, err
   123  	}
   124  
   125  	if err := yaml.Unmarshal(buf, &state); err != nil {
   126  		entry.WithError(err).Warn("Cannot unmarshal stored state")
   127  		return state, err
   128  	}
   129  	return state, nil
   130  }
   131  
   132  func (s *statusController) Config() *config.Config {
   133  	return s.Agent.Config()
   134  }