github.com/rish1988/moby@v25.0.2+incompatible/api/server/router/swarm/cluster_routes.go (about)

     1  package swarm // import "github.com/docker/docker/api/server/router/swarm"
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"strconv"
     8  
     9  	"github.com/containerd/log"
    10  	"github.com/docker/docker/api/server/httputils"
    11  	basictypes "github.com/docker/docker/api/types"
    12  	"github.com/docker/docker/api/types/backend"
    13  	"github.com/docker/docker/api/types/filters"
    14  	"github.com/docker/docker/api/types/registry"
    15  	types "github.com/docker/docker/api/types/swarm"
    16  	"github.com/docker/docker/api/types/versions"
    17  	"github.com/docker/docker/errdefs"
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  func (sr *swarmRouter) initCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    22  	var req types.InitRequest
    23  	if err := httputils.ReadJSON(r, &req); err != nil {
    24  		return err
    25  	}
    26  	version := httputils.VersionFromContext(ctx)
    27  
    28  	// DefaultAddrPool and SubnetSize were added in API 1.39. Ignore on older API versions.
    29  	if versions.LessThan(version, "1.39") {
    30  		req.DefaultAddrPool = nil
    31  		req.SubnetSize = 0
    32  	}
    33  	// DataPathPort was added in API 1.40. Ignore this option on older API versions.
    34  	if versions.LessThan(version, "1.40") {
    35  		req.DataPathPort = 0
    36  	}
    37  	nodeID, err := sr.backend.Init(req)
    38  	if err != nil {
    39  		log.G(ctx).WithContext(ctx).WithError(err).Debug("Error initializing swarm")
    40  		return err
    41  	}
    42  	return httputils.WriteJSON(w, http.StatusOK, nodeID)
    43  }
    44  
    45  func (sr *swarmRouter) joinCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    46  	var req types.JoinRequest
    47  	if err := httputils.ReadJSON(r, &req); err != nil {
    48  		return err
    49  	}
    50  	return sr.backend.Join(req)
    51  }
    52  
    53  func (sr *swarmRouter) leaveCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    54  	if err := httputils.ParseForm(r); err != nil {
    55  		return err
    56  	}
    57  
    58  	force := httputils.BoolValue(r, "force")
    59  	return sr.backend.Leave(ctx, force)
    60  }
    61  
    62  func (sr *swarmRouter) inspectCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    63  	swarm, err := sr.backend.Inspect()
    64  	if err != nil {
    65  		log.G(ctx).WithContext(ctx).WithError(err).Debug("Error getting swarm")
    66  		return err
    67  	}
    68  
    69  	return httputils.WriteJSON(w, http.StatusOK, swarm)
    70  }
    71  
    72  func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
    73  	var swarm types.Spec
    74  	if err := httputils.ReadJSON(r, &swarm); err != nil {
    75  		return err
    76  	}
    77  
    78  	rawVersion := r.URL.Query().Get("version")
    79  	version, err := strconv.ParseUint(rawVersion, 10, 64)
    80  	if err != nil {
    81  		err := fmt.Errorf("invalid swarm version '%s': %v", rawVersion, err)
    82  		return errdefs.InvalidParameter(err)
    83  	}
    84  
    85  	var flags types.UpdateFlags
    86  
    87  	if value := r.URL.Query().Get("rotateWorkerToken"); value != "" {
    88  		rot, err := strconv.ParseBool(value)
    89  		if err != nil {
    90  			err := fmt.Errorf("invalid value for rotateWorkerToken: %s", value)
    91  			return errdefs.InvalidParameter(err)
    92  		}
    93  
    94  		flags.RotateWorkerToken = rot
    95  	}
    96  
    97  	if value := r.URL.Query().Get("rotateManagerToken"); value != "" {
    98  		rot, err := strconv.ParseBool(value)
    99  		if err != nil {
   100  			err := fmt.Errorf("invalid value for rotateManagerToken: %s", value)
   101  			return errdefs.InvalidParameter(err)
   102  		}
   103  
   104  		flags.RotateManagerToken = rot
   105  	}
   106  
   107  	if value := r.URL.Query().Get("rotateManagerUnlockKey"); value != "" {
   108  		rot, err := strconv.ParseBool(value)
   109  		if err != nil {
   110  			return errdefs.InvalidParameter(fmt.Errorf("invalid value for rotateManagerUnlockKey: %s", value))
   111  		}
   112  
   113  		flags.RotateManagerUnlockKey = rot
   114  	}
   115  
   116  	if err := sr.backend.Update(version, swarm, flags); err != nil {
   117  		log.G(ctx).WithContext(ctx).WithError(err).Debug("Error configuring swarm")
   118  		return err
   119  	}
   120  	return nil
   121  }
   122  
   123  func (sr *swarmRouter) unlockCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   124  	var req types.UnlockRequest
   125  	if err := httputils.ReadJSON(r, &req); err != nil {
   126  		return err
   127  	}
   128  
   129  	if err := sr.backend.UnlockSwarm(req); err != nil {
   130  		log.G(ctx).WithContext(ctx).WithError(err).Debug("Error unlocking swarm")
   131  		return err
   132  	}
   133  	return nil
   134  }
   135  
   136  func (sr *swarmRouter) getUnlockKey(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   137  	unlockKey, err := sr.backend.GetUnlockKey()
   138  	if err != nil {
   139  		log.G(ctx).WithContext(ctx).WithError(err).Debug("Error retrieving swarm unlock key")
   140  		return err
   141  	}
   142  
   143  	return httputils.WriteJSON(w, http.StatusOK, &basictypes.SwarmUnlockKeyResponse{
   144  		UnlockKey: unlockKey,
   145  	})
   146  }
   147  
   148  func (sr *swarmRouter) getServices(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   149  	if err := httputils.ParseForm(r); err != nil {
   150  		return err
   151  	}
   152  	filter, err := filters.FromJSON(r.Form.Get("filters"))
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	// the status query parameter is only support in API versions >= 1.41. If
   158  	// the client is using a lesser version, ignore the parameter.
   159  	cliVersion := httputils.VersionFromContext(ctx)
   160  	var status bool
   161  	if value := r.URL.Query().Get("status"); value != "" && !versions.LessThan(cliVersion, "1.41") {
   162  		var err error
   163  		status, err = strconv.ParseBool(value)
   164  		if err != nil {
   165  			return errors.Wrapf(errdefs.InvalidParameter(err), "invalid value for status: %s", value)
   166  		}
   167  	}
   168  
   169  	services, err := sr.backend.GetServices(basictypes.ServiceListOptions{Filters: filter, Status: status})
   170  	if err != nil {
   171  		log.G(ctx).WithContext(ctx).WithError(err).Debug("Error getting services")
   172  		return err
   173  	}
   174  
   175  	return httputils.WriteJSON(w, http.StatusOK, services)
   176  }
   177  
   178  func (sr *swarmRouter) getService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   179  	var insertDefaults bool
   180  
   181  	if value := r.URL.Query().Get("insertDefaults"); value != "" {
   182  		var err error
   183  		insertDefaults, err = strconv.ParseBool(value)
   184  		if err != nil {
   185  			return errors.Wrapf(errdefs.InvalidParameter(err), "invalid value for insertDefaults: %s", value)
   186  		}
   187  	}
   188  
   189  	// you may note that there is no code here to handle the "status" query
   190  	// parameter, as in getServices. the Status field is not supported when
   191  	// retrieving an individual service because the Backend API changes
   192  	// required to accommodate it would be too disruptive, and because that
   193  	// field is so rarely needed as part of an individual service inspection.
   194  
   195  	service, err := sr.backend.GetService(vars["id"], insertDefaults)
   196  	if err != nil {
   197  		log.G(ctx).WithContext(ctx).WithFields(log.Fields{
   198  			"error":      err,
   199  			"service-id": vars["id"],
   200  		}).Debug("Error getting service")
   201  		return err
   202  	}
   203  
   204  	return httputils.WriteJSON(w, http.StatusOK, service)
   205  }
   206  
   207  func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   208  	var service types.ServiceSpec
   209  	if err := httputils.ReadJSON(r, &service); err != nil {
   210  		return err
   211  	}
   212  	// TODO(thaJeztah): remove logentries check and migration code in release v26.0.0.
   213  	if service.TaskTemplate.LogDriver != nil && service.TaskTemplate.LogDriver.Name == "logentries" {
   214  		return errdefs.InvalidParameter(errors.New("the logentries logging driver has been deprecated and removed"))
   215  	}
   216  
   217  	// Get returns "" if the header does not exist
   218  	encodedAuth := r.Header.Get(registry.AuthHeader)
   219  	queryRegistry := false
   220  	if v := httputils.VersionFromContext(ctx); v != "" {
   221  		if versions.LessThan(v, "1.30") {
   222  			queryRegistry = true
   223  		}
   224  		adjustForAPIVersion(v, &service)
   225  	}
   226  
   227  	version := httputils.VersionFromContext(ctx)
   228  	if versions.LessThan(version, "1.44") {
   229  		if service.TaskTemplate.ContainerSpec != nil && service.TaskTemplate.ContainerSpec.Healthcheck != nil {
   230  			// StartInterval was added in API 1.44
   231  			service.TaskTemplate.ContainerSpec.Healthcheck.StartInterval = 0
   232  		}
   233  	}
   234  
   235  	resp, err := sr.backend.CreateService(service, encodedAuth, queryRegistry)
   236  	if err != nil {
   237  		log.G(ctx).WithFields(log.Fields{
   238  			"error":        err,
   239  			"service-name": service.Name,
   240  		}).Debug("Error creating service")
   241  		return err
   242  	}
   243  
   244  	return httputils.WriteJSON(w, http.StatusCreated, resp)
   245  }
   246  
   247  func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   248  	var service types.ServiceSpec
   249  	if err := httputils.ReadJSON(r, &service); err != nil {
   250  		return err
   251  	}
   252  	// TODO(thaJeztah): remove logentries check and migration code in release v26.0.0.
   253  	if service.TaskTemplate.LogDriver != nil && service.TaskTemplate.LogDriver.Name == "logentries" {
   254  		return errdefs.InvalidParameter(errors.New("the logentries logging driver has been deprecated and removed"))
   255  	}
   256  
   257  	rawVersion := r.URL.Query().Get("version")
   258  	version, err := strconv.ParseUint(rawVersion, 10, 64)
   259  	if err != nil {
   260  		err := fmt.Errorf("invalid service version '%s': %v", rawVersion, err)
   261  		return errdefs.InvalidParameter(err)
   262  	}
   263  
   264  	var flags basictypes.ServiceUpdateOptions
   265  
   266  	// Get returns "" if the header does not exist
   267  	flags.EncodedRegistryAuth = r.Header.Get(registry.AuthHeader)
   268  	flags.RegistryAuthFrom = r.URL.Query().Get("registryAuthFrom")
   269  	flags.Rollback = r.URL.Query().Get("rollback")
   270  	queryRegistry := false
   271  	if v := httputils.VersionFromContext(ctx); v != "" {
   272  		if versions.LessThan(v, "1.30") {
   273  			queryRegistry = true
   274  		}
   275  		adjustForAPIVersion(v, &service)
   276  	}
   277  
   278  	resp, err := sr.backend.UpdateService(vars["id"], version, service, flags, queryRegistry)
   279  	if err != nil {
   280  		log.G(ctx).WithContext(ctx).WithFields(log.Fields{
   281  			"error":      err,
   282  			"service-id": vars["id"],
   283  		}).Debug("Error updating service")
   284  		return err
   285  	}
   286  	return httputils.WriteJSON(w, http.StatusOK, resp)
   287  }
   288  
   289  func (sr *swarmRouter) removeService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   290  	if err := sr.backend.RemoveService(vars["id"]); err != nil {
   291  		log.G(ctx).WithContext(ctx).WithFields(log.Fields{
   292  			"error":      err,
   293  			"service-id": vars["id"],
   294  		}).Debug("Error removing service")
   295  		return err
   296  	}
   297  	return nil
   298  }
   299  
   300  func (sr *swarmRouter) getTaskLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   301  	if err := httputils.ParseForm(r); err != nil {
   302  		return err
   303  	}
   304  
   305  	// make a selector to pass to the helper function
   306  	selector := &backend.LogSelector{
   307  		Tasks: []string{vars["id"]},
   308  	}
   309  	return sr.swarmLogs(ctx, w, r, selector)
   310  }
   311  
   312  func (sr *swarmRouter) getServiceLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   313  	if err := httputils.ParseForm(r); err != nil {
   314  		return err
   315  	}
   316  
   317  	// make a selector to pass to the helper function
   318  	selector := &backend.LogSelector{
   319  		Services: []string{vars["id"]},
   320  	}
   321  	return sr.swarmLogs(ctx, w, r, selector)
   322  }
   323  
   324  func (sr *swarmRouter) getNodes(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   325  	if err := httputils.ParseForm(r); err != nil {
   326  		return err
   327  	}
   328  	filter, err := filters.FromJSON(r.Form.Get("filters"))
   329  	if err != nil {
   330  		return err
   331  	}
   332  
   333  	nodes, err := sr.backend.GetNodes(basictypes.NodeListOptions{Filters: filter})
   334  	if err != nil {
   335  		log.G(ctx).WithContext(ctx).WithError(err).Debug("Error getting nodes")
   336  		return err
   337  	}
   338  
   339  	return httputils.WriteJSON(w, http.StatusOK, nodes)
   340  }
   341  
   342  func (sr *swarmRouter) getNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   343  	node, err := sr.backend.GetNode(vars["id"])
   344  	if err != nil {
   345  		log.G(ctx).WithContext(ctx).WithFields(log.Fields{
   346  			"error":   err,
   347  			"node-id": vars["id"],
   348  		}).Debug("Error getting node")
   349  		return err
   350  	}
   351  
   352  	return httputils.WriteJSON(w, http.StatusOK, node)
   353  }
   354  
   355  func (sr *swarmRouter) updateNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   356  	var node types.NodeSpec
   357  	if err := httputils.ReadJSON(r, &node); err != nil {
   358  		return err
   359  	}
   360  
   361  	rawVersion := r.URL.Query().Get("version")
   362  	version, err := strconv.ParseUint(rawVersion, 10, 64)
   363  	if err != nil {
   364  		err := fmt.Errorf("invalid node version '%s': %v", rawVersion, err)
   365  		return errdefs.InvalidParameter(err)
   366  	}
   367  
   368  	if err := sr.backend.UpdateNode(vars["id"], version, node); err != nil {
   369  		log.G(ctx).WithContext(ctx).WithFields(log.Fields{
   370  			"error":   err,
   371  			"node-id": vars["id"],
   372  		}).Debug("Error updating node")
   373  		return err
   374  	}
   375  	return nil
   376  }
   377  
   378  func (sr *swarmRouter) removeNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   379  	if err := httputils.ParseForm(r); err != nil {
   380  		return err
   381  	}
   382  
   383  	force := httputils.BoolValue(r, "force")
   384  
   385  	if err := sr.backend.RemoveNode(vars["id"], force); err != nil {
   386  		log.G(ctx).WithContext(ctx).WithFields(log.Fields{
   387  			"error":   err,
   388  			"node-id": vars["id"],
   389  		}).Debug("Error removing node")
   390  		return err
   391  	}
   392  	return nil
   393  }
   394  
   395  func (sr *swarmRouter) getTasks(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   396  	if err := httputils.ParseForm(r); err != nil {
   397  		return err
   398  	}
   399  	filter, err := filters.FromJSON(r.Form.Get("filters"))
   400  	if err != nil {
   401  		return err
   402  	}
   403  
   404  	tasks, err := sr.backend.GetTasks(basictypes.TaskListOptions{Filters: filter})
   405  	if err != nil {
   406  		log.G(ctx).WithContext(ctx).WithError(err).Debug("Error getting tasks")
   407  		return err
   408  	}
   409  
   410  	return httputils.WriteJSON(w, http.StatusOK, tasks)
   411  }
   412  
   413  func (sr *swarmRouter) getTask(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   414  	task, err := sr.backend.GetTask(vars["id"])
   415  	if err != nil {
   416  		log.G(ctx).WithContext(ctx).WithFields(log.Fields{
   417  			"error":   err,
   418  			"task-id": vars["id"],
   419  		}).Debug("Error getting task")
   420  		return err
   421  	}
   422  
   423  	return httputils.WriteJSON(w, http.StatusOK, task)
   424  }
   425  
   426  func (sr *swarmRouter) getSecrets(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   427  	if err := httputils.ParseForm(r); err != nil {
   428  		return err
   429  	}
   430  	filters, err := filters.FromJSON(r.Form.Get("filters"))
   431  	if err != nil {
   432  		return err
   433  	}
   434  
   435  	secrets, err := sr.backend.GetSecrets(basictypes.SecretListOptions{Filters: filters})
   436  	if err != nil {
   437  		return err
   438  	}
   439  
   440  	return httputils.WriteJSON(w, http.StatusOK, secrets)
   441  }
   442  
   443  func (sr *swarmRouter) createSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   444  	var secret types.SecretSpec
   445  	if err := httputils.ReadJSON(r, &secret); err != nil {
   446  		return err
   447  	}
   448  	version := httputils.VersionFromContext(ctx)
   449  	if secret.Templating != nil && versions.LessThan(version, "1.37") {
   450  		return errdefs.InvalidParameter(errors.Errorf("secret templating is not supported on the specified API version: %s", version))
   451  	}
   452  
   453  	id, err := sr.backend.CreateSecret(secret)
   454  	if err != nil {
   455  		return err
   456  	}
   457  
   458  	return httputils.WriteJSON(w, http.StatusCreated, &basictypes.SecretCreateResponse{
   459  		ID: id,
   460  	})
   461  }
   462  
   463  func (sr *swarmRouter) removeSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   464  	if err := sr.backend.RemoveSecret(vars["id"]); err != nil {
   465  		return err
   466  	}
   467  	w.WriteHeader(http.StatusNoContent)
   468  
   469  	return nil
   470  }
   471  
   472  func (sr *swarmRouter) getSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   473  	secret, err := sr.backend.GetSecret(vars["id"])
   474  	if err != nil {
   475  		return err
   476  	}
   477  
   478  	return httputils.WriteJSON(w, http.StatusOK, secret)
   479  }
   480  
   481  func (sr *swarmRouter) updateSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   482  	var secret types.SecretSpec
   483  	if err := httputils.ReadJSON(r, &secret); err != nil {
   484  		return err
   485  	}
   486  
   487  	rawVersion := r.URL.Query().Get("version")
   488  	version, err := strconv.ParseUint(rawVersion, 10, 64)
   489  	if err != nil {
   490  		return errdefs.InvalidParameter(fmt.Errorf("invalid secret version"))
   491  	}
   492  
   493  	id := vars["id"]
   494  	return sr.backend.UpdateSecret(id, version, secret)
   495  }
   496  
   497  func (sr *swarmRouter) getConfigs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   498  	if err := httputils.ParseForm(r); err != nil {
   499  		return err
   500  	}
   501  	filters, err := filters.FromJSON(r.Form.Get("filters"))
   502  	if err != nil {
   503  		return err
   504  	}
   505  
   506  	configs, err := sr.backend.GetConfigs(basictypes.ConfigListOptions{Filters: filters})
   507  	if err != nil {
   508  		return err
   509  	}
   510  
   511  	return httputils.WriteJSON(w, http.StatusOK, configs)
   512  }
   513  
   514  func (sr *swarmRouter) createConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   515  	var config types.ConfigSpec
   516  	if err := httputils.ReadJSON(r, &config); err != nil {
   517  		return err
   518  	}
   519  
   520  	version := httputils.VersionFromContext(ctx)
   521  	if config.Templating != nil && versions.LessThan(version, "1.37") {
   522  		return errdefs.InvalidParameter(errors.Errorf("config templating is not supported on the specified API version: %s", version))
   523  	}
   524  
   525  	id, err := sr.backend.CreateConfig(config)
   526  	if err != nil {
   527  		return err
   528  	}
   529  
   530  	return httputils.WriteJSON(w, http.StatusCreated, &basictypes.ConfigCreateResponse{
   531  		ID: id,
   532  	})
   533  }
   534  
   535  func (sr *swarmRouter) removeConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   536  	if err := sr.backend.RemoveConfig(vars["id"]); err != nil {
   537  		return err
   538  	}
   539  	w.WriteHeader(http.StatusNoContent)
   540  
   541  	return nil
   542  }
   543  
   544  func (sr *swarmRouter) getConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   545  	config, err := sr.backend.GetConfig(vars["id"])
   546  	if err != nil {
   547  		return err
   548  	}
   549  
   550  	return httputils.WriteJSON(w, http.StatusOK, config)
   551  }
   552  
   553  func (sr *swarmRouter) updateConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   554  	var config types.ConfigSpec
   555  	if err := httputils.ReadJSON(r, &config); err != nil {
   556  		return err
   557  	}
   558  
   559  	rawVersion := r.URL.Query().Get("version")
   560  	version, err := strconv.ParseUint(rawVersion, 10, 64)
   561  	if err != nil {
   562  		return errdefs.InvalidParameter(fmt.Errorf("invalid config version"))
   563  	}
   564  
   565  	id := vars["id"]
   566  	return sr.backend.UpdateConfig(id, version, config)
   567  }