Skip to content

Commit

Permalink
Unmarshal with k8s.io/apimachinery/pkg/util/yaml
Browse files Browse the repository at this point in the history
- apimachinery yaml.Unmarshal fixes number types to int64 and float64
- Fix inaccurate error message in mutator
- Move YamlStringer to object pkg for reuse by other pkgs
- Fix jsonpath tests parsing y as a bool
- Fix kstatus example_test.go to actually be a test
  • Loading branch information
karlkfi committed Sep 22, 2021
1 parent 61a4552 commit a468a88
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 85 deletions.
30 changes: 7 additions & 23 deletions pkg/apply/mutator/apply_time_mutator.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import (
"k8s.io/klog/v2"
"sigs.k8s.io/cli-utils/pkg/apply/cache"
"sigs.k8s.io/cli-utils/pkg/jsonpath"
"sigs.k8s.io/cli-utils/pkg/object"
"sigs.k8s.io/cli-utils/pkg/object/mutation"
"sigs.k8s.io/yaml"
)

// ApplyTimeMutator mutates a resource by injecting values specified by the
Expand Down Expand Up @@ -52,10 +52,10 @@ func (atm *ApplyTimeMutator) Mutate(ctx context.Context, obj *unstructured.Unstr

subs, err := mutation.ReadAnnotation(obj)
if err != nil {
return mutated, reason, fmt.Errorf("failed to read jsonpath field in target resource (%s): %w", targetRef, err)
return mutated, reason, fmt.Errorf("failed to read annotation in resource (%s): %w", targetRef, err)
}

klog.V(4).Infof("target resource (%v):\n%s", targetRef, yamlStringer{obj})
klog.V(4).Infof("target resource (%s):\n%s", targetRef, object.YamlStringer{O: obj})

// validate no self-references
// Early validation to avoid GETs, but won't catch sources with implicit namespace.
Expand Down Expand Up @@ -91,7 +91,7 @@ func (atm *ApplyTimeMutator) Mutate(ctx context.Context, obj *unstructured.Unstr
return mutated, reason, fmt.Errorf("failed to get source resource (%s): %w", sourceRef, err)
}

klog.V(4).Infof("source resource (%s):\n%s", targetRef, yamlStringer{sourceObj})
klog.V(4).Infof("source resource (%s):\n%s", sourceRef, object.YamlStringer{O: sourceObj})

// lookup target field in target resource
targetValue, _, err := readFieldValue(obj, sub.TargetPath)
Expand Down Expand Up @@ -129,8 +129,8 @@ func (atm *ApplyTimeMutator) Mutate(ctx context.Context, obj *unstructured.Unstr
newValue = strings.ReplaceAll(targetValueString, sub.Token, sourceValueString)
}

klog.V(5).Infof("substitution on (%v): source=(%s), token=(%s), old=(%s), new=(%s)",
targetRef, sourceValue, sub.Token, targetValue, newValue)
klog.V(5).Infof("substitution: targetRef=(%s), sourceRef=(%s): sourceValue=(%v), token=(%s), oldTargetValue=(%v), newTargetValue=(%v)",
targetRef, sourceRef, sourceValue, sub.Token, targetValue, newValue)

// update target field in target resource
err = writeFieldValue(obj, sub.TargetPath, newValue)
Expand All @@ -143,7 +143,7 @@ func (atm *ApplyTimeMutator) Mutate(ctx context.Context, obj *unstructured.Unstr
}

if mutated {
klog.V(4).Infof("mutated target resource (%s):\n%s", targetRef, yamlStringer{obj})
klog.V(4).Infof("mutated target resource (%s):\n%s", targetRef, object.YamlStringer{O: obj})
}

return mutated, reason, nil
Expand Down Expand Up @@ -245,19 +245,3 @@ func valueToString(value interface{}) (string, error) {
}
return valueString, nil
}

// yamlStringer delays YAML marshalling for logging until String() is called.
type yamlStringer struct {
obj *unstructured.Unstructured
}

// String marshals the wrapped object to a YAML string. If serializing errors,
// the error string will be returned instead. This is primarily for use with
// verbose multi-line logging.
func (ys yamlStringer) String() string {
yamlBytes, err := yaml.Marshal(ys.obj.Object)
if err != nil {
return fmt.Sprintf("failed to serialize as yaml: %s", err)
}
return string(yamlBytes)
}
2 changes: 1 addition & 1 deletion pkg/apply/mutator/apply_time_mutator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ func TestMutate(t *testing.T) {
mutated: false,
reason: "",
// exact error message isn't very important. Feel free to update if the error text changes.
errMsg: `failed to read jsonpath field in target resource (v1/namespaces/map-namespace/ConfigMap/map3-name): ` +
errMsg: `failed to read annotation in resource (v1/namespaces/map-namespace/ConfigMap/map3-name): ` +
`failed to parse apply-time-mutation annotation: "not a valid substitution list": ` +
`error unmarshaling JSON: ` +
`while decoding JSON: ` +
Expand Down
4 changes: 2 additions & 2 deletions pkg/jsonpath/jsonpath_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ entries:
- name: a
value: x
- name: b
value: y
value: "y"
- name: c
value: z
`
Expand Down Expand Up @@ -65,7 +65,7 @@ entries:
- name: a
value: x
- name: b
value: y
value: "y"
- name: c
value: z
`
Expand Down
2 changes: 1 addition & 1 deletion pkg/kstatus/polling/testutil/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"context"
"testing"

"gopkg.in/yaml.v3"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/controller-runtime/pkg/client"
)

