github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/daemon/cluster/convert/service.go (about)

     1  package convert // import "github.com/demonoid81/moby/daemon/cluster/convert"
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	types "github.com/demonoid81/moby/api/types/swarm"
     8  	"github.com/demonoid81/moby/api/types/swarm/runtime"
     9  	"github.com/demonoid81/moby/pkg/namesgenerator"
    10  	swarmapi "github.com/docker/swarmkit/api"
    11  	"github.com/docker/swarmkit/api/genericresource"
    12  	"github.com/gogo/protobuf/proto"
    13  	gogotypes "github.com/gogo/protobuf/types"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  var (
    18  	// ErrUnsupportedRuntime returns an error if the runtime is not supported by the daemon
    19  	ErrUnsupportedRuntime = errors.New("unsupported runtime")
    20  	// ErrMismatchedRuntime returns an error if the runtime does not match the provided spec
    21  	ErrMismatchedRuntime = errors.New("mismatched Runtime and *Spec fields")
    22  )
    23  
    24  // ServiceFromGRPC converts a grpc Service to a Service.
    25  func ServiceFromGRPC(s swarmapi.Service) (types.Service, error) {
    26  	curSpec, err := serviceSpecFromGRPC(&s.Spec)
    27  	if err != nil {
    28  		return types.Service{}, err
    29  	}
    30  	prevSpec, err := serviceSpecFromGRPC(s.PreviousSpec)
    31  	if err != nil {
    32  		return types.Service{}, err
    33  	}
    34  	service := types.Service{
    35  		ID:           s.ID,
    36  		Spec:         *curSpec,
    37  		PreviousSpec: prevSpec,
    38  
    39  		Endpoint: endpointFromGRPC(s.Endpoint),
    40  	}
    41  
    42  	// Meta
    43  	service.Version.Index = s.Meta.Version.Index
    44  	service.CreatedAt, _ = gogotypes.TimestampFromProto(s.Meta.CreatedAt)
    45  	service.UpdatedAt, _ = gogotypes.TimestampFromProto(s.Meta.UpdatedAt)
    46  
    47  	if s.JobStatus != nil {
    48  		service.JobStatus = &types.JobStatus{
    49  			JobIteration: types.Version{
    50  				Index: s.JobStatus.JobIteration.Index,
    51  			},
    52  		}
    53  		service.JobStatus.LastExecution, _ = gogotypes.TimestampFromProto(s.JobStatus.LastExecution)
    54  	}
    55  
    56  	// UpdateStatus
    57  	if s.UpdateStatus != nil {
    58  		service.UpdateStatus = &types.UpdateStatus{}
    59  		switch s.UpdateStatus.State {
    60  		case swarmapi.UpdateStatus_UPDATING:
    61  			service.UpdateStatus.State = types.UpdateStateUpdating
    62  		case swarmapi.UpdateStatus_PAUSED:
    63  			service.UpdateStatus.State = types.UpdateStatePaused
    64  		case swarmapi.UpdateStatus_COMPLETED:
    65  			service.UpdateStatus.State = types.UpdateStateCompleted
    66  		case swarmapi.UpdateStatus_ROLLBACK_STARTED:
    67  			service.UpdateStatus.State = types.UpdateStateRollbackStarted
    68  		case swarmapi.UpdateStatus_ROLLBACK_PAUSED:
    69  			service.UpdateStatus.State = types.UpdateStateRollbackPaused
    70  		case swarmapi.UpdateStatus_ROLLBACK_COMPLETED:
    71  			service.UpdateStatus.State = types.UpdateStateRollbackCompleted
    72  		}
    73  
    74  		startedAt, _ := gogotypes.TimestampFromProto(s.UpdateStatus.StartedAt)
    75  		if !startedAt.IsZero() && startedAt.Unix() != 0 {
    76  			service.UpdateStatus.StartedAt = &startedAt
    77  		}
    78  
    79  		completedAt, _ := gogotypes.TimestampFromProto(s.UpdateStatus.CompletedAt)
    80  		if !completedAt.IsZero() && completedAt.Unix() != 0 {
    81  			service.UpdateStatus.CompletedAt = &completedAt
    82  		}
    83  
    84  		service.UpdateStatus.Message = s.UpdateStatus.Message
    85  	}
    86  
    87  	return service, nil
    88  }
    89  
    90  func serviceSpecFromGRPC(spec *swarmapi.ServiceSpec) (*types.ServiceSpec, error) {
    91  	if spec == nil {
    92  		return nil, nil
    93  	}
    94  
    95  	serviceNetworks := make([]types.NetworkAttachmentConfig, 0, len(spec.Networks))
    96  	for _, n := range spec.Networks {
    97  		netConfig := types.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverOpts: n.DriverAttachmentOpts}
    98  		serviceNetworks = append(serviceNetworks, netConfig)
    99  
   100  	}
   101  
   102  	taskTemplate, err := taskSpecFromGRPC(spec.Task)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	switch t := spec.Task.GetRuntime().(type) {
   108  	case *swarmapi.TaskSpec_Container:
   109  		containerConfig := t.Container
   110  		taskTemplate.ContainerSpec = containerSpecFromGRPC(containerConfig)
   111  		taskTemplate.Runtime = types.RuntimeContainer
   112  	case *swarmapi.TaskSpec_Generic:
   113  		switch t.Generic.Kind {
   114  		case string(types.RuntimePlugin):
   115  			taskTemplate.Runtime = types.RuntimePlugin
   116  		default:
   117  			return nil, fmt.Errorf("unknown task runtime type: %s", t.Generic.Payload.TypeUrl)
   118  		}
   119  
   120  	default:
   121  		return nil, fmt.Errorf("error creating service; unsupported runtime %T", t)
   122  	}
   123  
   124  	convertedSpec := &types.ServiceSpec{
   125  		Annotations:  annotationsFromGRPC(spec.Annotations),
   126  		TaskTemplate: taskTemplate,
   127  		Networks:     serviceNetworks,
   128  		EndpointSpec: endpointSpecFromGRPC(spec.Endpoint),
   129  	}
   130  
   131  	// UpdateConfig
   132  	convertedSpec.UpdateConfig = updateConfigFromGRPC(spec.Update)
   133  	convertedSpec.RollbackConfig = updateConfigFromGRPC(spec.Rollback)
   134  
   135  	// Mode
   136  	switch t := spec.GetMode().(type) {
   137  	case *swarmapi.ServiceSpec_Global:
   138  		convertedSpec.Mode.Global = &types.GlobalService{}
   139  	case *swarmapi.ServiceSpec_Replicated:
   140  		convertedSpec.Mode.Replicated = &types.ReplicatedService{
   141  			Replicas: &t.Replicated.Replicas,
   142  		}
   143  	case *swarmapi.ServiceSpec_ReplicatedJob:
   144  		convertedSpec.Mode.ReplicatedJob = &types.ReplicatedJob{
   145  			MaxConcurrent:    &t.ReplicatedJob.MaxConcurrent,
   146  			TotalCompletions: &t.ReplicatedJob.TotalCompletions,
   147  		}
   148  	case *swarmapi.ServiceSpec_GlobalJob:
   149  		convertedSpec.Mode.GlobalJob = &types.GlobalJob{}
   150  	}
   151  
   152  	return convertedSpec, nil
   153  }
   154  
   155  // ServiceSpecToGRPC converts a ServiceSpec to a grpc ServiceSpec.
   156  func ServiceSpecToGRPC(s types.ServiceSpec) (swarmapi.ServiceSpec, error) {
   157  	name := s.Name
   158  	if name == "" {
   159  		name = namesgenerator.GetRandomName(0)
   160  	}
   161  
   162  	serviceNetworks := make([]*swarmapi.NetworkAttachmentConfig, 0, len(s.Networks))
   163  	for _, n := range s.Networks {
   164  		netConfig := &swarmapi.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverAttachmentOpts: n.DriverOpts}
   165  		serviceNetworks = append(serviceNetworks, netConfig)
   166  	}
   167  
   168  	taskNetworks := make([]*swarmapi.NetworkAttachmentConfig, 0, len(s.TaskTemplate.Networks))
   169  	for _, n := range s.TaskTemplate.Networks {
   170  		netConfig := &swarmapi.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverAttachmentOpts: n.DriverOpts}
   171  		taskNetworks = append(taskNetworks, netConfig)
   172  
   173  	}
   174  
   175  	spec := swarmapi.ServiceSpec{
   176  		Annotations: swarmapi.Annotations{
   177  			Name:   name,
   178  			Labels: s.Labels,
   179  		},
   180  		Task: swarmapi.TaskSpec{
   181  			Resources:   resourcesToGRPC(s.TaskTemplate.Resources),
   182  			LogDriver:   driverToGRPC(s.TaskTemplate.LogDriver),
   183  			Networks:    taskNetworks,
   184  			ForceUpdate: s.TaskTemplate.ForceUpdate,
   185  		},
   186  		Networks: serviceNetworks,
   187  	}
   188  
   189  	switch s.TaskTemplate.Runtime {
   190  	case types.RuntimeContainer, "": // if empty runtime default to container
   191  		if s.TaskTemplate.ContainerSpec != nil {
   192  			containerSpec, err := containerToGRPC(s.TaskTemplate.ContainerSpec)
   193  			if err != nil {
   194  				return swarmapi.ServiceSpec{}, err
   195  			}
   196  			spec.Task.Runtime = &swarmapi.TaskSpec_Container{Container: containerSpec}
   197  		} else {
   198  			// If the ContainerSpec is nil, we can't set the task runtime
   199  			return swarmapi.ServiceSpec{}, ErrMismatchedRuntime
   200  		}
   201  	case types.RuntimePlugin:
   202  		if s.TaskTemplate.PluginSpec != nil {
   203  			if s.Mode.Replicated != nil {
   204  				return swarmapi.ServiceSpec{}, errors.New("plugins must not use replicated mode")
   205  			}
   206  
   207  			s.Mode.Global = &types.GlobalService{} // must always be global
   208  
   209  			pluginSpec, err := proto.Marshal(s.TaskTemplate.PluginSpec)
   210  			if err != nil {
   211  				return swarmapi.ServiceSpec{}, err
   212  			}
   213  			spec.Task.Runtime = &swarmapi.TaskSpec_Generic{
   214  				Generic: &swarmapi.GenericRuntimeSpec{
   215  					Kind: string(types.RuntimePlugin),
   216  					Payload: &gogotypes.Any{
   217  						TypeUrl: string(types.RuntimeURLPlugin),
   218  						Value:   pluginSpec,
   219  					},
   220  				},
   221  			}
   222  		} else {
   223  			return swarmapi.ServiceSpec{}, ErrMismatchedRuntime
   224  		}
   225  	case types.RuntimeNetworkAttachment:
   226  		// NOTE(dperny) I'm leaving this case here for completeness. The actual
   227  		// code is left out deliberately, as we should refuse to parse a
   228  		// Network Attachment runtime; it will cause weird behavior all over
   229  		// the system if we do. Instead, fallthrough and return
   230  		// ErrUnsupportedRuntime if we get one.
   231  		fallthrough
   232  	default:
   233  		return swarmapi.ServiceSpec{}, ErrUnsupportedRuntime
   234  	}
   235  
   236  	restartPolicy, err := restartPolicyToGRPC(s.TaskTemplate.RestartPolicy)
   237  	if err != nil {
   238  		return swarmapi.ServiceSpec{}, err
   239  	}
   240  	spec.Task.Restart = restartPolicy
   241  
   242  	if s.TaskTemplate.Placement != nil {
   243  		var preferences []*swarmapi.PlacementPreference
   244  		for _, pref := range s.TaskTemplate.Placement.Preferences {
   245  			if pref.Spread != nil {
   246  				preferences = append(preferences, &swarmapi.PlacementPreference{
   247  					Preference: &swarmapi.PlacementPreference_Spread{
   248  						Spread: &swarmapi.SpreadOver{
   249  							SpreadDescriptor: pref.Spread.SpreadDescriptor,
   250  						},
   251  					},
   252  				})
   253  			}
   254  		}
   255  		var platforms []*swarmapi.Platform
   256  		for _, plat := range s.TaskTemplate.Placement.Platforms {
   257  			platforms = append(platforms, &swarmapi.Platform{
   258  				Architecture: plat.Architecture,
   259  				OS:           plat.OS,
   260  			})
   261  		}
   262  		spec.Task.Placement = &swarmapi.Placement{
   263  			Constraints: s.TaskTemplate.Placement.Constraints,
   264  			Preferences: preferences,
   265  			MaxReplicas: s.TaskTemplate.Placement.MaxReplicas,
   266  			Platforms:   platforms,
   267  		}
   268  	}
   269  
   270  	spec.Update, err = updateConfigToGRPC(s.UpdateConfig)
   271  	if err != nil {
   272  		return swarmapi.ServiceSpec{}, err
   273  	}
   274  	spec.Rollback, err = updateConfigToGRPC(s.RollbackConfig)
   275  	if err != nil {
   276  		return swarmapi.ServiceSpec{}, err
   277  	}
   278  
   279  	if s.EndpointSpec != nil {
   280  		if s.EndpointSpec.Mode != "" &&
   281  			s.EndpointSpec.Mode != types.ResolutionModeVIP &&
   282  			s.EndpointSpec.Mode != types.ResolutionModeDNSRR {
   283  			return swarmapi.ServiceSpec{}, fmt.Errorf("invalid resolution mode: %q", s.EndpointSpec.Mode)
   284  		}
   285  
   286  		spec.Endpoint = &swarmapi.EndpointSpec{}
   287  
   288  		spec.Endpoint.Mode = swarmapi.EndpointSpec_ResolutionMode(swarmapi.EndpointSpec_ResolutionMode_value[strings.ToUpper(string(s.EndpointSpec.Mode))])
   289  
   290  		for _, portConfig := range s.EndpointSpec.Ports {
   291  			spec.Endpoint.Ports = append(spec.Endpoint.Ports, &swarmapi.PortConfig{
   292  				Name:          portConfig.Name,
   293  				Protocol:      swarmapi.PortConfig_Protocol(swarmapi.PortConfig_Protocol_value[strings.ToUpper(string(portConfig.Protocol))]),
   294  				PublishMode:   swarmapi.PortConfig_PublishMode(swarmapi.PortConfig_PublishMode_value[strings.ToUpper(string(portConfig.PublishMode))]),
   295  				TargetPort:    portConfig.TargetPort,
   296  				PublishedPort: portConfig.PublishedPort,
   297  			})
   298  		}
   299  	}
   300  
   301  	// Mode
   302  	numModes := 0
   303  	if s.Mode.Global != nil {
   304  		numModes++
   305  	}
   306  	if s.Mode.Replicated != nil {
   307  		numModes++
   308  	}
   309  	if s.Mode.ReplicatedJob != nil {
   310  		numModes++
   311  	}
   312  	if s.Mode.GlobalJob != nil {
   313  		numModes++
   314  	}
   315  
   316  	if numModes > 1 {
   317  		return swarmapi.ServiceSpec{}, fmt.Errorf("must specify only one service mode")
   318  	}
   319  
   320  	if s.Mode.Global != nil {
   321  		spec.Mode = &swarmapi.ServiceSpec_Global{
   322  			Global: &swarmapi.GlobalService{},
   323  		}
   324  	} else if s.Mode.GlobalJob != nil {
   325  		spec.Mode = &swarmapi.ServiceSpec_GlobalJob{
   326  			GlobalJob: &swarmapi.GlobalJob{},
   327  		}
   328  	} else if s.Mode.ReplicatedJob != nil {
   329  		// if the service is a replicated job, we have two different kinds of
   330  		// values that might need to be defaulted.
   331  
   332  		r := &swarmapi.ReplicatedJob{}
   333  		if s.Mode.ReplicatedJob.MaxConcurrent != nil {
   334  			r.MaxConcurrent = *s.Mode.ReplicatedJob.MaxConcurrent
   335  		} else {
   336  			r.MaxConcurrent = 1
   337  		}
   338  
   339  		if s.Mode.ReplicatedJob.TotalCompletions != nil {
   340  			r.TotalCompletions = *s.Mode.ReplicatedJob.TotalCompletions
   341  		} else {
   342  			r.TotalCompletions = r.MaxConcurrent
   343  		}
   344  
   345  		spec.Mode = &swarmapi.ServiceSpec_ReplicatedJob{
   346  			ReplicatedJob: r,
   347  		}
   348  	} else if s.Mode.Replicated != nil && s.Mode.Replicated.Replicas != nil {
   349  		spec.Mode = &swarmapi.ServiceSpec_Replicated{
   350  			Replicated: &swarmapi.ReplicatedService{Replicas: *s.Mode.Replicated.Replicas},
   351  		}
   352  	} else {
   353  		spec.Mode = &swarmapi.ServiceSpec_Replicated{
   354  			Replicated: &swarmapi.ReplicatedService{Replicas: 1},
   355  		}
   356  	}
   357  
   358  	return spec, nil
   359  }
   360  
   361  func annotationsFromGRPC(ann swarmapi.Annotations) types.Annotations {
   362  	a := types.Annotations{
   363  		Name:   ann.Name,
   364  		Labels: ann.Labels,
   365  	}
   366  
   367  	if a.Labels == nil {
   368  		a.Labels = make(map[string]string)
   369  	}
   370  
   371  	return a
   372  }
   373  
   374  // GenericResourcesFromGRPC converts a GRPC GenericResource to a GenericResource
   375  func GenericResourcesFromGRPC(genericRes []*swarmapi.GenericResource) []types.GenericResource {
   376  	var generic []types.GenericResource
   377  	for _, res := range genericRes {
   378  		var current types.GenericResource
   379  
   380  		switch r := res.Resource.(type) {
   381  		case *swarmapi.GenericResource_DiscreteResourceSpec:
   382  			current.DiscreteResourceSpec = &types.DiscreteGenericResource{
   383  				Kind:  r.DiscreteResourceSpec.Kind,
   384  				Value: r.DiscreteResourceSpec.Value,
   385  			}
   386  		case *swarmapi.GenericResource_NamedResourceSpec:
   387  			current.NamedResourceSpec = &types.NamedGenericResource{
   388  				Kind:  r.NamedResourceSpec.Kind,
   389  				Value: r.NamedResourceSpec.Value,
   390  			}
   391  		}
   392  
   393  		generic = append(generic, current)
   394  	}
   395  
   396  	return generic
   397  }
   398  
   399  func resourcesFromGRPC(res *swarmapi.ResourceRequirements) *types.ResourceRequirements {
   400  	var resources *types.ResourceRequirements
   401  	if res != nil {
   402  		resources = &types.ResourceRequirements{}
   403  		if res.Limits != nil {
   404  			resources.Limits = &types.Resources{
   405  				NanoCPUs:    res.Limits.NanoCPUs,
   406  				MemoryBytes: res.Limits.MemoryBytes,
   407  			}
   408  		}
   409  		if res.Reservations != nil {
   410  			resources.Reservations = &types.Resources{
   411  				NanoCPUs:         res.Reservations.NanoCPUs,
   412  				MemoryBytes:      res.Reservations.MemoryBytes,
   413  				GenericResources: GenericResourcesFromGRPC(res.Reservations.Generic),
   414  			}
   415  		}
   416  	}
   417  
   418  	return resources
   419  }
   420  
   421  // GenericResourcesToGRPC converts a GenericResource to a GRPC GenericResource
   422  func GenericResourcesToGRPC(genericRes []types.GenericResource) []*swarmapi.GenericResource {
   423  	var generic []*swarmapi.GenericResource
   424  	for _, res := range genericRes {
   425  		var r *swarmapi.GenericResource
   426  
   427  		if res.DiscreteResourceSpec != nil {
   428  			r = genericresource.NewDiscrete(res.DiscreteResourceSpec.Kind, res.DiscreteResourceSpec.Value)
   429  		} else if res.NamedResourceSpec != nil {
   430  			r = genericresource.NewString(res.NamedResourceSpec.Kind, res.NamedResourceSpec.Value)
   431  		}
   432  
   433  		generic = append(generic, r)
   434  	}
   435  
   436  	return generic
   437  }
   438  
   439  func resourcesToGRPC(res *types.ResourceRequirements) *swarmapi.ResourceRequirements {
   440  	var reqs *swarmapi.ResourceRequirements
   441  	if res != nil {
   442  		reqs = &swarmapi.ResourceRequirements{}
   443  		if res.Limits != nil {
   444  			reqs.Limits = &swarmapi.Resources{
   445  				NanoCPUs:    res.Limits.NanoCPUs,
   446  				MemoryBytes: res.Limits.MemoryBytes,
   447  			}
   448  		}
   449  		if res.Reservations != nil {
   450  			reqs.Reservations = &swarmapi.Resources{
   451  				NanoCPUs:    res.Reservations.NanoCPUs,
   452  				MemoryBytes: res.Reservations.MemoryBytes,
   453  				Generic:     GenericResourcesToGRPC(res.Reservations.GenericResources),
   454  			}
   455  
   456  		}
   457  	}
   458  	return reqs
   459  }
   460  
   461  func restartPolicyFromGRPC(p *swarmapi.RestartPolicy) *types.RestartPolicy {
   462  	var rp *types.RestartPolicy
   463  	if p != nil {
   464  		rp = &types.RestartPolicy{}
   465  
   466  		switch p.Condition {
   467  		case swarmapi.RestartOnNone:
   468  			rp.Condition = types.RestartPolicyConditionNone
   469  		case swarmapi.RestartOnFailure:
   470  			rp.Condition = types.RestartPolicyConditionOnFailure
   471  		case swarmapi.RestartOnAny:
   472  			rp.Condition = types.RestartPolicyConditionAny
   473  		default:
   474  			rp.Condition = types.RestartPolicyConditionAny
   475  		}
   476  
   477  		if p.Delay != nil {
   478  			delay, _ := gogotypes.DurationFromProto(p.Delay)
   479  			rp.Delay = &delay
   480  		}
   481  		if p.Window != nil {
   482  			window, _ := gogotypes.DurationFromProto(p.Window)
   483  			rp.Window = &window
   484  		}
   485  
   486  		rp.MaxAttempts = &p.MaxAttempts
   487  	}
   488  	return rp
   489  }
   490  
   491  func restartPolicyToGRPC(p *types.RestartPolicy) (*swarmapi.RestartPolicy, error) {
   492  	var rp *swarmapi.RestartPolicy
   493  	if p != nil {
   494  		rp = &swarmapi.RestartPolicy{}
   495  
   496  		switch p.Condition {
   497  		case types.RestartPolicyConditionNone:
   498  			rp.Condition = swarmapi.RestartOnNone
   499  		case types.RestartPolicyConditionOnFailure:
   500  			rp.Condition = swarmapi.RestartOnFailure
   501  		case types.RestartPolicyConditionAny:
   502  			rp.Condition = swarmapi.RestartOnAny
   503  		default:
   504  			if string(p.Condition) != "" {
   505  				return nil, fmt.Errorf("invalid RestartCondition: %q", p.Condition)
   506  			}
   507  			rp.Condition = swarmapi.RestartOnAny
   508  		}
   509  
   510  		if p.Delay != nil {
   511  			rp.Delay = gogotypes.DurationProto(*p.Delay)
   512  		}
   513  		if p.Window != nil {
   514  			rp.Window = gogotypes.DurationProto(*p.Window)
   515  		}
   516  		if p.MaxAttempts != nil {
   517  			rp.MaxAttempts = *p.MaxAttempts
   518  
   519  		}
   520  	}
   521  	return rp, nil
   522  }
   523  
   524  func placementFromGRPC(p *swarmapi.Placement) *types.Placement {
   525  	if p == nil {
   526  		return nil
   527  	}
   528  	r := &types.Placement{
   529  		Constraints: p.Constraints,
   530  		MaxReplicas: p.MaxReplicas,
   531  	}
   532  
   533  	for _, pref := range p.Preferences {
   534  		if spread := pref.GetSpread(); spread != nil {
   535  			r.Preferences = append(r.Preferences, types.PlacementPreference{
   536  				Spread: &types.SpreadOver{
   537  					SpreadDescriptor: spread.SpreadDescriptor,
   538  				},
   539  			})
   540  		}
   541  	}
   542  
   543  	for _, plat := range p.Platforms {
   544  		r.Platforms = append(r.Platforms, types.Platform{
   545  			Architecture: plat.Architecture,
   546  			OS:           plat.OS,
   547  		})
   548  	}
   549  
   550  	return r
   551  }
   552  
   553  func driverFromGRPC(p *swarmapi.Driver) *types.Driver {
   554  	if p == nil {
   555  		return nil
   556  	}
   557  
   558  	return &types.Driver{
   559  		Name:    p.Name,
   560  		Options: p.Options,
   561  	}
   562  }
   563  
   564  func driverToGRPC(p *types.Driver) *swarmapi.Driver {
   565  	if p == nil {
   566  		return nil
   567  	}
   568  
   569  	return &swarmapi.Driver{
   570  		Name:    p.Name,
   571  		Options: p.Options,
   572  	}
   573  }
   574  
   575  func updateConfigFromGRPC(updateConfig *swarmapi.UpdateConfig) *types.UpdateConfig {
   576  	if updateConfig == nil {
   577  		return nil
   578  	}
   579  
   580  	converted := &types.UpdateConfig{
   581  		Parallelism:     updateConfig.Parallelism,
   582  		MaxFailureRatio: updateConfig.MaxFailureRatio,
   583  	}
   584  
   585  	converted.Delay = updateConfig.Delay
   586  	if updateConfig.Monitor != nil {
   587  		converted.Monitor, _ = gogotypes.DurationFromProto(updateConfig.Monitor)
   588  	}
   589  
   590  	switch updateConfig.FailureAction {
   591  	case swarmapi.UpdateConfig_PAUSE:
   592  		converted.FailureAction = types.UpdateFailureActionPause
   593  	case swarmapi.UpdateConfig_CONTINUE:
   594  		converted.FailureAction = types.UpdateFailureActionContinue
   595  	case swarmapi.UpdateConfig_ROLLBACK:
   596  		converted.FailureAction = types.UpdateFailureActionRollback
   597  	}
   598  
   599  	switch updateConfig.Order {
   600  	case swarmapi.UpdateConfig_STOP_FIRST:
   601  		converted.Order = types.UpdateOrderStopFirst
   602  	case swarmapi.UpdateConfig_START_FIRST:
   603  		converted.Order = types.UpdateOrderStartFirst
   604  	}
   605  
   606  	return converted
   607  }
   608  
   609  func updateConfigToGRPC(updateConfig *types.UpdateConfig) (*swarmapi.UpdateConfig, error) {
   610  	if updateConfig == nil {
   611  		return nil, nil
   612  	}
   613  
   614  	converted := &swarmapi.UpdateConfig{
   615  		Parallelism:     updateConfig.Parallelism,
   616  		Delay:           updateConfig.Delay,
   617  		MaxFailureRatio: updateConfig.MaxFailureRatio,
   618  	}
   619  
   620  	switch updateConfig.FailureAction {
   621  	case types.UpdateFailureActionPause, "":
   622  		converted.FailureAction = swarmapi.UpdateConfig_PAUSE
   623  	case types.UpdateFailureActionContinue:
   624  		converted.FailureAction = swarmapi.UpdateConfig_CONTINUE
   625  	case types.UpdateFailureActionRollback:
   626  		converted.FailureAction = swarmapi.UpdateConfig_ROLLBACK
   627  	default:
   628  		return nil, fmt.Errorf("unrecognized update failure action %s", updateConfig.FailureAction)
   629  	}
   630  	if updateConfig.Monitor != 0 {
   631  		converted.Monitor = gogotypes.DurationProto(updateConfig.Monitor)
   632  	}
   633  
   634  	switch updateConfig.Order {
   635  	case types.UpdateOrderStopFirst, "":
   636  		converted.Order = swarmapi.UpdateConfig_STOP_FIRST
   637  	case types.UpdateOrderStartFirst:
   638  		converted.Order = swarmapi.UpdateConfig_START_FIRST
   639  	default:
   640  		return nil, fmt.Errorf("unrecognized update order %s", updateConfig.Order)
   641  	}
   642  
   643  	return converted, nil
   644  }
   645  
   646  func networkAttachmentSpecFromGRPC(attachment swarmapi.NetworkAttachmentSpec) *types.NetworkAttachmentSpec {
   647  	return &types.NetworkAttachmentSpec{
   648  		ContainerID: attachment.ContainerID,
   649  	}
   650  }
   651  
   652  func taskSpecFromGRPC(taskSpec swarmapi.TaskSpec) (types.TaskSpec, error) {
   653  	taskNetworks := make([]types.NetworkAttachmentConfig, 0, len(taskSpec.Networks))
   654  	for _, n := range taskSpec.Networks {
   655  		netConfig := types.NetworkAttachmentConfig{Target: n.Target, Aliases: n.Aliases, DriverOpts: n.DriverAttachmentOpts}
   656  		taskNetworks = append(taskNetworks, netConfig)
   657  	}
   658  
   659  	t := types.TaskSpec{
   660  		Resources:     resourcesFromGRPC(taskSpec.Resources),
   661  		RestartPolicy: restartPolicyFromGRPC(taskSpec.Restart),
   662  		Placement:     placementFromGRPC(taskSpec.Placement),
   663  		LogDriver:     driverFromGRPC(taskSpec.LogDriver),
   664  		Networks:      taskNetworks,
   665  		ForceUpdate:   taskSpec.ForceUpdate,
   666  	}
   667  
   668  	switch taskSpec.GetRuntime().(type) {
   669  	case *swarmapi.TaskSpec_Container, nil:
   670  		c := taskSpec.GetContainer()
   671  		if c != nil {
   672  			t.ContainerSpec = containerSpecFromGRPC(c)
   673  		}
   674  	case *swarmapi.TaskSpec_Generic:
   675  		g := taskSpec.GetGeneric()
   676  		if g != nil {
   677  			switch g.Kind {
   678  			case string(types.RuntimePlugin):
   679  				var p runtime.PluginSpec
   680  				if err := proto.Unmarshal(g.Payload.Value, &p); err != nil {
   681  					return t, errors.Wrap(err, "error unmarshalling plugin spec")
   682  				}
   683  				t.PluginSpec = &p
   684  			}
   685  		}
   686  	case *swarmapi.TaskSpec_Attachment:
   687  		a := taskSpec.GetAttachment()
   688  		if a != nil {
   689  			t.NetworkAttachmentSpec = networkAttachmentSpecFromGRPC(*a)
   690  		}
   691  		t.Runtime = types.RuntimeNetworkAttachment
   692  	}
   693  
   694  	return t, nil
   695  }