package controller import ( "crypto/sha256" "encoding/hex" "encoding/json" crawlv1alpha1 "gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/api/v1alpha1" ) // specHashInput is the explicit set of fields whose change requires // replacing the instance. Deliberately not ProxySpec wholesale: adding a // spec field that doesn't affect the VM (attributes, maxLeases, healthCheck) // must not churn the fleet on operator upgrade. type specHashInput struct { Placement *crawlv1alpha1.PlacementSpec `json:"placement,omitempty"` CloudInit string `json:"cloudInit,omitempty"` Port int32 `json:"port"` } // specHash returns the hex SHA-256 of the replacement-triggering spec // fields. cloudInit is the already-resolved content, so rotating a // referenced Secret changes the hash even though the spec is untouched. func specHash(p *crawlv1alpha1.Proxy, cloudInit string) string { in := specHashInput{ Placement: p.Spec.Placement, CloudInit: cloudInit, Port: p.EffectivePort(), } // nil and empty placement mean the same thing; hash them identically. if in.Placement != nil && *in.Placement == (crawlv1alpha1.PlacementSpec{}) { in.Placement = nil } b, err := json.Marshal(in) if err != nil { // A struct of strings and an int32 cannot fail to marshal. panic(err) } sum := sha256.Sum256(b) return hex.EncodeToString(sum[:]) }