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