1// Copyright 2016 go-dockerclient authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package docker 6 7import ( 8 "context" 9 "encoding/json" 10 "errors" 11 "net/http" 12 "net/url" 13 "strconv" 14 15 "github.com/docker/docker/api/types/swarm" 16) 17 18var ( 19 // ErrNodeAlreadyInSwarm is the error returned by InitSwarm and JoinSwarm 20 // when the node is already part of a Swarm. 21 ErrNodeAlreadyInSwarm = errors.New("node already in a Swarm") 22 23 // ErrNodeNotInSwarm is the error returned by LeaveSwarm and UpdateSwarm 24 // when the node is not part of a Swarm. 25 ErrNodeNotInSwarm = errors.New("node is not in a Swarm") 26) 27 28// InitSwarmOptions specify parameters to the InitSwarm function. 29// See https://goo.gl/hzkgWu for more details. 30type InitSwarmOptions struct { 31 swarm.InitRequest 32 Context context.Context 33} 34 35// InitSwarm initializes a new Swarm and returns the node ID. 36// See https://goo.gl/ZWyG1M for more details. 37func (c *Client) InitSwarm(opts InitSwarmOptions) (string, error) { 38 path := "/swarm/init" 39 resp, err := c.do("POST", path, doOptions{ 40 data: opts.InitRequest, 41 forceJSON: true, 42 context: opts.Context, 43 }) 44 if err != nil { 45 if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { 46 return "", ErrNodeAlreadyInSwarm 47 } 48 return "", err 49 } 50 defer resp.Body.Close() 51 var response string 52 if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { 53 return "", err 54 } 55 return response, nil 56} 57 58// JoinSwarmOptions specify parameters to the JoinSwarm function. 59// See https://goo.gl/TdhJWU for more details. 60type JoinSwarmOptions struct { 61 swarm.JoinRequest 62 Context context.Context 63} 64 65// JoinSwarm joins an existing Swarm. 66// See https://goo.gl/N59IP1 for more details. 67func (c *Client) JoinSwarm(opts JoinSwarmOptions) error { 68 path := "/swarm/join" 69 resp, err := c.do("POST", path, doOptions{ 70 data: opts.JoinRequest, 71 forceJSON: true, 72 context: opts.Context, 73 }) 74 if err != nil { 75 if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { 76 return ErrNodeAlreadyInSwarm 77 } 78 } 79 resp.Body.Close() 80 return err 81} 82 83// LeaveSwarmOptions specify parameters to the LeaveSwarm function. 84// See https://goo.gl/UWDlLg for more details. 85type LeaveSwarmOptions struct { 86 Force bool 87 Context context.Context 88} 89 90// LeaveSwarm leaves a Swarm. 91// See https://goo.gl/FTX1aD for more details. 92func (c *Client) LeaveSwarm(opts LeaveSwarmOptions) error { 93 params := make(url.Values) 94 params.Set("force", strconv.FormatBool(opts.Force)) 95 path := "/swarm/leave?" + params.Encode() 96 resp, err := c.do("POST", path, doOptions{ 97 context: opts.Context, 98 }) 99 if err != nil { 100 if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { 101 return ErrNodeNotInSwarm 102 } 103 } 104 resp.Body.Close() 105 return err 106} 107 108// UpdateSwarmOptions specify parameters to the UpdateSwarm function. 109// See https://goo.gl/vFbq36 for more details. 110type UpdateSwarmOptions struct { 111 Version int 112 RotateWorkerToken bool 113 RotateManagerToken bool 114 Swarm swarm.Spec 115 Context context.Context 116} 117 118// UpdateSwarm updates a Swarm. 119// See https://goo.gl/iJFnsw for more details. 120func (c *Client) UpdateSwarm(opts UpdateSwarmOptions) error { 121 params := make(url.Values) 122 params.Set("version", strconv.Itoa(opts.Version)) 123 params.Set("rotateWorkerToken", strconv.FormatBool(opts.RotateWorkerToken)) 124 params.Set("rotateManagerToken", strconv.FormatBool(opts.RotateManagerToken)) 125 path := "/swarm/update?" + params.Encode() 126 resp, err := c.do("POST", path, doOptions{ 127 data: opts.Swarm, 128 forceJSON: true, 129 context: opts.Context, 130 }) 131 if err != nil { 132 if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { 133 return ErrNodeNotInSwarm 134 } 135 } 136 resp.Body.Close() 137 return err 138} 139 140// InspectSwarm inspects a Swarm. 141// See https://goo.gl/MFwgX9 for more details. 142func (c *Client) InspectSwarm(ctx context.Context) (swarm.Swarm, error) { 143 response := swarm.Swarm{} 144 resp, err := c.do("GET", "/swarm", doOptions{ 145 context: ctx, 146 }) 147 if err != nil { 148 if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { 149 return response, ErrNodeNotInSwarm 150 } 151 return response, err 152 } 153 defer resp.Body.Close() 154 err = json.NewDecoder(resp.Body).Decode(&response) 155 return response, err 156} 157