Signed-off-by: Nishant Totla <nishanttotla@gmail.com>
Nishant Totla authored on 2017/05/19 09:10:49... | ... |
@@ -13,6 +13,7 @@ import ( |
13 | 13 |
"github.com/docker/docker/api/types" |
14 | 14 |
registrytypes "github.com/docker/docker/api/types/registry" |
15 | 15 |
"github.com/docker/docker/api/types/swarm" |
16 |
+ "github.com/opencontainers/go-digest" |
|
16 | 17 |
"github.com/opencontainers/image-spec/specs-go/v1" |
17 | 18 |
"golang.org/x/net/context" |
18 | 19 |
) |
... | ... |
@@ -121,3 +122,92 @@ func TestServiceCreateCompatiblePlatforms(t *testing.T) { |
121 | 121 |
t.Fatalf("expected `service_amd64`, got %s", r.ID) |
122 | 122 |
} |
123 | 123 |
} |
124 |
+ |
|
125 |
+func TestServiceCreateDigestPinning(t *testing.T) { |
|
126 |
+ dgst := "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" |
|
127 |
+ dgstAlt := "sha256:37ffbf3f7497c07584dc9637ffbf3f7497c0758c0537ffbf3f7497c0c88e2bb7" |
|
128 |
+ serviceCreateImage := "" |
|
129 |
+ pinByDigestTests := []struct { |
|
130 |
+ img string // input image provided by the user |
|
131 |
+ expected string // expected image after digest pinning |
|
132 |
+ }{ |
|
133 |
+ // default registry returns familiar string |
|
134 |
+ {"docker.io/library/alpine", "alpine:latest@" + dgst}, |
|
135 |
+ // provided tag is preserved and digest added |
|
136 |
+ {"alpine:edge", "alpine:edge@" + dgst}, |
|
137 |
+ // image with provided alternative digest remains unchanged |
|
138 |
+ {"alpine@" + dgstAlt, "alpine@" + dgstAlt}, |
|
139 |
+ // image with provided tag and alternative digest remains unchanged |
|
140 |
+ {"alpine:edge@" + dgstAlt, "alpine:edge@" + dgstAlt}, |
|
141 |
+ // image on alternative registry does not result in familiar string |
|
142 |
+ {"alternate.registry/library/alpine", "alternate.registry/library/alpine:latest@" + dgst}, |
|
143 |
+ // unresolvable image does not get a digest |
|
144 |
+ {"cannotresolve", "cannotresolve:latest"}, |
|
145 |
+ } |
|
146 |
+ |
|
147 |
+ client := &Client{ |
|
148 |
+ client: newMockClient(func(req *http.Request) (*http.Response, error) { |
|
149 |
+ if strings.HasPrefix(req.URL.Path, "/services/create") { |
|
150 |
+ // reset and set image received by the service create endpoint |
|
151 |
+ serviceCreateImage = "" |
|
152 |
+ var service swarm.ServiceSpec |
|
153 |
+ if err := json.NewDecoder(req.Body).Decode(&service); err != nil { |
|
154 |
+ return nil, fmt.Errorf("could not parse service create request") |
|
155 |
+ } |
|
156 |
+ serviceCreateImage = service.TaskTemplate.ContainerSpec.Image |
|
157 |
+ |
|
158 |
+ b, err := json.Marshal(types.ServiceCreateResponse{ |
|
159 |
+ ID: "service_id", |
|
160 |
+ }) |
|
161 |
+ if err != nil { |
|
162 |
+ return nil, err |
|
163 |
+ } |
|
164 |
+ return &http.Response{ |
|
165 |
+ StatusCode: http.StatusOK, |
|
166 |
+ Body: ioutil.NopCloser(bytes.NewReader(b)), |
|
167 |
+ }, nil |
|
168 |
+ } else if strings.HasPrefix(req.URL.Path, "/distribution/cannotresolve") { |
|
169 |
+ // unresolvable image |
|
170 |
+ return nil, fmt.Errorf("cannot resolve image") |
|
171 |
+ } else if strings.HasPrefix(req.URL.Path, "/distribution/") { |
|
172 |
+ // resolvable images |
|
173 |
+ b, err := json.Marshal(registrytypes.DistributionInspect{ |
|
174 |
+ Descriptor: v1.Descriptor{ |
|
175 |
+ Digest: digest.Digest(dgst), |
|
176 |
+ }, |
|
177 |
+ }) |
|
178 |
+ if err != nil { |
|
179 |
+ return nil, err |
|
180 |
+ } |
|
181 |
+ return &http.Response{ |
|
182 |
+ StatusCode: http.StatusOK, |
|
183 |
+ Body: ioutil.NopCloser(bytes.NewReader(b)), |
|
184 |
+ }, nil |
|
185 |
+ } |
|
186 |
+ return nil, fmt.Errorf("unexpected URL '%s'", req.URL.Path) |
|
187 |
+ }), |
|
188 |
+ } |
|
189 |
+ |
|
190 |
+ // run pin by digest tests |
|
191 |
+ for _, p := range pinByDigestTests { |
|
192 |
+ r, err := client.ServiceCreate(context.Background(), swarm.ServiceSpec{ |
|
193 |
+ TaskTemplate: swarm.TaskSpec{ |
|
194 |
+ ContainerSpec: swarm.ContainerSpec{ |
|
195 |
+ Image: p.img, |
|
196 |
+ }, |
|
197 |
+ }, |
|
198 |
+ }, types.ServiceCreateOptions{QueryRegistry: true}) |
|
199 |
+ |
|
200 |
+ if err != nil { |
|
201 |
+ t.Fatal(err) |
|
202 |
+ } |
|
203 |
+ |
|
204 |
+ if r.ID != "service_id" { |
|
205 |
+ t.Fatalf("expected `service_id`, got %s", r.ID) |
|
206 |
+ } |
|
207 |
+ |
|
208 |
+ if p.expected != serviceCreateImage { |
|
209 |
+ t.Fatalf("expected image %s, got %s", p.expected, serviceCreateImage) |
|
210 |
+ } |
|
211 |
+ } |
|
212 |
+} |