diff --git a/CHANGELOG.md b/CHANGELOG.md index 98ce840d..a0b99a9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +* Remove multizone and fix URL environment (#4) + ## 0.29.0 ### Features diff --git a/cmd/exoscale-csi-driver/main.go b/cmd/exoscale-csi-driver/main.go index 7ddd741e..64f441fd 100644 --- a/cmd/exoscale-csi-driver/main.go +++ b/cmd/exoscale-csi-driver/main.go @@ -5,6 +5,7 @@ import ( "fmt" "os" + v3 "github.com/exoscale/egoscale/v3" "github.com/exoscale/exoscale-csi-driver/cmd/exoscale-csi-driver/buildinfo" "github.com/exoscale/exoscale-csi-driver/driver" @@ -42,6 +43,7 @@ func main() { apiKey := os.Getenv("EXOSCALE_API_KEY") apiSecret := os.Getenv("EXOSCALE_API_SECRET") + apiURL := os.Getenv("EXOSCALE_API_URL") // The node mode don't need secrets and do not interact with Exoscale API. if *mode != string(driver.NodeMode) && (apiKey == "" || apiSecret == "") { @@ -54,6 +56,7 @@ func main() { Prefix: *prefix, APIKey: apiKey, APISecret: apiSecret, + Zone: v3.URL(apiURL), }) if err != nil { klog.Error(err) diff --git a/driver/controller.go b/driver/controller.go index 64d9ab70..6d97b5c1 100644 --- a/driver/controller.go +++ b/driver/controller.go @@ -77,14 +77,16 @@ const ( ) type controllerService struct { - client *v3.Client - zone v3.URL + client *v3.Client + zone v3.URL + zoneName string } func newControllerService(client *v3.Client, nodeMeta *nodeMetadata) controllerService { return controllerService{ - client: client, - zone: nodeMeta.zone, + client: client, + zone: nodeMeta.zone, + zoneName: nodeMeta.zoneName, } } @@ -107,10 +109,10 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol if v.Name == req.Name { return &csi.CreateVolumeResponse{ Volume: &csi.Volume{ - VolumeId: exoscaleID(d.zone, v.ID), + VolumeId: exoscaleID(d.zoneName, v.ID), // API reply in bytes then send it without conversion CapacityBytes: v.Size, - AccessibleTopology: newZoneTopology(d.zone), + AccessibleTopology: newZoneTopology(d.zoneName), }, }, nil } @@ -127,14 +129,13 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol if srcSnapshot == nil { return nil, status.Error(codes.Internal, "error retrieving snapshot from the volumeContentSource") } - zone, snapshotID, err := getExoscaleID(srcSnapshot.SnapshotId) + _, snapshotID, err := getExoscaleID(srcSnapshot.SnapshotId) if err != nil { klog.Errorf("create volume from snapshot: %v", err) return nil, err } - client := d.client.WithURL(zone) - snapshot, err := client.GetBlockStorageSnapshot(ctx, snapshotID) + snapshot, err := d.client.GetBlockStorageSnapshot(ctx, snapshotID) if err != nil { if errors.Is(err, v3.ErrNotFound) { klog.Errorf("create volume get snapshot not found: %v", err) @@ -182,9 +183,9 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol return &csi.CreateVolumeResponse{ Volume: &csi.Volume{ - VolumeId: exoscaleID(d.zone, opDone.Reference.ID), + VolumeId: exoscaleID(d.zoneName, opDone.Reference.ID), CapacityBytes: sizeInBytes, - AccessibleTopology: newZoneTopology(d.zone), + AccessibleTopology: newZoneTopology(d.zoneName), ContentSource: req.GetVolumeContentSource(), }, }, nil @@ -195,14 +196,13 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol func (d *controllerService) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) { klog.V(4).Infof("DeleteVolume") - zone, volumeID, err := getExoscaleID(req.VolumeId) + _, volumeID, err := getExoscaleID(req.VolumeId) if err != nil { klog.Errorf("parse exoscale volume ID %s: %v", req.VolumeId, err) return nil, err } - client := d.client.WithURL(zone) - op, err := client.DeleteBlockStorageVolume(ctx, volumeID) + op, err := d.client.DeleteBlockStorageVolume(ctx, volumeID) if err != nil { if errors.Is(err, v3.ErrNotFound) { return &csi.DeleteVolumeResponse{}, nil @@ -226,12 +226,11 @@ func (d *controllerService) DeleteVolume(ctx context.Context, req *csi.DeleteVol func (d *controllerService) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) { klog.V(4).Infof("ControllerPublishVolume") - zone, instanceID, err := getExoscaleID(req.NodeId) + _, instanceID, err := getExoscaleID(req.NodeId) if err != nil { klog.Errorf("parse node ID %s: %v", req.NodeId, err) return nil, err } - client := d.client.WithURL(zone) _, volumeID, err := getExoscaleID(req.VolumeId) if err != nil { @@ -239,7 +238,7 @@ func (d *controllerService) ControllerPublishVolume(ctx context.Context, req *cs return nil, err } - volume, err := client.GetBlockStorageVolume(ctx, volumeID) + volume, err := d.client.GetBlockStorageVolume(ctx, volumeID) if err != nil { if errors.Is(err, v3.ErrNotFound) { return nil, status.Errorf(codes.NotFound, "volume %s not found", volumeID) @@ -255,13 +254,13 @@ func (d *controllerService) ControllerPublishVolume(ctx context.Context, req *cs PublishContext: map[string]string{ exoscaleVolumeName: volume.Name, exoscaleVolumeID: volume.ID.String(), - exoscaleVolumeZone: string(zone), + exoscaleVolumeZone: string(d.zoneName), }, }, nil } } - op, err := client.AttachBlockStorageVolumeToInstance(ctx, volumeID, v3.AttachBlockStorageVolumeToInstanceRequest{ + op, err := d.client.AttachBlockStorageVolumeToInstance(ctx, volumeID, v3.AttachBlockStorageVolumeToInstanceRequest{ Instance: &v3.InstanceTarget{ ID: instanceID, }, @@ -271,7 +270,7 @@ func (d *controllerService) ControllerPublishVolume(ctx context.Context, req *cs return nil, err } - _, err = client.Wait(ctx, op, v3.OperationStateSuccess) + _, err = d.client.Wait(ctx, op, v3.OperationStateSuccess) if err != nil { klog.Errorf("wait attach block storage volume %s to instance %s: %v", volumeID, instanceID, err) return nil, err @@ -281,7 +280,7 @@ func (d *controllerService) ControllerPublishVolume(ctx context.Context, req *cs PublishContext: map[string]string{ exoscaleVolumeName: volume.Name, exoscaleVolumeID: volume.ID.String(), - exoscaleVolumeZone: string(zone), + exoscaleVolumeZone: string(d.zoneName), }, }, nil } @@ -292,14 +291,13 @@ func (d *controllerService) ControllerPublishVolume(ctx context.Context, req *cs func (d *controllerService) ControllerUnpublishVolume(ctx context.Context, req *csi.ControllerUnpublishVolumeRequest) (*csi.ControllerUnpublishVolumeResponse, error) { klog.V(4).Infof("ControllerUnpublishVolume") - zone, volumeID, err := getExoscaleID(req.VolumeId) + _, volumeID, err := getExoscaleID(req.VolumeId) if err != nil { klog.Errorf("parse exoscale volume ID %s: %v", req.VolumeId, err) return nil, err } - client := d.client.WithURL(zone) - op, err := client.DetachBlockStorageVolume(ctx, volumeID) + op, err := d.client.DetachBlockStorageVolume(ctx, volumeID) if err != nil { if errors.Is(err, v3.ErrNotFound) || (errors.Is(err, v3.ErrInvalidRequest) && strings.Contains(err.Error(), "Volume not attached")) { @@ -325,14 +323,13 @@ func (d *controllerService) ControllerUnpublishVolume(ctx context.Context, req * // This operation MUST be idempotent. func (d *controllerService) ValidateVolumeCapabilities(ctx context.Context, req *csi.ValidateVolumeCapabilitiesRequest) (*csi.ValidateVolumeCapabilitiesResponse, error) { klog.V(4).Infof("ValidateVolumeCapabilities") - zone, volumeID, err := getExoscaleID(req.VolumeId) + _, volumeID, err := getExoscaleID(req.VolumeId) if err != nil { klog.Errorf("parse exoscale ID %s: %v", req.VolumeId, err) return nil, err } - client := d.client.WithURL(zone) - _, err = client.GetBlockStorageVolume(ctx, volumeID) + _, err = d.client.GetBlockStorageVolume(ctx, volumeID) if err != nil { klog.Errorf("get block storage volume %s: %v", volumeID, err) return nil, err @@ -402,15 +399,15 @@ func (d *controllerService) ListVolumes(ctx context.Context, req *csi.ListVolume for _, v := range volumes { var instancesID []string if v.Instance != nil && v.Instance.ID != "" { - instancesID = append(instancesID, exoscaleID(d.zone, v.Instance.ID)) + instancesID = append(instancesID, exoscaleID(d.zoneName, v.Instance.ID)) } volumesEntries = append(volumesEntries, &csi.ListVolumesResponse_Entry{ Volume: &csi.Volume{ - VolumeId: exoscaleID(d.zone, v.ID), + VolumeId: exoscaleID(d.zoneName, v.ID), // API reply in bytes then send it without conversion CapacityBytes: v.Size, - AccessibleTopology: newZoneTopology(d.zone), + AccessibleTopology: newZoneTopology(d.zoneName), }, Status: &csi.ListVolumesResponse_VolumeStatus{ PublishedNodeIds: instancesID, @@ -451,20 +448,19 @@ func (d *controllerService) ControllerGetCapabilities(ctx context.Context, req * func (d *controllerService) CreateSnapshot(ctx context.Context, req *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error) { klog.V(4).Infof("CreateSnapshot") - zone, volumeID, err := getExoscaleID(req.SourceVolumeId) + _, volumeID, err := getExoscaleID(req.SourceVolumeId) if err != nil { klog.Errorf("parse exoscale ID %s: %v", req.SourceVolumeId, err) return nil, err } - client := d.client.WithURL(zone) - volume, err := client.GetBlockStorageVolume(ctx, volumeID) + volume, err := d.client.GetBlockStorageVolume(ctx, volumeID) if err != nil { klog.Errorf("create snapshot get volume %s: %v", volumeID, err) } for _, s := range volume.BlockStorageSnapshots { - snapshot, err := client.GetBlockStorageSnapshot(ctx, s.ID) + snapshot, err := d.client.GetBlockStorageSnapshot(ctx, s.ID) if err != nil { klog.Errorf("create snapshot get snapshot %s: %v", s.ID, err) } @@ -472,8 +468,8 @@ func (d *controllerService) CreateSnapshot(ctx context.Context, req *csi.CreateS if snapshot.Name == req.Name { return &csi.CreateSnapshotResponse{ Snapshot: &csi.Snapshot{ - SnapshotId: exoscaleID(zone, snapshot.ID), - SourceVolumeId: exoscaleID(zone, volume.ID), + SnapshotId: exoscaleID(d.zoneName, snapshot.ID), + SourceVolumeId: exoscaleID(d.zoneName, volume.ID), CreationTime: timestamppb.New(snapshot.CreatedAT), ReadyToUse: true, SizeBytes: volume.Size, @@ -482,7 +478,7 @@ func (d *controllerService) CreateSnapshot(ctx context.Context, req *csi.CreateS } } - op, err := client.CreateBlockStorageSnapshot(ctx, volume.ID, v3.CreateBlockStorageSnapshotRequest{ + op, err := d.client.CreateBlockStorageSnapshot(ctx, volume.ID, v3.CreateBlockStorageSnapshotRequest{ Name: req.Name, }) if err != nil { @@ -510,8 +506,8 @@ func (d *controllerService) CreateSnapshot(ctx context.Context, req *csi.CreateS return &csi.CreateSnapshotResponse{ Snapshot: &csi.Snapshot{ - SnapshotId: exoscaleID(zone, snapshot.ID), - SourceVolumeId: exoscaleID(zone, volume.ID), + SnapshotId: exoscaleID(d.zoneName, snapshot.ID), + SourceVolumeId: exoscaleID(d.zoneName, volume.ID), CreationTime: timestamppb.New(snapshot.CreatedAT), ReadyToUse: true, SizeBytes: volume.Size, @@ -523,14 +519,13 @@ func (d *controllerService) CreateSnapshot(ctx context.Context, req *csi.CreateS func (d *controllerService) DeleteSnapshot(ctx context.Context, req *csi.DeleteSnapshotRequest) (*csi.DeleteSnapshotResponse, error) { klog.V(4).Infof("DeleteSnapshot") - zone, snapshotID, err := getExoscaleID(req.SnapshotId) + _, snapshotID, err := getExoscaleID(req.SnapshotId) if err != nil { klog.Errorf("parse exoscale snapshot ID %s: %v", req.SnapshotId, err) return nil, err } - client := d.client.WithURL(zone) - op, err := client.DeleteBlockStorageSnapshot(ctx, snapshotID) + op, err := d.client.DeleteBlockStorageSnapshot(ctx, snapshotID) if err != nil { if errors.Is(err, v3.ErrNotFound) { return &csi.DeleteSnapshotResponse{}, nil @@ -538,7 +533,7 @@ func (d *controllerService) DeleteSnapshot(ctx context.Context, req *csi.DeleteS return nil, err } - if _, err := client.Wait(ctx, op, v3.OperationStateSuccess); err != nil { + if _, err := d.client.Wait(ctx, op, v3.OperationStateSuccess); err != nil { return nil, err } @@ -587,8 +582,8 @@ func (d *controllerService) ListSnapshots(ctx context.Context, req *csi.ListSnap for _, s := range snapshots { snapshotsEntries = append(snapshotsEntries, &csi.ListSnapshotsResponse_Entry{ Snapshot: &csi.Snapshot{ - SourceVolumeId: exoscaleID(d.zone, s.BlockStorageVolume.ID), - SnapshotId: exoscaleID(d.zone, s.ID), + SourceVolumeId: exoscaleID(d.zoneName, s.BlockStorageVolume.ID), + SnapshotId: exoscaleID(d.zoneName, s.ID), CreationTime: timestamppb.New(s.CreatedAT), ReadyToUse: true, // TODO SizeBytes @@ -611,14 +606,13 @@ func (d *controllerService) ControllerExpandVolume(ctx context.Context, req *csi // ControllerGetVolume gets a volume and return it. func (d *controllerService) ControllerGetVolume(ctx context.Context, req *csi.ControllerGetVolumeRequest) (*csi.ControllerGetVolumeResponse, error) { - zone, volumeID, err := getExoscaleID(req.VolumeId) + _, volumeID, err := getExoscaleID(req.VolumeId) if err != nil { klog.Errorf("parse exoscale ID %s: %v", req.VolumeId, err) return nil, err } - client := d.client.WithURL(zone) - volume, err := client.GetBlockStorageVolume(ctx, volumeID) + volume, err := d.client.GetBlockStorageVolume(ctx, volumeID) if err != nil { if errors.Is(err, v3.ErrNotFound) { return nil, status.Errorf(codes.NotFound, "volume %s not found", volumeID) @@ -630,12 +624,12 @@ func (d *controllerService) ControllerGetVolume(ctx context.Context, req *csi.Co var instancesID []string if volume.Instance != nil && volume.Instance.ID != "" { - instancesID = append(instancesID, exoscaleID(d.zone, volume.Instance.ID)) + instancesID = append(instancesID, exoscaleID(d.zoneName, volume.Instance.ID)) } return &csi.ControllerGetVolumeResponse{ Volume: &csi.Volume{ - VolumeId: exoscaleID(zone, volume.ID), + VolumeId: exoscaleID(d.zoneName, volume.ID), // API reply in bytes then send it without conversion CapacityBytes: volume.Size, }, diff --git a/driver/driver.go b/driver/driver.go index 336ff498..d9e1c308 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -48,6 +48,7 @@ type DriverConfig struct { Mode Mode APIKey, APISecret string RestConfig *rest.Config + Zone v3.URL } // Driver implements the interfaces csi.IdentityServer, csi.ControllerServer and csi.NodeServer @@ -71,10 +72,14 @@ func NewDriver(config *DriverConfig) (*Driver, error) { config: config, } + var zone = nodeMeta.zone + if config.Zone != "" { + zone = config.Zone + } var client *v3.Client if config.Mode != NodeMode { client, err = v3.NewClient(config.APIKey, config.APISecret, - v3.ClientOptWithURL(nodeMeta.zone), + v3.ClientOptWithURL(zone), ) if err != nil { return nil, fmt.Errorf("new driver: %w", err) @@ -173,6 +178,7 @@ func (d *Driver) Run() error { type nodeMetadata struct { zone v3.URL + zoneName string InstanceID v3.UUID } @@ -220,6 +226,7 @@ func getExoscaleNodeMetadata() (*nodeMetadata, error) { return &nodeMetadata{ zone: zone, + zoneName: region, InstanceID: instanceID, }, nil } diff --git a/driver/helpers.go b/driver/helpers.go index c29739f0..ee968c9e 100644 --- a/driver/helpers.go +++ b/driver/helpers.go @@ -11,12 +11,13 @@ import ( v3 "github.com/exoscale/egoscale/v3" ) -func exoscaleID(zone v3.URL, id v3.UUID) string { - z, _ := zone.Zone() - return fmt.Sprintf("%s/%s", z, id) +// TODO (pej) multizone: Add v3.URL back once url environment are fixed. +func exoscaleID(zoneName string, id v3.UUID) string { + return fmt.Sprintf("%s/%s", zoneName, id) } -func getExoscaleID(exoID string) (v3.URL, v3.UUID, error) { +// TODO (pej) multizone: Add v3.URL back once url environment are fixed. +func getExoscaleID(exoID string) (string, v3.UUID, error) { s := strings.Split(exoID, "/") if len(s) != 2 { return "", "", fmt.Errorf("malformed exoscale id") @@ -27,19 +28,14 @@ func getExoscaleID(exoID string) (v3.URL, v3.UUID, error) { return "", "", err } - zone, ok := v3.Zones[s[0]] - if !ok { - return "", "", fmt.Errorf("invalid zone name: %s", s[0]) - } - - return zone, id, nil + return s[0], id, nil } -func newZoneTopology(zone v3.URL) []*csi.Topology { - z, _ := zone.Zone() +// TODO (pej) multizone: Add v3.URL back once url environment are fixed. +func newZoneTopology(zoneName string) []*csi.Topology { return []*csi.Topology{ { - Segments: map[string]string{ZoneTopologyKey: z}, + Segments: map[string]string{ZoneTopologyKey: zoneName}, }, } } diff --git a/driver/node.go b/driver/node.go index f75c2669..ee33423e 100644 --- a/driver/node.go +++ b/driver/node.go @@ -22,6 +22,7 @@ const ( type nodeService struct { nodeID v3.UUID zone v3.URL + zoneName string diskUtils *diskUtils } @@ -29,6 +30,7 @@ func newNodeService(meta *nodeMetadata) nodeService { return nodeService{ nodeID: meta.InstanceID, zone: meta.zone, + zoneName: meta.zoneName, diskUtils: newDiskUtils(), } } @@ -413,11 +415,11 @@ func (d *nodeService) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoReque klog.V(4).Infof("NodeGetInfo") return &csi.NodeGetInfoResponse{ // Store the zone and the instanceID to let the CSI controller know the zone of the node. - NodeId: exoscaleID(d.zone, d.nodeID), + NodeId: exoscaleID(d.zoneName, d.nodeID), // TODO Will depend on Exoscale account limit, (remove const) MaxVolumesPerNode: maxVolumesPerNode, // newZoneTopology returns always len(1). - AccessibleTopology: newZoneTopology(d.zone)[0], + AccessibleTopology: newZoneTopology(d.zoneName)[0], }, nil }