2016-09-06 18:46:37 +00:00
package client
import (
"encoding/json"
2017-04-05 22:43:17 +00:00
"fmt"
2016-09-06 18:46:37 +00:00
2017-04-05 22:43:17 +00:00
"github.com/docker/distribution/reference"
2016-09-06 18:46:37 +00:00
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
2017-04-05 22:43:17 +00:00
"github.com/opencontainers/go-digest"
2016-09-06 18:46:37 +00:00
"golang.org/x/net/context"
)
// ServiceCreate creates a new Service.
func ( cli * Client ) ServiceCreate ( ctx context . Context , service swarm . ServiceSpec , options types . ServiceCreateOptions ) ( types . ServiceCreateResponse , error ) {
2017-04-05 22:43:17 +00:00
var (
headers map [ string ] [ ] string
distErr error
)
2016-09-06 18:46:37 +00:00
if options . EncodedRegistryAuth != "" {
headers = map [ string ] [ ] string {
"X-Registry-Auth" : { options . EncodedRegistryAuth } ,
}
}
2017-04-05 22:43:17 +00:00
// Contact the registry to retrieve digest and platform information
if options . QueryRegistry {
distributionInspect , err := cli . DistributionInspect ( ctx , service . TaskTemplate . ContainerSpec . Image , options . EncodedRegistryAuth )
distErr = err
if err == nil {
// now pin by digest if the image doesn't already contain a digest
img := imageWithDigestString ( service . TaskTemplate . ContainerSpec . Image , distributionInspect . Descriptor . Digest )
if img != "" {
service . TaskTemplate . ContainerSpec . Image = img
}
}
}
2016-09-06 18:46:37 +00:00
var response types . ServiceCreateResponse
resp , err := cli . post ( ctx , "/services/create" , nil , service , headers )
if err != nil {
return response , err
}
err = json . NewDecoder ( resp . body ) . Decode ( & response )
2017-04-05 22:43:17 +00:00
if distErr != nil {
response . Warnings = append ( response . Warnings , digestWarning ( service . TaskTemplate . ContainerSpec . Image ) )
}
2016-09-06 18:46:37 +00:00
ensureReaderClosed ( resp )
return response , err
}
2017-04-05 22:43:17 +00:00
// imageWithDigestString takes an image string and a digest, and updates
// the image string if it didn't originally contain a digest. It assumes
// that the image string is not an image ID
func imageWithDigestString ( image string , dgst digest . Digest ) string {
isCanonical := false
ref , err := reference . ParseAnyReference ( image )
if err == nil {
_ , isCanonical = ref . ( reference . Canonical )
if ! isCanonical {
namedRef , _ := ref . ( reference . Named )
img , err := reference . WithDigest ( namedRef , dgst )
if err == nil {
return img . String ( )
}
}
}
return ""
}
// digestWarning constructs a formatted warning string using the
// image name that could not be pinned by digest. The formatting
// is hardcoded, but could me made smarter in the future
func digestWarning ( image string ) string {
return fmt . Sprintf ( "image %s could not be accessed on a registry to record\nits digest. Each node will access %s independently,\npossibly leading to different nodes running different\nversions of the image.\n" , image , image )
}