diff --git a/pkg/name/digest.go b/pkg/name/digest.go index c049c1ef4..28f6967ba 100644 --- a/pkg/name/digest.go +++ b/pkg/name/digest.go @@ -17,6 +17,7 @@ package name import ( // nolint: depguard _ "crypto/sha256" // Recommended by go-digest. + "encoding/json" "strings" "github.com/opencontainers/go-digest" @@ -59,6 +60,25 @@ func (d Digest) String() string { return d.original } +// MarshalJSON formats the digest into a string for JSON serialization. +func (d Digest) MarshalJSON() ([]byte, error) { + return json.Marshal(d.String()) +} + +// UnmarshalJSON parses a JSON string into a Digest. +func (d *Digest) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + n, err := NewDigest(s) + if err != nil { + return err + } + *d = n + return nil +} + // NewDigest returns a new Digest representing the given name. func NewDigest(name string, opts ...Option) (Digest, error) { // Split on "@" diff --git a/pkg/name/digest_test.go b/pkg/name/digest_test.go index 85775cccf..c8600dd0e 100644 --- a/pkg/name/digest_test.go +++ b/pkg/name/digest_test.go @@ -15,7 +15,9 @@ package name import ( + "encoding/json" "path" + "reflect" "strings" "testing" ) @@ -150,3 +152,58 @@ func TestDigestScopes(t *testing.T) { t.Errorf("scope was incorrect for %v. Wanted: `%s` Got: `%s`", digest, expectedScope, actualScope) } } + +func TestJSON(t *testing.T) { + t.Parallel() + digestNameStr := "gcr.io/project-id/image@" + validDigest + digest, err := NewDigest(digestNameStr, StrictValidation) + if err != nil { + t.Fatalf("`%s` should be a valid Digest name, got error: %v", digestNameStr, err) + } + + t.Run("string", func(t *testing.T) { + t.Parallel() + b, err := json.Marshal(digest) + if err != nil { + t.Fatalf("Marshal() failed: %v", err) + } + + if want := `"` + digestNameStr + `"`; want != string(b) { + t.Errorf("Marshal() was incorrect. Wanted: `%s` Got: `%s`", want, string(b)) + } + + var out Digest + if err := json.Unmarshal(b, &out); err != nil { + t.Fatalf("Unmarshal() failed: %v", err) + } + + if out.String() != digest.String() { + t.Errorf("Unmarshaled Digest should be the same as the original. Wanted: `%s` Got: `%s`", digest, out) + } + }) + + t.Run("map", func(t *testing.T) { + t.Parallel() + in := map[string]Digest{ + "a": digest, + } + b, err := json.Marshal(in) + if err != nil { + t.Fatalf("MarshalJSON() failed: %v", err) + } + + want := `{"a":"` + digestNameStr + `"}` + if want != string(b) { + t.Errorf("Marshal() was incorrect. Wanted: `%s` Got: `%s`", want, string(b)) + } + + var out map[string]Digest + if err := json.Unmarshal(b, &out); err != nil { + t.Fatalf("Unmarshal() failed: %v", err) + } + + if !reflect.DeepEqual(in, out) { + t.Errorf("Unmarshaled map should be the same as the original. Wanted: `%v` Got: `%v`", in, out) + } + }) +}