Expand Down
101 changes: 44 additions & 57 deletions pkg/kstatus/status/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
package status_test

import (
"fmt"
"log"
"strings"
"testing"

"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
. "sigs.k8s.io/cli-utils/pkg/kstatus/status"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/cli-utils/pkg/kstatus/polling/testutil"
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
"sigs.k8s.io/yaml"
)

func ExampleCompute() {
func TestExampleCompute(t *testing.T) {
deploymentManifest := `
apiVersion: apps/v1
kind: Deployment
Expand All @@ -27,24 +28,21 @@ status:
availableReplicas: 1
replicas: 1
conditions:
- type: Progressing
- type: Progressing
status: "True"
reason: NewReplicaSetAvailable
- type: Available
- type: Available
status: "True"
`
deployment := yamlManifestToUnstructured(deploymentManifest)
deployment := testutil.YamlToUnstructured(t, deploymentManifest)

res, err := Compute(deployment)
if err != nil {
log.Fatal(err)
}
fmt.Println(res.Status)
// Output:
// Current
res, err := status.Compute(deployment)
assert.NoError(t, err)

assert.Equal(t, status.Status("Current"), res.Status)
}

func ExampleAugment() {
func TestExampleAugment(t *testing.T) {
deploymentManifest := `
apiVersion: apps/v1
kind: Deployment
Expand All @@ -59,52 +57,41 @@ status:
availableReplicas: 1
replicas: 1
conditions:
- type: Progressing
- type: Progressing
status: "True"
reason: NewReplicaSetAvailable
- type: Available
- type: Available
status: "True"
`
deployment := yamlManifestToUnstructured(deploymentManifest)
deployment := testutil.YamlToUnstructured(t, deploymentManifest)

err := status.Augment(deployment)
assert.NoError(t, err)

err := Augment(deployment)
if err != nil {
log.Fatal(err)
}
b, err := yaml.Marshal(deployment.Object)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
// Output:
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// generation: 1
// name: test
// namespace: qual
// status:
// availableReplicas: 1
// conditions:
// - reason: NewReplicaSetAvailable
// status: "True"
// type: Progressing
// - status: "True"
// type: Available
// observedGeneration: 1
// readyReplicas: 1
// replicas: 1
// updatedReplicas: 1
}
assert.NoError(t, err)

receivedManifest := strings.TrimSpace(string(b))
expectedManifest := strings.TrimSpace(`
apiVersion: apps/v1
kind: Deployment
metadata:
generation: 1
name: test
namespace: qual
status:
availableReplicas: 1
conditions:
- reason: NewReplicaSetAvailable
status: "True"
type: Progressing
- status: "True"
type: Available
observedGeneration: 1
readyReplicas: 1
replicas: 1
updatedReplicas: 1
`)

func yamlManifestToUnstructured(manifest string) *unstructured.Unstructured {
jsonManifest, err := yaml.YAMLToJSON([]byte(manifest))
if err != nil {
log.Fatal(err)
}
resource, _, err := unstructured.UnstructuredJSONScheme.Decode(jsonManifest, nil, nil)
if err != nil {
log.Fatal(err)
}
return resource.(*unstructured.Unstructured)
assert.Equal(t, expectedManifest, receivedManifest)
}
35 changes: 35 additions & 0 deletions pkg/object/strings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package object

import (
"fmt"

"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubectl/pkg/scheme"
"sigs.k8s.io/yaml"
)

var codec = scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)

// YamlStringer delays YAML marshalling for logging until String() is called.
type YamlStringer struct {
O runtime.Object
}

// String marshals the wrapped object to a YAML string. If serializing errors,
// the error string will be returned instead. This is primarily for use with
// verbose logging.
func (ys YamlStringer) String() string {
jsonBytes, err := runtime.Encode(unstructured.NewJSONFallbackEncoder(codec), ys.O)
if err != nil {
return fmt.Sprintf("<<failed to serialize as json: %s>>", err)
}
yamlBytes, err := yaml.JSONToYAML(jsonBytes)
if err != nil {
return fmt.Sprintf("<<failed to convert from json to yaml: %s>>", err)
}
return string(yamlBytes)
}
2 changes: 1 addition & 1 deletion test/e2e/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/cli-utils/pkg/apply/event"
"sigs.k8s.io/cli-utils/pkg/common"
"sigs.k8s.io/kustomize/kyaml/yaml"
)

func randomString(prefix string) string {
Expand Down

0 comments on commit a468a88

Please sign in to comment.