github.com/LazyboyChen7/engine@v17.12.1-ce-rc2+incompatible/client/service_create_test.go (about) 1 package client 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "net/http" 9 "strings" 10 "testing" 11 12 "github.com/docker/docker/api/types" 13 registrytypes "github.com/docker/docker/api/types/registry" 14 "github.com/docker/docker/api/types/swarm" 15 "github.com/opencontainers/go-digest" 16 "github.com/opencontainers/image-spec/specs-go/v1" 17 "github.com/stretchr/testify/assert" 18 "golang.org/x/net/context" 19 ) 20 21 func TestServiceCreateError(t *testing.T) { 22 client := &Client{ 23 client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), 24 } 25 _, err := client.ServiceCreate(context.Background(), swarm.ServiceSpec{}, types.ServiceCreateOptions{}) 26 if err == nil || err.Error() != "Error response from daemon: Server error" { 27 t.Fatalf("expected a Server Error, got %v", err) 28 } 29 } 30 31 func TestServiceCreate(t *testing.T) { 32 expectedURL := "/services/create" 33 client := &Client{ 34 client: newMockClient(func(req *http.Request) (*http.Response, error) { 35 if !strings.HasPrefix(req.URL.Path, expectedURL) { 36 return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) 37 } 38 if req.Method != "POST" { 39 return nil, fmt.Errorf("expected POST method, got %s", req.Method) 40 } 41 b, err := json.Marshal(types.ServiceCreateResponse{ 42 ID: "service_id", 43 }) 44 if err != nil { 45 return nil, err 46 } 47 return &http.Response{ 48 StatusCode: http.StatusOK, 49 Body: ioutil.NopCloser(bytes.NewReader(b)), 50 }, nil 51 }), 52 } 53 54 r, err := client.ServiceCreate(context.Background(), swarm.ServiceSpec{}, types.ServiceCreateOptions{}) 55 if err != nil { 56 t.Fatal(err) 57 } 58 if r.ID != "service_id" { 59 t.Fatalf("expected `service_id`, got %s", r.ID) 60 } 61 } 62 63 func TestServiceCreateCompatiblePlatforms(t *testing.T) { 64 client := &Client{ 65 version: "1.30", 66 client: newMockClient(func(req *http.Request) (*http.Response, error) { 67 if strings.HasPrefix(req.URL.Path, "/v1.30/services/create") { 68 var serviceSpec swarm.ServiceSpec 69 70 // check if the /distribution endpoint returned correct output 71 err := json.NewDecoder(req.Body).Decode(&serviceSpec) 72 if err != nil { 73 return nil, err 74 } 75 76 assert.Equal(t, "foobar:1.0@sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96", serviceSpec.TaskTemplate.ContainerSpec.Image) 77 assert.Len(t, serviceSpec.TaskTemplate.Placement.Platforms, 1) 78 79 p := serviceSpec.TaskTemplate.Placement.Platforms[0] 80 b, err := json.Marshal(types.ServiceCreateResponse{ 81 ID: "service_" + p.OS + "_" + p.Architecture, 82 }) 83 if err != nil { 84 return nil, err 85 } 86 return &http.Response{ 87 StatusCode: http.StatusOK, 88 Body: ioutil.NopCloser(bytes.NewReader(b)), 89 }, nil 90 } else if strings.HasPrefix(req.URL.Path, "/v1.30/distribution/") { 91 b, err := json.Marshal(registrytypes.DistributionInspect{ 92 Descriptor: v1.Descriptor{ 93 Digest: "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96", 94 }, 95 Platforms: []v1.Platform{ 96 { 97 Architecture: "amd64", 98 OS: "linux", 99 }, 100 }, 101 }) 102 if err != nil { 103 return nil, err 104 } 105 return &http.Response{ 106 StatusCode: http.StatusOK, 107 Body: ioutil.NopCloser(bytes.NewReader(b)), 108 }, nil 109 } else { 110 return nil, fmt.Errorf("unexpected URL '%s'", req.URL.Path) 111 } 112 }), 113 } 114 115 spec := swarm.ServiceSpec{TaskTemplate: swarm.TaskSpec{ContainerSpec: &swarm.ContainerSpec{Image: "foobar:1.0"}}} 116 117 r, err := client.ServiceCreate(context.Background(), spec, types.ServiceCreateOptions{QueryRegistry: true}) 118 assert.NoError(t, err) 119 assert.Equal(t, "service_linux_amd64", r.ID) 120 } 121 122 func TestServiceCreateDigestPinning(t *testing.T) { 123 dgst := "sha256:c0537ff6a5218ef531ece93d4984efc99bbf3f7497c0a7726c88e2bb7584dc96" 124 dgstAlt := "sha256:37ffbf3f7497c07584dc9637ffbf3f7497c0758c0537ffbf3f7497c0c88e2bb7" 125 serviceCreateImage := "" 126 pinByDigestTests := []struct { 127 img string // input image provided by the user 128 expected string // expected image after digest pinning 129 }{ 130 // default registry returns familiar string 131 {"docker.io/library/alpine", "alpine:latest@" + dgst}, 132 // provided tag is preserved and digest added 133 {"alpine:edge", "alpine:edge@" + dgst}, 134 // image with provided alternative digest remains unchanged 135 {"alpine@" + dgstAlt, "alpine@" + dgstAlt}, 136 // image with provided tag and alternative digest remains unchanged 137 {"alpine:edge@" + dgstAlt, "alpine:edge@" + dgstAlt}, 138 // image on alternative registry does not result in familiar string 139 {"alternate.registry/library/alpine", "alternate.registry/library/alpine:latest@" + dgst}, 140 // unresolvable image does not get a digest 141 {"cannotresolve", "cannotresolve:latest"}, 142 } 143 144 client := &Client{ 145 version: "1.30", 146 client: newMockClient(func(req *http.Request) (*http.Response, error) { 147 if strings.HasPrefix(req.URL.Path, "/v1.30/services/create") { 148 // reset and set image received by the service create endpoint 149 serviceCreateImage = "" 150 var service swarm.ServiceSpec 151 if err := json.NewDecoder(req.Body).Decode(&service); err != nil { 152 return nil, fmt.Errorf("could not parse service create request") 153 } 154 serviceCreateImage = service.TaskTemplate.ContainerSpec.Image 155 156 b, err := json.Marshal(types.ServiceCreateResponse{ 157 ID: "service_id", 158 }) 159 if err != nil { 160 return nil, err 161 } 162 return &http.Response{ 163 StatusCode: http.StatusOK, 164 Body: ioutil.NopCloser(bytes.NewReader(b)), 165 }, nil 166 } else if strings.HasPrefix(req.URL.Path, "/v1.30/distribution/cannotresolve") { 167 // unresolvable image 168 return nil, fmt.Errorf("cannot resolve image") 169 } else if strings.HasPrefix(req.URL.Path, "/v1.30/distribution/") { 170 // resolvable images 171 b, err := json.Marshal(registrytypes.DistributionInspect{ 172 Descriptor: v1.Descriptor{ 173 Digest: digest.Digest(dgst), 174 }, 175 }) 176 if err != nil { 177 return nil, err 178 } 179 return &http.Response{ 180 StatusCode: http.StatusOK, 181 Body: ioutil.NopCloser(bytes.NewReader(b)), 182 }, nil 183 } 184 return nil, fmt.Errorf("unexpected URL '%s'", req.URL.Path) 185 }), 186 } 187 188 // run pin by digest tests 189 for _, p := range pinByDigestTests { 190 r, err := client.ServiceCreate(context.Background(), swarm.ServiceSpec{ 191 TaskTemplate: swarm.TaskSpec{ 192 ContainerSpec: &swarm.ContainerSpec{ 193 Image: p.img, 194 }, 195 }, 196 }, types.ServiceCreateOptions{QueryRegistry: true}) 197 198 if err != nil { 199 t.Fatal(err) 200 } 201 202 if r.ID != "service_id" { 203 t.Fatalf("expected `service_id`, got %s", r.ID) 204 } 205 206 if p.expected != serviceCreateImage { 207 t.Fatalf("expected image %s, got %s", p.expected, serviceCreateImage) 208 } 209 } 210 }