@@ -49,106 +49,132 @@ type inputNormalized struct {
49
49
Do func (ctx context.Context , dstRef registry.Reference ) (ociregistry.Descriptor , error ) `json:"-"`
50
50
}
51
51
52
- func NormalizeInput (raw inputRaw ) (inputNormalized , error ) {
53
- var normal inputNormalized
54
-
55
- switch raw .Type {
56
- case "" :
57
- // TODO is there one of the two types that I might push by hand more often than the other that could be the default when this is unspecified?
58
- return normal , fmt .Errorf ("missing type" )
59
-
60
- case typeManifest , typeBlob :
61
- normal .Type = raw .Type
62
-
63
- default :
64
- return normal , fmt .Errorf ("unknown type: %s" , raw .Type )
65
- }
66
-
67
- if raw .Refs == nil {
68
- return normal , fmt .Errorf ("missing refs entirely (JSON input glitch?)" )
69
- }
70
- if len (raw .Refs ) == 0 {
71
- return normal , fmt .Errorf ("zero refs specified for pushing (need at least one)" )
72
- }
73
- normal .Refs = make ([]registry.Reference , len (raw .Refs ))
74
- var refsDigest ociregistry.Digest // if any ref has a digest, they all have to have the same digest (and our data has to match)
75
- for i , refString := range raw .Refs {
52
+ func normalizeInputRefs (deployType deployType , rawRefs []string ) ([]registry.Reference , ociregistry.Digest , error ) {
53
+ refs := make ([]registry.Reference , len (rawRefs ))
54
+ var commonDigest ociregistry.Digest // if any ref has a digest, they all have to have the same digest (and our data has to match)
55
+ for i , refString := range rawRefs {
76
56
ref , err := registry .ParseRef (refString )
77
57
if err != nil {
78
- return normal , fmt .Errorf ("%s: failed to parse ref: %w" , refString , err )
58
+ return nil , "" , fmt .Errorf ("%s: failed to parse ref: %w" , refString , err )
79
59
}
80
60
81
61
if ref .Digest != "" {
82
- if refsDigest == "" {
83
- refsDigest = ref .Digest
84
- } else if ref .Digest != refsDigest {
85
- return normal , fmt .Errorf ("refs digest mismatch in %s: %s" , ref , refsDigest )
62
+ if commonDigest == "" {
63
+ commonDigest = ref .Digest
64
+ } else if ref .Digest != commonDigest {
65
+ return nil , "" , fmt .Errorf ("refs digest mismatch in %s: %s" , ref , commonDigest )
86
66
}
87
67
}
88
68
89
- if normal . Type == typeBlob && ref .Tag != "" {
90
- return normal , fmt .Errorf ("cannot push blobs to a tag: %s" , ref )
69
+ if deployType == typeBlob && ref .Tag != "" {
70
+ return nil , "" , fmt .Errorf ("cannot push blobs to a tag: %s" , ref )
91
71
}
92
72
93
- normal . Refs [i ] = ref
73
+ refs [i ] = ref
94
74
}
95
- debugId := normal .Refs [0 ]
96
75
97
- normal .Lookup = make (map [ociregistry.Digest ]registry.Reference , len (raw .Lookup ))
98
- var lookupDigest ociregistry.Digest // if we store this out here, we can abuse it later to get the "last" lookup digest (for getting the single key in the case of len(lookup) == 1 without a new loop)
99
- for d , refString := range raw .Lookup {
100
- lookupDigest = ociregistry .Digest (d )
101
- if lookupDigest != "" {
76
+ return refs , commonDigest , nil
77
+ }
78
+
79
+ func normalizeInputLookup (rawLookup map [string ]string ) (map [ociregistry.Digest ]registry.Reference , * ociregistry.Digest , error ) {
80
+ lookup := make (map [ociregistry.Digest ]registry.Reference , len (rawLookup ))
81
+ var digest ociregistry.Digest // if we store this out here, we can abuse it later to get the "last" lookup digest (for getting the single key in the case of len(lookup) == 1 without a new loop)
82
+ for d , refString := range rawLookup {
83
+ digest = ociregistry .Digest (d )
84
+ if digest != "" {
102
85
// normal.Lookup[""] is a special case for fallback (where to look for any child object that isn't explicitly referenced)
103
- if err := lookupDigest .Validate (); err != nil {
104
- return normal , fmt .Errorf ("%s: lookup key %q invalid: %w" , debugId , lookupDigest , err )
86
+ if err := digest .Validate (); err != nil {
87
+ return nil , nil , fmt .Errorf ("lookup key %q invalid: %w" , digest , err )
105
88
}
106
89
}
107
90
if ref , err := registry .ParseRef (refString ); err != nil {
108
- return normal , fmt .Errorf ("%s: failed to parse lookup ref %q: %v" , debugId , refString , err )
91
+ return nil , nil , fmt .Errorf ("failed to parse lookup ref %q: %v" , refString , err )
109
92
} else {
110
- if ref .Tag != "" && lookupDigest != "" {
93
+ if ref .Tag != "" && digest != "" {
111
94
//return normal, fmt.Errorf("%s: tag on by-digest lookup ref makes no sense: %s (%s)", debugId, ref, d)
112
95
}
113
96
114
- if ref .Digest == "" && lookupDigest != "" {
115
- ref .Digest = lookupDigest
97
+ if ref .Digest == "" && digest != "" {
98
+ ref .Digest = digest
116
99
}
117
- if ref . Digest != lookupDigest && lookupDigest != "" {
118
- return normal , fmt .Errorf ("%s: digest on lookup ref should either be omitted or match key: %s vs %s" , debugId , ref , d )
100
+ if digest != "" && ref . Digest != digest {
101
+ return nil , nil , fmt .Errorf ("digest on lookup ref should either be omitted or match key: %s vs %s" , ref , d )
119
102
}
120
103
121
- normal . Lookup [ lookupDigest ] = ref
104
+ lookup [ digest ] = ref
122
105
}
123
106
}
124
107
108
+ // see notes on "digest" definition
109
+ if len (lookup ) != 1 {
110
+ return lookup , nil , nil
111
+ }
112
+ if digest == "" && (lookup ["" ].Digest == "" && lookup ["" ].Tag == "" ) {
113
+ // if it was a fallback, it needs at least Tag or Digest (or our refs need Digest, so we can infer)
114
+ return lookup , nil , nil
115
+ }
116
+ return lookup , & digest , nil
117
+ }
118
+
119
+ func NormalizeInput (raw inputRaw ) (inputNormalized , error ) {
120
+ var normal inputNormalized
121
+
122
+ switch raw .Type {
123
+ case "" :
124
+ // TODO is there one of the two types that I might push by hand more often than the other that could be the default when this is unspecified?
125
+ return normal , fmt .Errorf ("missing type" )
126
+
127
+ case typeManifest , typeBlob :
128
+ normal .Type = raw .Type
129
+
130
+ default :
131
+ return normal , fmt .Errorf ("unknown type: %s" , raw .Type )
132
+ }
133
+
134
+ if raw .Refs == nil {
135
+ return normal , fmt .Errorf ("missing refs entirely (JSON input glitch?)" )
136
+ }
137
+ if len (raw .Refs ) == 0 {
138
+ return normal , fmt .Errorf ("zero refs specified for pushing (need at least one)" )
139
+ }
140
+ var (
141
+ refsDigest ociregistry.Digest
142
+ err error
143
+ )
144
+ normal .Refs , refsDigest , err = normalizeInputRefs (normal .Type , raw .Refs )
145
+ if err != nil {
146
+ return normal , err
147
+ }
148
+
149
+ debugId := normal .Refs [0 ] // used for annotating errors from here out
150
+ var lookupDigest * ociregistry.Digest
151
+ normal .Lookup , lookupDigest , err = normalizeInputLookup (raw .Lookup )
152
+ if err != nil {
153
+ return normal , fmt .Errorf ("%s: %w" , debugId , err )
154
+ }
155
+
125
156
if raw .Data == nil || bytes .Equal (raw .Data , []byte ("null" )) {
126
157
// if we have no Data, let's see if we have enough information to infer an object to copy
127
158
if lookupRef , ok := normal .Lookup [refsDigest ]; refsDigest != "" && ok {
128
159
// if any of our Refs had a digest, *and* we have a way to Lookup that digest, that's the one
129
- lookupDigest = refsDigest
160
+ lookupDigest = & refsDigest
130
161
normal .CopyFrom = & lookupRef
131
- } else if lookupRef , ok := normal . Lookup [ lookupDigest ]; len ( normal . Lookup ) == 1 && ok {
162
+ } else if lookupDigest != nil {
132
163
// if we only had one Lookup entry, that's the one
133
- if lookupDigest == "" {
134
- // if it was a fallback, it needs at least Tag or Digest (or our refs need Digest, so we can infer)
135
- if lookupRef .Digest == "" && lookupRef .Tag == "" {
136
- if refsDigest != "" {
137
- lookupRef .Digest = refsDigest
138
- } else {
139
- return normal , fmt .Errorf ("%s: (single) fallback needs digest or tag: %s" , debugId , lookupRef )
140
- }
141
- }
142
- }
164
+ lookupRef := normal .Lookup [* lookupDigest ]
165
+ normal .CopyFrom = & lookupRef
166
+ } else if lookupRef , ok := normal .Lookup ["" ]; refsDigest != "" && ok {
167
+ lookupDigest = & refsDigest
168
+ lookupRef .Digest = refsDigest
143
169
normal .CopyFrom = & lookupRef
144
170
} else {
145
171
// if Lookup has only a single entry, that's the one (but that's our last chance for inferring intent)
146
- return normal , fmt .Errorf ("%s: missing data (and lookup is not a single item)" , debugId )
172
+ return normal , fmt .Errorf ("%s: missing data (and lookup is not a single item or fallback with digest or tag )" , debugId )
147
173
// TODO *technically* it would be fair to have lookup have two items if one of them is the fallback reference, but it doesn't really make much sense to copy an object from one namespace, but to get all its children from somewhere else
148
174
}
149
175
150
- if lookupDigest == "" && normal .CopyFrom .Digest != "" {
151
- lookupDigest = normal .CopyFrom .Digest
176
+ if * lookupDigest == "" && normal .CopyFrom .Digest != "" {
177
+ lookupDigest = & normal .CopyFrom .Digest
152
178
}
153
179
154
180
if _ , ok := normal .Lookup ["" ]; ! ok {
@@ -157,8 +183,8 @@ func NormalizeInput(raw inputRaw) (inputNormalized, error) {
157
183
}
158
184
159
185
if refsDigest == "" {
160
- refsDigest = lookupDigest
161
- } else if lookupDigest != "" && refsDigest != lookupDigest {
186
+ refsDigest = * lookupDigest
187
+ } else if * lookupDigest != "" && refsDigest != * lookupDigest {
162
188
return normal , fmt .Errorf ("%s: copy-by-digest mismatch: %s vs %s" , debugId , refsDigest , normal .CopyFrom )
163
189
}
164
190
} else {
0 commit comments