Files
2026-06-18 10:13:42 -04:00

170 lines
5.3 KiB
Go

package v1alpha1
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/yaml"
)
// Set the ValuesObject property to the json representation of the yaml contained in value
// Remove Values property if present
func (h *ApplicationSourceHelm) SetValuesString(value string) error {
if value == "" {
h.ValuesObject = nil
h.Values = ""
} else {
data, err := yaml.YAMLToJSON([]byte(value))
if err != nil {
return fmt.Errorf("failed converting yaml to json: %w", err)
}
var v any
if err := json.Unmarshal(data, &v); err != nil {
return fmt.Errorf("failed to unmarshal json: %w", err)
}
switch v.(type) {
case string:
case map[string]any:
default:
return fmt.Errorf("invalid type %q", reflect.TypeOf(v))
}
h.ValuesObject = &runtime.RawExtension{Raw: data}
h.Values = ""
}
return nil
}
// Equals reports whether h and other are semantically equal. ValuesObject is compared by
// marshaling both sides to canonical JSON so that HTML-escaping differences (e.g. '&' vs
// '&') that arise between the Kubernetes API server and encoding/json do not cause
// a spurious inequality.
func (h *ApplicationSourceHelm) Equals(other *ApplicationSourceHelm) bool {
if h == nil && other == nil {
return true
}
if h == nil || other == nil {
return false
}
// Compare ValuesObject: skip the JSON normalisation round-trip when raw bytes are
// already identical. Otherwise normalise both to canonical JSON to absorb
// HTML-escaping differences (e.g. '&' vs '&').
var hRaw, otherRaw []byte
if h.ValuesObject != nil {
hRaw = h.ValuesObject.Raw
}
if other.ValuesObject != nil {
otherRaw = other.ValuesObject.Raw
}
if !bytes.Equal(hRaw, otherRaw) &&
!bytes.Equal(canonicalValuesJSON(h.ValuesObject), canonicalValuesJSON(other.ValuesObject)) {
return false
}
hCopy, otherCopy := h.DeepCopy(), other.DeepCopy()
hCopy.ValuesObject = nil
otherCopy.ValuesObject = nil
return reflect.DeepEqual(hCopy, otherCopy)
}
// canonicalValuesJSON returns the canonical JSON encoding of a ValuesObject by round-tripping
// through json.Unmarshal + json.Marshal. Returns the original Raw bytes for invalid JSON,
// or nil if the extension is absent.
func canonicalValuesJSON(ext *runtime.RawExtension) []byte {
if ext == nil || ext.Raw == nil {
return nil
}
var v any
if err := json.Unmarshal(ext.Raw, &v); err != nil {
return ext.Raw
}
b, err := json.Marshal(v)
if err != nil {
return ext.Raw
}
return b
}
func (h *ApplicationSourceHelm) ValuesYAML() []byte {
if h.ValuesObject == nil || h.ValuesObject.Raw == nil {
return []byte(h.Values)
}
b, err := yaml.JSONToYAML(h.ValuesObject.Raw)
if err != nil {
// This should be impossible, because rawValue isn't set directly.
return []byte{}
}
return b
}
func (h *ApplicationSourceHelm) ValuesIsEmpty() bool {
return len(h.ValuesYAML()) == 0
}
// String returns a human-readable representation of ApplicationSourceHelm.
// It replaces the suppressed auto-generated stringer so that
// ValuesObject is rendered as YAML rather than as a raw byte-array
// See https://github.com/argoproj/argo-cd/issues/18342
//
// The format mirrors what gogo-proto produces for every other field, so the
// only observable difference is the ValuesObject rendering. If new fields are
// added to ApplicationSourceHelm, they must be added here as well.
func (h *ApplicationSourceHelm) String() string {
if h == nil {
return "nil"
}
var parametersBuilder strings.Builder
parametersBuilder.WriteString("[]HelmParameter{")
for _, f := range h.Parameters {
parametersBuilder.WriteString(strings.Replace(f.String(), `&`, ``, 1))
parametersBuilder.WriteString(",")
}
parametersBuilder.WriteString("}")
var fileParametersBuilder strings.Builder
fileParametersBuilder.WriteString("[]HelmFileParameter{")
for _, f := range h.FileParameters {
fileParametersBuilder.WriteString(strings.Replace(f.String(), `&`, ``, 1))
fileParametersBuilder.WriteString(",")
}
fileParametersBuilder.WriteString("}")
valuesObjectStr := "nil"
if h.ValuesObject != nil {
// Render the JSON-encoded RawExtension as YAML instead of the
// default %v formatting of []byte (which produces "[123 34 ...]").
valuesObjectStr = "&runtime.RawExtension{" + h.ValuesString() + "}"
}
return strings.Join([]string{
`&ApplicationSourceHelm{`,
`ValueFiles:` + fmt.Sprintf("%v", h.ValueFiles) + `,`,
`Parameters:` + parametersBuilder.String() + `,`,
`ReleaseName:` + h.ReleaseName + `,`,
`Values:` + h.Values + `,`,
`FileParameters:` + fileParametersBuilder.String() + `,`,
`Version:` + h.Version + `,`,
`PassCredentials:` + strconv.FormatBool(h.PassCredentials) + `,`,
`IgnoreMissingValueFiles:` + strconv.FormatBool(h.IgnoreMissingValueFiles) + `,`,
`SkipCrds:` + strconv.FormatBool(h.SkipCrds) + `,`,
`ValuesObject:` + valuesObjectStr + `,`,
`Namespace:` + h.Namespace + `,`,
`KubeVersion:` + h.KubeVersion + `,`,
`APIVersions:` + fmt.Sprintf("%v", h.APIVersions) + `,`,
`SkipTests:` + strconv.FormatBool(h.SkipTests) + `,`,
`SkipSchemaValidation:` + strconv.FormatBool(h.SkipSchemaValidation) + `,`,
`}`,
}, "")
}
func (h *ApplicationSourceHelm) ValuesString() string {
if h.ValuesObject == nil || h.ValuesObject.Raw == nil {
return h.Values
}
return strings.TrimSuffix(string(h.ValuesYAML()), "\n")
}