github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/api/server/router/swarm/cluster_routes.go (about)

     1  package swarm // import "github.com/Prakhar-Agarwal-byte/moby/api/server/router/swarm"
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"strconv"
     8  
     9  	"github.com/Prakhar-Agarwal-byte/moby/api/server/httputils"
    10  	basictypes "github.com/Prakhar-Agarwal-byte/moby/api/types"
    11  	"github.com/Prakhar-Agarwal-byte/moby/api/types/backend"
    12  	"github.com/Prakhar-Agarwal-byte/moby/api/types/filters"
    13  	"github.com/Prakhar-Agarwal-byte/moby/api/types/registry"
    14  	types "github.com/Prakhar-Agarwal-byte/moby/api/types/swarm"
    15  	"github.com/Prakhar-Agarwal-byte/moby/api/types/versions"
    16  	"github.com/Prakhar-Agarwal-byte/moby/errdefs"
    17  	"github.com/containerd/log"
    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  
   213  	// Get returns "" if the header does not exist
   214  	encodedAuth := r.Header.Get(registry.AuthHeader)
   215  	queryRegistry := false
   216  	if v := httputils.VersionFromContext(ctx); v != "" {
   217  		if versions.LessThan(v, "1.30") {
   218  			queryRegistry = true
   219  		}
   220  		adjustForAPIVersion(v, &service)
   221  	}
   222  
   223  	version := httputils.VersionFromContext(ctx)
   224  	if versions.LessThan(version, "1.44") {
   225  		if service.TaskTemplate.ContainerSpec != nil && service.TaskTemplate.ContainerSpec.Healthcheck != nil {
   226  			// StartInterval was added in API 1.44
   227  			service.TaskTemplate.ContainerSpec.Healthcheck.StartInterval = 0
   228  		}
   229  	}
   230  
   231  	resp, err := sr.backend.CreateService(service, encodedAuth, queryRegistry)
   232  	if err != nil {
   233  		log.G(ctx).WithFields(log.Fields{
   234  			"error":        err,
   235  			"service-name": service.Name,
   236  		}).Debug("Error creating service")
   237  		return err
   238  	}
   239  
   240  	return httputils.WriteJSON(w, http.StatusCreated, resp)
   241  }
   242  
   243  func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   244  	var service types.ServiceSpec
   245  	if err := httputils.ReadJSON(r, &service); err != nil {
   246  		return err
   247  	}
   248  
   249  	rawVersion := r.URL.Query().Get("version")
   250  	version, err := strconv.ParseUint(rawVersion, 10, 64)
   251  	if err != nil {
   252  		err := fmt.Errorf("invalid service version '%s': %v", rawVersion, err)
   253  		return errdefs.InvalidParameter(err)
   254  	}
   255  
   256  	var flags basictypes.ServiceUpdateOptions
   257  
   258  	// Get returns "" if the header does not exist
   259  	flags.EncodedRegistryAuth = r.Header.Get(registry.AuthHeader)
   260  	flags.RegistryAuthFrom = r.URL.Query().Get("registryAuthFrom")
   261  	flags.Rollback = r.URL.Query().Get("rollback")
   262  	queryRegistry := false
   263  	if v := httputils.VersionFromContext(ctx); v != "" {
   264  		if versions.LessThan(v, "1.30") {
   265  			queryRegistry = true
   266  		}
   267  		adjustForAPIVersion(v, &service)
   268  	}
   269  
   270  	resp, err := sr.backend.UpdateService(vars["id"], version, service, flags, queryRegistry)
   271  	if err != nil {
   272  		log.G(ctx).WithContext(ctx).WithFields(log.Fields{
   273  			"error":      err,
   274  			"service-id": vars["id"],
   275  		}).Debug("Error updating service")
   276  		return err
   277  	}
   278  	return httputils.WriteJSON(w, http.StatusOK, resp)
   279  }
   280  
   281  func (sr *swarmRouter) removeService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   282  	if err := sr.backend.RemoveService(vars["id"]); err != nil {
   283  		log.G(ctx).WithContext(ctx).WithFields(log.Fields{
   284  			"error":      err,
   285  			"service-id": vars["id"],
   286  		}).Debug("Error removing service")
   287  		return err
   288  	}
   289  	return nil
   290  }
   291  
   292  func (sr *swarmRouter) getTaskLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   293  	if err := httputils.ParseForm(r); err != nil {
   294  		return err
   295  	}
   296  
   297  	// make a selector to pass to the helper function
   298  	selector := &backend.LogSelector{
   299  		Tasks: []string{vars["id"]},
   300  	}
   301  	return sr.swarmLogs(ctx, w, r, selector)
   302  }
   303  
   304  func (sr *swarmRouter) getServiceLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   305  	if err := httputils.ParseForm(r); err != nil {
   306  		return err
   307  	}
   308  
   309  	// make a selector to pass to the helper function
   310  	selector := &backend.LogSelector{
   311  		Services: []string{vars["id"]},
   312  	}
   313  	return sr.swarmLogs(ctx, w, r, selector)
   314  }
   315  
   316  func (sr *swarmRouter) getNodes(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   317  	if err := httputils.ParseForm(r); err != nil {
   318  		return err
   319  	}
   320  	filter, err := filters.FromJSON(r.Form.Get("filters"))
   321  	if err != nil {
   322  		return err
   323  	}
   324  
   325  	nodes, err := sr.backend.GetNodes(basictypes.NodeListOptions{Filters: filter})
   326  	if err != nil {
   327  		log.G(ctx).WithContext(ctx).WithError(err).Debug("Error getting nodes")
   328  		return err
   329  	}
   330  
   331  	return httputils.WriteJSON(w, http.StatusOK, nodes)
   332  }
   333  
   334  func (sr *swarmRouter) getNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   335  	node, err := sr.backend.GetNode(vars["id"])
   336  	if err != nil {
   337  		log.G(ctx).WithContext(ctx).WithFields(log.Fields{
   338  			"error":   err,
   339  			"node-id": vars["id"],
   340  		}).Debug("Error getting node")
   341  		return err
   342  	}
   343  
   344  	return httputils.WriteJSON(w, http.StatusOK, node)
   345  }
   346  
   347  func (sr *swarmRouter) updateNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   348  	var node types.NodeSpec
   349  	if err := httputils.ReadJSON(r, &node); err != nil {
   350  		return err
   351  	}
   352  
   353  	rawVersion := r.URL.Query().Get("version")
   354  	version, err := strconv.ParseUint(rawVersion, 10, 64)
   355  	if err != nil {
   356  		err := fmt.Errorf("invalid node version '%s': %v", rawVersion, err)
   357  		return errdefs.InvalidParameter(err)
   358  	}
   359  
   360  	if err := sr.backend.UpdateNode(vars["id"], version, node); err != nil {
   361  		log.G(ctx).WithContext(ctx).WithFields(log.Fields{
   362  			"error":   err,
   363  			"node-id": vars["id"],
   364  		}).Debug("Error updating node")
   365  		return err
   366  	}
   367  	return nil
   368  }
   369  
   370  func (sr *swarmRouter) removeNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   371  	if err := httputils.ParseForm(r); err != nil {
   372  		return err
   373  	}
   374  
   375  	force := httputils.BoolValue(r, "force")
   376  
   377  	if err := sr.backend.RemoveNode(vars["id"], force); err != nil {
   378  		log.G(ctx).WithContext(ctx).WithFields(log.Fields{
   379  			"error":   err,
   380  			"node-id": vars["id"],
   381  		}).Debug("Error removing node")
   382  		return err
   383  	}
   384  	return nil
   385  }
   386  
   387  func (sr *swarmRouter) getTasks(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   388  	if err := httputils.ParseForm(r); err != nil {
   389  		return err
   390  	}
   391  	filter, err := filters.FromJSON(r.Form.Get("filters"))
   392  	if err != nil {
   393  		return err
   394  	}
   395  
   396  	tasks, err := sr.backend.GetTasks(basictypes.TaskListOptions{Filters: filter})
   397  	if err != nil {
   398  		log.G(ctx).WithContext(ctx).WithError(err).Debug("Error getting tasks")
   399  		return err
   400  	}
   401  
   402  	return httputils.WriteJSON(w, http.StatusOK, tasks)
   403  }
   404  
   405  func (sr *swarmRouter) getTask(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   406  	task, err := sr.backend.GetTask(vars["id"])
   407  	if err != nil {
   408  		log.G(ctx).WithContext(ctx).WithFields(log.Fields{
   409  			"error":   err,
   410  			"task-id": vars["id"],
   411  		}).Debug("Error getting task")
   412  		return err
   413  	}
   414  
   415  	return httputils.WriteJSON(w, http.StatusOK, task)
   416  }
   417  
   418  func (sr *swarmRouter) getSecrets(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   419  	if err := httputils.ParseForm(r); err != nil {
   420  		return err
   421  	}
   422  	filters, err := filters.FromJSON(r.Form.Get("filters"))
   423  	if err != nil {
   424  		return err
   425  	}
   426  
   427  	secrets, err := sr.backend.GetSecrets(basictypes.SecretListOptions{Filters: filters})
   428  	if err != nil {
   429  		return err
   430  	}
   431  
   432  	return httputils.WriteJSON(w, http.StatusOK, secrets)
   433  }
   434  
   435  func (sr *swarmRouter) createSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   436  	var secret types.SecretSpec
   437  	if err := httputils.ReadJSON(r, &secret); err != nil {
   438  		return err
   439  	}
   440  	version := httputils.VersionFromContext(ctx)
   441  	if secret.Templating != nil && versions.LessThan(version, "1.37") {
   442  		return errdefs.InvalidParameter(errors.Errorf("secret templating is not supported on the specified API version: %s", version))
   443  	}
   444  
   445  	id, err := sr.backend.CreateSecret(secret)
   446  	if err != nil {
   447  		return err
   448  	}
   449  
   450  	return httputils.WriteJSON(w, http.StatusCreated, &basictypes.SecretCreateResponse{
   451  		ID: id,
   452  	})
   453  }
   454  
   455  func (sr *swarmRouter) removeSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   456  	if err := sr.backend.RemoveSecret(vars["id"]); err != nil {
   457  		return err
   458  	}
   459  	w.WriteHeader(http.StatusNoContent)
   460  
   461  	return nil
   462  }
   463  
   464  func (sr *swarmRouter) getSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   465  	secret, err := sr.backend.GetSecret(vars["id"])
   466  	if err != nil {
   467  		return err
   468  	}
   469  
   470  	return httputils.WriteJSON(w, http.StatusOK, secret)
   471  }
   472  
   473  func (sr *swarmRouter) updateSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   474  	var secret types.SecretSpec
   475  	if err := httputils.ReadJSON(r, &secret); err != nil {
   476  		return err
   477  	}
   478  
   479  	rawVersion := r.URL.Query().Get("version")
   480  	version, err := strconv.ParseUint(rawVersion, 10, 64)
   481  	if err != nil {
   482  		return errdefs.InvalidParameter(fmt.Errorf("invalid secret version"))
   483  	}
   484  
   485  	id := vars["id"]
   486  	return sr.backend.UpdateSecret(id, version, secret)
   487  }
   488  
   489  func (sr *swarmRouter) getConfigs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   490  	if err := httputils.ParseForm(r); err != nil {
   491  		return err
   492  	}
   493  	filters, err := filters.FromJSON(r.Form.Get("filters"))
   494  	if err != nil {
   495  		return err
   496  	}
   497  
   498  	configs, err := sr.backend.GetConfigs(basictypes.ConfigListOptions{Filters: filters})
   499  	if err != nil {
   500  		return err
   501  	}
   502  
   503  	return httputils.WriteJSON(w, http.StatusOK, configs)
   504  }
   505  
   506  func (sr *swarmRouter) createConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   507  	var config types.ConfigSpec
   508  	if err := httputils.ReadJSON(r, &config); err != nil {
   509  		return err
   510  	}
   511  
   512  	version := httputils.VersionFromContext(ctx)
   513  	if config.Templating != nil && versions.LessThan(version, "1.37") {
   514  		return errdefs.InvalidParameter(errors.Errorf("config templating is not supported on the specified API version: %s", version))
   515  	}
   516  
   517  	id, err := sr.backend.CreateConfig(config)
   518  	if err != nil {
   519  		return err
   520  	}
   521  
   522  	return httputils.WriteJSON(w, http.StatusCreated, &basictypes.ConfigCreateResponse{
   523  		ID: id,
   524  	})
   525  }
   526  
   527  func (sr *swarmRouter) removeConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   528  	if err := sr.backend.RemoveConfig(vars["id"]); err != nil {
   529  		return err
   530  	}
   531  	w.WriteHeader(http.StatusNoContent)
   532  
   533  	return nil
   534  }
   535  
   536  func (sr *swarmRouter) getConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   537  	config, err := sr.backend.GetConfig(vars["id"])
   538  	if err != nil {
   539  		return err
   540  	}
   541  
   542  	return httputils.WriteJSON(w, http.StatusOK, config)
   543  }
   544  
   545  func (sr *swarmRouter) updateConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   546  	var config types.ConfigSpec
   547  	if err := httputils.ReadJSON(r, &config); err != nil {
   548  		return err
   549  	}
   550  
   551  	rawVersion := r.URL.Query().Get("version")
   552  	version, err := strconv.ParseUint(rawVersion, 10, 64)
   553  	if err != nil {
   554  		return errdefs.InvalidParameter(fmt.Errorf("invalid config version"))
   555  	}
   556  
   557  	id := vars["id"]
   558  	return sr.backend.UpdateConfig(id, version, config)
   559  }