package convert // import "github.com/docker/docker/daemon/cluster/convert" import ( volumetypes "github.com/docker/docker/api/types/volume" gogotypes "github.com/gogo/protobuf/types" swarmapi "github.com/moby/swarmkit/v2/api" ) // VolumeFromGRPC converts a swarmkit api Volume object to a docker api Volume // object func VolumeFromGRPC(v *swarmapi.Volume) volumetypes.Volume { clusterVolumeSpec := volumetypes.ClusterVolumeSpec{ Group: v.Spec.Group, AccessMode: accessModeFromGRPC(v.Spec.AccessMode), AccessibilityRequirements: topologyRequirementFromGRPC(v.Spec.AccessibilityRequirements), CapacityRange: capacityRangeFromGRPC(v.Spec.CapacityRange), Secrets: volumeSecretsFromGRPC(v.Spec.Secrets), Availability: volumeAvailabilityFromGRPC(v.Spec.Availability), } clusterVolume := &volumetypes.ClusterVolume{ ID: v.ID, Spec: clusterVolumeSpec, PublishStatus: volumePublishStatusFromGRPC(v.PublishStatus), Info: volumeInfoFromGRPC(v.VolumeInfo), } clusterVolume.Version.Index = v.Meta.Version.Index clusterVolume.CreatedAt, _ = gogotypes.TimestampFromProto(v.Meta.CreatedAt) clusterVolume.UpdatedAt, _ = gogotypes.TimestampFromProto(v.Meta.UpdatedAt) return volumetypes.Volume{ ClusterVolume: clusterVolume, CreatedAt: clusterVolume.CreatedAt.String(), Driver: v.Spec.Driver.Name, Labels: v.Spec.Annotations.Labels, Name: v.Spec.Annotations.Name, Options: v.Spec.Driver.Options, Scope: "global", } } func volumeSpecToGRPC(spec volumetypes.ClusterVolumeSpec) *swarmapi.VolumeSpec { swarmSpec := &swarmapi.VolumeSpec{ Group: spec.Group, } if spec.AccessMode != nil { swarmSpec.AccessMode = &swarmapi.VolumeAccessMode{} switch spec.AccessMode.Scope { case volumetypes.ScopeSingleNode: swarmSpec.AccessMode.Scope = swarmapi.VolumeScopeSingleNode case volumetypes.ScopeMultiNode: swarmSpec.AccessMode.Scope = swarmapi.VolumeScopeMultiNode } switch spec.AccessMode.Sharing { case volumetypes.SharingNone: swarmSpec.AccessMode.Sharing = swarmapi.VolumeSharingNone case volumetypes.SharingReadOnly: swarmSpec.AccessMode.Sharing = swarmapi.VolumeSharingReadOnly case volumetypes.SharingOneWriter: swarmSpec.AccessMode.Sharing = swarmapi.VolumeSharingOneWriter case volumetypes.SharingAll: swarmSpec.AccessMode.Sharing = swarmapi.VolumeSharingAll } if spec.AccessMode.BlockVolume != nil { swarmSpec.AccessMode.AccessType = &swarmapi.VolumeAccessMode_Block{ Block: &swarmapi.VolumeAccessMode_BlockVolume{}, } } if spec.AccessMode.MountVolume != nil { swarmSpec.AccessMode.AccessType = &swarmapi.VolumeAccessMode_Mount{ Mount: &swarmapi.VolumeAccessMode_MountVolume{ FsType: spec.AccessMode.MountVolume.FsType, MountFlags: spec.AccessMode.MountVolume.MountFlags, }, } } } for _, secret := range spec.Secrets { swarmSpec.Secrets = append(swarmSpec.Secrets, &swarmapi.VolumeSecret{ Key: secret.Key, Secret: secret.Secret, }) } if spec.AccessibilityRequirements != nil { swarmSpec.AccessibilityRequirements = &swarmapi.TopologyRequirement{} for _, top := range spec.AccessibilityRequirements.Requisite { swarmSpec.AccessibilityRequirements.Requisite = append( swarmSpec.AccessibilityRequirements.Requisite, &swarmapi.Topology{ Segments: top.Segments, }, ) } for _, top := range spec.AccessibilityRequirements.Preferred { swarmSpec.AccessibilityRequirements.Preferred = append( swarmSpec.AccessibilityRequirements.Preferred, &swarmapi.Topology{ Segments: top.Segments, }, ) } } if spec.CapacityRange != nil { swarmSpec.CapacityRange = &swarmapi.CapacityRange{ RequiredBytes: spec.CapacityRange.RequiredBytes, LimitBytes: spec.CapacityRange.LimitBytes, } } // availability is not a pointer, it is a value. if the user does not // specify an availability, it will be inferred as the 0-value, which is // "active". switch spec.Availability { case volumetypes.AvailabilityActive: swarmSpec.Availability = swarmapi.VolumeAvailabilityActive case volumetypes.AvailabilityPause: swarmSpec.Availability = swarmapi.VolumeAvailabilityPause case volumetypes.AvailabilityDrain: swarmSpec.Availability = swarmapi.VolumeAvailabilityDrain } return swarmSpec } // VolumeCreateToGRPC takes a VolumeCreateBody and outputs the matching // swarmapi VolumeSpec. func VolumeCreateToGRPC(volume *volumetypes.CreateOptions) *swarmapi.VolumeSpec { var swarmSpec *swarmapi.VolumeSpec if volume != nil && volume.ClusterVolumeSpec != nil { swarmSpec = volumeSpecToGRPC(*volume.ClusterVolumeSpec) } else { swarmSpec = &swarmapi.VolumeSpec{} } swarmSpec.Annotations = swarmapi.Annotations{ Name: volume.Name, Labels: volume.Labels, } swarmSpec.Driver = &swarmapi.Driver{ Name: volume.Driver, Options: volume.DriverOpts, } return swarmSpec } func volumeInfoFromGRPC(info *swarmapi.VolumeInfo) *volumetypes.Info { if info == nil { return nil } var accessibleTopology []volumetypes.Topology if info.AccessibleTopology != nil { accessibleTopology = make([]volumetypes.Topology, len(info.AccessibleTopology)) for i, top := range info.AccessibleTopology { accessibleTopology[i] = topologyFromGRPC(top) } } return &volumetypes.Info{ CapacityBytes: info.CapacityBytes, VolumeContext: info.VolumeContext, VolumeID: info.VolumeID, AccessibleTopology: accessibleTopology, } } func volumePublishStatusFromGRPC(publishStatus []*swarmapi.VolumePublishStatus) []*volumetypes.PublishStatus { if publishStatus == nil { return nil } vps := make([]*volumetypes.PublishStatus, len(publishStatus)) for i, status := range publishStatus { var state volumetypes.PublishState switch status.State { case swarmapi.VolumePublishStatus_PENDING_PUBLISH: state = volumetypes.StatePending case swarmapi.VolumePublishStatus_PUBLISHED: state = volumetypes.StatePublished case swarmapi.VolumePublishStatus_PENDING_NODE_UNPUBLISH: state = volumetypes.StatePendingNodeUnpublish case swarmapi.VolumePublishStatus_PENDING_UNPUBLISH: state = volumetypes.StatePendingUnpublish } vps[i] = &volumetypes.PublishStatus{ NodeID: status.NodeID, State: state, PublishContext: status.PublishContext, } } return vps } func accessModeFromGRPC(accessMode *swarmapi.VolumeAccessMode) *volumetypes.AccessMode { if accessMode == nil { return nil } convertedAccessMode := &volumetypes.AccessMode{} switch accessMode.Scope { case swarmapi.VolumeScopeSingleNode: convertedAccessMode.Scope = volumetypes.ScopeSingleNode case swarmapi.VolumeScopeMultiNode: convertedAccessMode.Scope = volumetypes.ScopeMultiNode } switch accessMode.Sharing { case swarmapi.VolumeSharingNone: convertedAccessMode.Sharing = volumetypes.SharingNone case swarmapi.VolumeSharingReadOnly: convertedAccessMode.Sharing = volumetypes.SharingReadOnly case swarmapi.VolumeSharingOneWriter: convertedAccessMode.Sharing = volumetypes.SharingOneWriter case swarmapi.VolumeSharingAll: convertedAccessMode.Sharing = volumetypes.SharingAll } if block := accessMode.GetBlock(); block != nil { convertedAccessMode.BlockVolume = &volumetypes.TypeBlock{} } if mount := accessMode.GetMount(); mount != nil { convertedAccessMode.MountVolume = &volumetypes.TypeMount{ FsType: mount.FsType, MountFlags: mount.MountFlags, } } return convertedAccessMode } func volumeSecretsFromGRPC(secrets []*swarmapi.VolumeSecret) []volumetypes.Secret { if secrets == nil { return nil } convertedSecrets := make([]volumetypes.Secret, len(secrets)) for i, secret := range secrets { convertedSecrets[i] = volumetypes.Secret{ Key: secret.Key, Secret: secret.Secret, } } return convertedSecrets } func topologyRequirementFromGRPC(top *swarmapi.TopologyRequirement) *volumetypes.TopologyRequirement { if top == nil { return nil } convertedTop := &volumetypes.TopologyRequirement{} if top.Requisite != nil { convertedTop.Requisite = make([]volumetypes.Topology, len(top.Requisite)) for i, req := range top.Requisite { convertedTop.Requisite[i] = topologyFromGRPC(req) } } if top.Preferred != nil { convertedTop.Preferred = make([]volumetypes.Topology, len(top.Preferred)) for i, pref := range top.Preferred { convertedTop.Preferred[i] = topologyFromGRPC(pref) } } return convertedTop } func topologyFromGRPC(top *swarmapi.Topology) volumetypes.Topology { if top == nil { return volumetypes.Topology{} } return volumetypes.Topology{ Segments: top.Segments, } } func capacityRangeFromGRPC(capacity *swarmapi.CapacityRange) *volumetypes.CapacityRange { if capacity == nil { return nil } return &volumetypes.CapacityRange{ RequiredBytes: capacity.RequiredBytes, LimitBytes: capacity.LimitBytes, } } func volumeAvailabilityFromGRPC(availability swarmapi.VolumeSpec_VolumeAvailability) volumetypes.Availability { switch availability { case swarmapi.VolumeAvailabilityActive: return volumetypes.AvailabilityActive case swarmapi.VolumeAvailabilityPause: return volumetypes.AvailabilityPause } return volumetypes.AvailabilityDrain }