Skip to content

Commit

Permalink
upgrade: version-based migrations
Browse files Browse the repository at this point in the history
Implement migrations based on the metadata version instead of just
unmarshalling and re-marshalling the config.
  • Loading branch information
chrboe committed Nov 6, 2023
1 parent 8723022 commit f72c2c3
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 53 deletions.
2 changes: 1 addition & 1 deletion pkg/reactor/reactor.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type PromoterMetadata struct {

// PromoterConfig is the configuration for drbd-reactors "promoter" plugin.
type PromoterConfig struct {
ID string `toml:"id"`
ID string `toml:"id,omitempty"`
Resources map[string]PromoterResourceConfig `toml:"resources,omitempty"`
Metadata PromoterMetadata `toml:"metadata,omitempty"`
}
Expand Down
18 changes: 18 additions & 0 deletions pkg/upgrade/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package upgrade

import "github.com/LINBIT/linstor-gateway/pkg/reactor"

// removeID unsets the ID field of the promoter config.
// Starting with drbd-reactor v1.2.0, configs no longer require an ID. It
// prints a deprecation warning when it is used, so remove the field.
func removeID(cfg *reactor.PromoterConfig) error {
cfg.ID = ""
return nil
}

func firstResourceId(cfg *reactor.PromoterConfig) string {
for k := range cfg.Resources {
return k
}
return ""
}
27 changes: 19 additions & 8 deletions pkg/upgrade/iscsi.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,40 @@ import (
"fmt"
"github.com/LINBIT/golinstor/client"
"github.com/LINBIT/linstor-gateway/pkg/iscsi"
"github.com/LINBIT/linstor-gateway/pkg/reactor"
)

// iscsiMigrations defines the operations for upgrading a single version.
// The array index ("n") denotes the starting version; the function at
// index "n" migrates from version "n" to version "n+1".
var iscsiMigrations = []func(cfg *reactor.PromoterConfig) error{
0: removeID,
}

func upgradeIscsi(ctx context.Context, linstor *client.Client, name string, forceYes bool, dryRun bool) (bool, error) {
const gatewayConfigPath = "/etc/drbd-reactor.d/linstor-gateway-iscsi-%s.toml"
cfg, resourceDefinition, volumeDefinitions, resources, err := parseExistingConfig(ctx, linstor, fmt.Sprintf(gatewayConfigPath, name))
cfg, _, _, _, err := parseExistingConfig(ctx, linstor, fmt.Sprintf(gatewayConfigPath, name))
if err != nil {
return false, err
}
if cfg.Metadata.LinstorGatewaySchemaVersion > iscsi.CurrentVersion {
return false, fmt.Errorf("schema version %d is not supported",
cfg.Metadata.LinstorGatewaySchemaVersion)
}

parsedCfg, err := iscsi.FromPromoter(cfg, resourceDefinition, volumeDefinitions)
newCfg, _, _, _, err := parseExistingConfig(ctx, linstor, fmt.Sprintf(gatewayConfigPath, name))
if err != nil {
return false, fmt.Errorf("failed to parse config: %w", err)
return false, err
}
newConfig, err := parsedCfg.ToPromoter(resources)
if err != nil {
return false, fmt.Errorf("failed to generate config: %w", err)

for i := cfg.Metadata.LinstorGatewaySchemaVersion; i < iscsi.CurrentVersion; i++ {
err := iscsiMigrations[i](newCfg)
if err != nil {
return false, fmt.Errorf("failed to migrate from version %d to %d: %w", i, i+1, err)
}
}
newCfg.Metadata.LinstorGatewaySchemaVersion = iscsi.CurrentVersion

return maybeWriteNewConfig(ctx, linstor, cfg, newConfig, forceYes, dryRun)
return maybeWriteNewConfig(ctx, linstor, cfg, newCfg, forceYes, dryRun)
}

func Iscsi(ctx context.Context, linstor *client.Client, iqn iscsi.Iqn, forceYes bool, dryRun bool) error {
Expand Down
82 changes: 74 additions & 8 deletions pkg/upgrade/nfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,92 @@ import (
"context"
"fmt"
"github.com/LINBIT/golinstor/client"
"github.com/LINBIT/linstor-gateway/pkg/iscsi"
"github.com/LINBIT/linstor-gateway/pkg/nfs"
"github.com/LINBIT/linstor-gateway/pkg/reactor"
)

// nfsMigrations defines the operations for upgrading a single version.
// The array index ("n") denotes the starting version; the function at
// index "n" migrates from version "n" to version "n+1".
var nfsMigrations = []func(cfg *reactor.PromoterConfig) error{
0: initialMigrations,
}

// initialMigrations combines two migrations that make up version 1.
func initialMigrations(cfg *reactor.PromoterConfig) error {
if err := removeID(cfg); err != nil {
return err
}
return switchIpAndNfsServer(cfg)
}

// switchIpAndNfsServer changes the order of the resource agents such that
// the service IP is started before the NFS server. This was previously not
// the case, leading to an NFS server that is not limited to the service IP.
func switchIpAndNfsServer(cfg *reactor.PromoterConfig) error {
id := firstResourceId(cfg)
firstResource := cfg.Resources[id]
var serviceIpIndex, nfsServerIndex int
for i, entry := range firstResource.Start {
switch agent := entry.(type) {
case *reactor.ResourceAgent:
if agent.Type == "ocf:heartbeat:IPaddr2" {
serviceIpIndex = i
}
if agent.Type == "ocf:heartbeat:nfsserver" {
nfsServerIndex = i
}
}
}

// slice up the "start" list and create a new one with this order:
// 1. everything before the nfs server
// 2. the service IP
// 3. the NFS server
// 4. all the exportfs entries
// 5. everything after the original service IP index
start := firstResource.Start[:nfsServerIndex]
serviceIpEntry := firstResource.Start[serviceIpIndex]
nfsServerEntry := firstResource.Start[nfsServerIndex]
exportFsEntries := firstResource.Start[nfsServerIndex+1 : serviceIpIndex]
rest := firstResource.Start[serviceIpIndex+1:]

var s []reactor.StartEntry
s = append(s, start...)
s = append(s, serviceIpEntry)
s = append(s, nfsServerEntry)
s = append(s, exportFsEntries...)
s = append(s, rest...)
firstResource.Start = s
cfg.Resources[id] = firstResource
return nil
}

func upgradeNfs(ctx context.Context, linstor *client.Client, name string, forceYes bool, dryRun bool) (bool, error) {
const gatewayConfigPath = "/etc/drbd-reactor.d/linstor-gateway-nfs-%s.toml"
cfg, resourceDefinition, volumeDefinitions, resources, err := parseExistingConfig(ctx, linstor, fmt.Sprintf(gatewayConfigPath, name))
cfg, _, _, _, err := parseExistingConfig(ctx, linstor, fmt.Sprintf(gatewayConfigPath, name))
if err != nil {
return false, err
}

parsedCfg, err := nfs.FromPromoter(cfg, resourceDefinition, volumeDefinitions)
if err != nil {
return false, fmt.Errorf("failed to parse config: %w", err)
if cfg.Metadata.LinstorGatewaySchemaVersion > iscsi.CurrentVersion {
return false, fmt.Errorf("schema version %d is not supported",
cfg.Metadata.LinstorGatewaySchemaVersion)
}
newConfig, err := parsedCfg.ToPromoter(resources)
newCfg, _, _, _, err := parseExistingConfig(ctx, linstor, fmt.Sprintf(gatewayConfigPath, name))
if err != nil {
return false, fmt.Errorf("failed to generate config: %w", err)
return false, err
}

for i := cfg.Metadata.LinstorGatewaySchemaVersion; i < nfs.CurrentVersion; i++ {
err := nfsMigrations[i](newCfg)
if err != nil {
return false, fmt.Errorf("failed to migrate from version %d to %d: %w", i, i+1, err)
}
}
newCfg.Metadata.LinstorGatewaySchemaVersion = nfs.CurrentVersion

return maybeWriteNewConfig(ctx, linstor, cfg, newConfig, forceYes, dryRun)
return maybeWriteNewConfig(ctx, linstor, cfg, newCfg, forceYes, dryRun)
}

func Nfs(ctx context.Context, linstor *client.Client, name string, forceYes bool, dryRun bool) error {
Expand Down
31 changes: 23 additions & 8 deletions pkg/upgrade/nvmeof.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,40 @@ import (
"fmt"
"github.com/LINBIT/golinstor/client"
"github.com/LINBIT/linstor-gateway/pkg/nvmeof"
"github.com/LINBIT/linstor-gateway/pkg/reactor"
)

// nvmeOfMigrations defines the operations for upgrading a single version.
// The array index ("n") denotes the starting version; the function at
// index "n" migrates from version "n" to version "n+1".
var nvmeOfMigrations = []func(cfg *reactor.PromoterConfig) error{
0: removeID,
}

func upgradeNvmeOf(ctx context.Context, linstor *client.Client, name string, forceYes, dryRun bool) (bool, error) {
const gatewayConfigPath = "/etc/drbd-reactor.d/linstor-gateway-nvmeof-%s.toml"
cfg, resourceDefinition, volumeDefinitions, resources, err := parseExistingConfig(ctx, linstor, fmt.Sprintf(gatewayConfigPath, name))
cfg, _, _, _, err := parseExistingConfig(ctx, linstor, fmt.Sprintf(gatewayConfigPath, name))
if err != nil {
return false, err
}

parsedCfg, err := nvmeof.FromPromoter(cfg, resourceDefinition, volumeDefinitions)
if err != nil {
return false, fmt.Errorf("failed to parse config: %w", err)
if cfg.Metadata.LinstorGatewaySchemaVersion > nvmeof.CurrentVersion {
return false, fmt.Errorf("schema version %d is not supported",
cfg.Metadata.LinstorGatewaySchemaVersion)
}
newConfig, err := parsedCfg.ToPromoter(resources)
newCfg, _, _, _, err := parseExistingConfig(ctx, linstor, fmt.Sprintf(gatewayConfigPath, name))
if err != nil {
return false, fmt.Errorf("failed to generate config: %w", err)
return false, err
}

for i := cfg.Metadata.LinstorGatewaySchemaVersion; i < nvmeof.CurrentVersion; i++ {
err := nvmeOfMigrations[i](newCfg)
if err != nil {
return false, fmt.Errorf("failed to migrate from version %d to %d: %w", i, i+1, err)
}
}
newCfg.Metadata.LinstorGatewaySchemaVersion = nvmeof.CurrentVersion

return maybeWriteNewConfig(ctx, linstor, cfg, newConfig, forceYes, dryRun)
return maybeWriteNewConfig(ctx, linstor, cfg, newCfg, forceYes, dryRun)
}

func NvmeOf(ctx context.Context, linstor *client.Client, nqn nvmeof.Nqn, forceYes, dryRun bool) error {
Expand Down
28 changes: 0 additions & 28 deletions pkg/upgrade/upgrade.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,6 @@
/*
Package upgrade migrates existing resources to the latest version.
We will always try to upgrade to the most modern version of the drbd-reactor configuration.
In practice, this means the following DRBD options:
options {
auto-promote no;
quorum majority;
on-suspended-primary-outdated force-secondary;
on-no-quorum io-error;
}
And the following drbd-reactor settings:
[[promoter]]
id = "iscsi-target1"
[promoter.resources]
[promoter.resources.target1]
on-drbd-demote-failure = "reboot-immediate"
runner = "systemd"
start = [
"ocf:heartbeat:Filesystem fs_cluster_private device=/dev/drbd/by-res/target1/0 directory=/srv/ha/internal/target1 fstype=ext4 run_fsck=no",
"ocf:heartbeat:portblock pblock0 action=block ip=192.168.122.222 portno=3260 protocol=tcp",
"ocf:heartbeat:IPaddr2 service_ip0 cidr_netmask=24 ip=192.168.122.222",
"ocf:heartbeat:iSCSITarget target allowed_initiators='' incoming_password='' incoming_username='' iqn=iqn.2020-01.com.linbit:target1 portals=192.168.122.222:3260",
"ocf:heartbeat:iSCSILogicalUnit lu1 lun=1 path=/dev/drbd/by-res/target1/1 product_id='LINSTOR iSCSI' scsi_sn=6391f15d target_iqn=iqn.2020-01.com.linbit:target1",
"ocf:heartbeat:portblock portunblock0 action=unblock ip=192.168.122.222 portno=3260 protocol=tcp tickle_dir=/srv/ha/internal/target1",
]
stop-services-on-exit = true
target-as = "Requires"
*/
package upgrade

Expand Down

0 comments on commit f72c2c3

Please sign in to comment.