github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/controllers/core/kubernetesapply/hash.go (about)

     1  package kubernetesapply
     2  
     3  import (
     4  	"crypto"
     5  	"encoding/base64"
     6  	"fmt"
     7  	"hash"
     8  
     9  	jsoniter "github.com/json-iterator/go"
    10  	"github.com/pkg/errors"
    11  	"k8s.io/apimachinery/pkg/types"
    12  
    13  	"github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1"
    14  )
    15  
    16  var defaultJSONIterator = createDefaultJSONIterator()
    17  
    18  func createDefaultJSONIterator() jsoniter.API {
    19  	return jsoniter.Config{
    20  		EscapeHTML:             true,
    21  		SortMapKeys:            true,
    22  		ValidateJsonRawMessage: true,
    23  		CaseSensitive:          true,
    24  	}.Froze()
    25  }
    26  
    27  // Compute the hash of all the inputs we fed into this apply.
    28  func ComputeInputHash(spec v1alpha1.KubernetesApplySpec, imageMaps map[types.NamespacedName]*v1alpha1.ImageMap) (string, error) {
    29  	w := newHashWriter()
    30  	err := w.append(spec)
    31  	if err != nil {
    32  		return "", err
    33  	}
    34  
    35  	for _, imageMapName := range spec.ImageMaps {
    36  		imageMap, ok := imageMaps[types.NamespacedName{Name: imageMapName}]
    37  		if !ok {
    38  			return "", fmt.Errorf("missing image map: %v", err)
    39  		}
    40  		err = w.append(imageMap.Spec)
    41  		if err != nil {
    42  			return "", fmt.Errorf("hashing %s spec: %v", imageMapName, err)
    43  		}
    44  
    45  		// Don't hash in the BuildStartedTime, because
    46  		// this changes even when the image does not change.
    47  		status := imageMap.Status
    48  		status.BuildStartTime = nil
    49  		err = w.append(status)
    50  		if err != nil {
    51  			return "", fmt.Errorf("hashing %s status: %v", imageMapName, err)
    52  		}
    53  	}
    54  
    55  	return w.done(), nil
    56  }
    57  
    58  type hashWriter struct {
    59  	h hash.Hash
    60  }
    61  
    62  func newHashWriter() *hashWriter {
    63  	return &hashWriter{h: crypto.SHA1.New()}
    64  }
    65  
    66  func (w hashWriter) append(o interface{}) error {
    67  	data, err := defaultJSONIterator.Marshal(o)
    68  	if err != nil {
    69  		return errors.Wrap(err, "serializing object for hash")
    70  	}
    71  	_, err = w.h.Write(data)
    72  	if err != nil {
    73  		return errors.Wrap(err, "computing hash")
    74  	}
    75  	return nil
    76  }
    77  
    78  func (w hashWriter) done() string {
    79  	return base64.URLEncoding.EncodeToString(w.h.Sum(nil))
    80  }