Skip to content

Commit

Permalink
[ENGDTR-4307] Allow msr2 and msr3 products to coexist (#488)
Browse files Browse the repository at this point in the history
* Seperate out MSR inlined config into two seperate products

* Allow both MSR2 and MSR3 to coexist
* Refactor describe to iterate through both product types and
  remove uses of switch.
* Refactor GatherFacts and ValidateFacts phases to validate
  both products.
* Refactor phase to just check for config non-nil and remove
  uses of switch.
* Rename MSR to MSR2 and rename files, packages and struct
  entries.

Signed-off-by: Kyle Squizzato <[email protected]>
Co-authored-by: James Nesbitt <[email protected]>
  • Loading branch information
squizzi and james-nesbitt authored Jul 31, 2024
1 parent 41149a0 commit 95a2422
Show file tree
Hide file tree
Showing 41 changed files with 728 additions and 708 deletions.
3 changes: 2 additions & 1 deletion cmd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

"github.com/Mirantis/mcc/pkg/config"
"github.com/Mirantis/mcc/pkg/product/mke/api"
"github.com/kballard/go-shellquote"
"github.com/urfave/cli/v2"
)
Expand Down Expand Up @@ -63,7 +64,7 @@ func NewExecCommand() *cli.Command {

args := ctx.Args().Slice()

err = product.Exec(ctx.StringSlice("target"), ctx.Bool("interactive"), ctx.Bool("first"), ctx.Bool("all"), ctx.Bool("parallel"), ctx.String("role"), ctx.String("os"), shellquote.Join(args...))
err = product.Exec(ctx.StringSlice("target"), ctx.Bool("interactive"), ctx.Bool("first"), ctx.Bool("all"), ctx.Bool("parallel"), api.RoleType(ctx.String("role")), ctx.String("os"), shellquote.Join(args...))
if err != nil {
return fmt.Errorf("failed to execute command: %w", err)
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
// needed to load the migrators.
_ "github.com/Mirantis/mcc/pkg/config/migration/v14"
// needed to load the migrators.
_ "github.com/Mirantis/mcc/pkg/config/migration/v15"
// needed to load the migrators.
_ "github.com/Mirantis/mcc/pkg/config/migration/v1beta1"
// needed to load the migrators.
_ "github.com/Mirantis/mcc/pkg/config/migration/v1beta2"
Expand Down
28 changes: 28 additions & 0 deletions pkg/config/migration/v15/v15.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package v15

import (
"github.com/Mirantis/mcc/pkg/config/migration"
log "github.com/sirupsen/logrus"
)

// Migrate migrates a v1.5 format configuration into the v1.6 api format and
// replaces the contents of the supplied data byte slice.
func Migrate(plain map[string]interface{}) error {
plain["apiVersion"] = "launchpad.mirantis.com/mke/v1.6"
if spec, ok := plain["spec"].(map[interface{}]interface{}); ok {
if msr, ok := spec["msr"].(map[interface{}]interface{}); ok {
// Convert msr key to msr2.
spec["msr2"] = msr
delete(spec, "msr")
}
}

log.Debugf("migrated configuration from launchpad.mirantis.com/v1.5 to launchpad.mirantis.com/mke/v1.6")
log.Infof("Note: The configuration has been migrated from a previous version")
log.Infof(" to see the migrated configuration use: launchpad describe config")
return nil
}

func init() {
migration.Register("launchpad.mirantis.com/mke/v1.5", Migrate)
}
73 changes: 73 additions & 0 deletions pkg/config/migration/v15/v15_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package v15

import (
"testing"

"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
)

func TestVersionMigration(t *testing.T) {
v15 := []byte(`apiVersion: launchpad.mirantis.com/mke/v1.5
kind: mke
spec:
mcr:
channel: stable
installURLLinux: https://get.mirantis.com/
installURLWindows: https://get.mirantis.com/install.ps1
repoURL: https://repos.mirantis.com
version: 23.0.8
mke:
adminPassword: miradmin
adminUsername: admin
imageRepo: docker.io/mirantis
installFlags:
- --san=pgedaray-mke-lb-1477b84d031720d6.elb.us-west-1.amazonaws.com
- --nodeport-range=32768-35535
upgradeFlags:
- --force-recent-backup
- --force-minimums
version: 3.7.3
msr:
imageRepo: docker.io/mirantis
installFlags:
- --nfs-options nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport
- --nfs-storage-url nfs://nfs.example.com/
replicaIDs: sequential
version: 2.9.15
`)
v16 := []byte(`apiVersion: launchpad.mirantis.com/mke/v1.6
kind: mke
spec:
mcr:
channel: stable
installURLLinux: https://get.mirantis.com/
installURLWindows: https://get.mirantis.com/install.ps1
repoURL: https://repos.mirantis.com
version: 23.0.8
mke:
adminPassword: miradmin
adminUsername: admin
imageRepo: docker.io/mirantis
installFlags:
- --san=pgedaray-mke-lb-1477b84d031720d6.elb.us-west-1.amazonaws.com
- --nodeport-range=32768-35535
upgradeFlags:
- --force-recent-backup
- --force-minimums
version: 3.7.3
msr2:
imageRepo: docker.io/mirantis
installFlags:
- --nfs-options nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport
- --nfs-storage-url nfs://nfs.example.com/
replicaIDs: sequential
version: 2.9.15
`)
in := make(map[string]interface{})
require.NoError(t, yaml.Unmarshal(v15, in))
require.NoError(t, Migrate(in))
out, err := yaml.Marshal(in)
require.NoError(t, err)
require.Equal(t, string(v16), string(out))
}
2 changes: 0 additions & 2 deletions pkg/constant/constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ const (
MSROperatorDeploymentLabels = "app.kubernetes.io/name=msr-operator"
// KubeConfigFile is the name of the kubeconfig file.
KubeConfigFile = "kube.yml"
// MSRNodeSelector is the node selector for MSR nodes.
MSRNodeSelector = "node-role.kubernetes.io/msr"
// DefaultStorageClassAnnotation is the annotation to set a StorageClass to the default.
DefaultStorageClassAnnotation = "storageclass.kubernetes.io/is-default-class"
)
Expand Down
38 changes: 2 additions & 36 deletions pkg/kubeclient/msr.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"k8s.io/client-go/dynamic"
)

// GetMSRCR get the MSR Custom Resource instance.
func (kc *KubeClient) GetMSRCR(ctx context.Context, name string, rc dynamic.ResourceInterface) (*unstructured.Unstructured, error) {
unstructured, err := rc.Get(ctx, name, metav1.GetOptions{})
if err != nil {
Expand Down Expand Up @@ -153,39 +154,6 @@ func (kc *KubeClient) ApplyMSRCR(ctx context.Context, obj *unstructured.Unstruct
return nil
}

// PrepareNodeForMSR updates the given node name setting the MSRNodeSelector
// on the node and removing any found Kubernetes NoExecute taints added by MKE.
func (kc *KubeClient) PrepareNodeForMSR(ctx context.Context, name string) error {
node, err := kc.client.CoreV1().Nodes().Get(ctx, name, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("failed to get node %q: %w", name, err)
}

if node.Labels == nil {
node.Labels = make(map[string]string)
}

node.Labels[constant.MSRNodeSelector] = "true"

// Rebuild the taints list without the NoExecute taint if found.
taints := []corev1.Taint{}
for _, t := range node.Spec.Taints {
if t.Key == constant.KubernetesOrchestratorTaint && t.Value == "NoExecute" {
continue
}
taints = append(taints, t)
}

node.Spec.Taints = taints

_, err = kc.client.CoreV1().Nodes().Update(ctx, node, metav1.UpdateOptions{})
if err != nil {
return fmt.Errorf("failed to update node %q: %w", name, err)
}

return nil
}

// GetMSRResourceClient returns a dynamic client for the MSR custom resource.
//
//nolint:ireturn
Expand Down Expand Up @@ -261,9 +229,7 @@ func (kc *KubeClient) MSRURL(ctx context.Context, name string, rc dynamic.Resour

switch serviceType {
case string(corev1.ServiceTypeNodePort):
nodes, err := kc.client.CoreV1().Nodes().List(ctx, metav1.ListOptions{
LabelSelector: constant.MSRNodeSelector + "=true",
})
nodes, err := kc.client.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("failed to list nodes: %w", err)
}
Expand Down
31 changes: 0 additions & 31 deletions pkg/kubeclient/msr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"testing"

"github.com/Mirantis/mcc/pkg/constant"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
appsv1 "k8s.io/api/apps/v1"
Expand Down Expand Up @@ -99,33 +98,6 @@ func TestApplyMSRCR(t *testing.T) {
})
}

func TestPrepareNodeForMSR(t *testing.T) {
kc := NewTestClient(t)

kc.client.CoreV1().Nodes().Create(context.Background(), &corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node1",
},
Spec: corev1.NodeSpec{
Taints: []corev1.Taint{
{
Key: constant.KubernetesOrchestratorTaint,
Value: "NoExecute",
},
},
},
}, metav1.CreateOptions{})

err := kc.PrepareNodeForMSR(context.Background(), "node1")
assert.NoError(t, err)

actualNode, err := kc.client.CoreV1().Nodes().Get(context.Background(), "node1", metav1.GetOptions{})
require.NoError(t, err)

assert.Equal(t, "true", actualNode.Labels[constant.MSRNodeSelector])
assert.Empty(t, actualNode.Spec.Taints)
}

func TestMSRURL(t *testing.T) {
t.Run("no spec.service.externalHTTPSPort", func(t *testing.T) {
kc := NewTestClient(t)
Expand Down Expand Up @@ -258,9 +230,6 @@ func TestMSRURL(t *testing.T) {
kc.client.CoreV1().Nodes().Create(context.Background(), &corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "msr-node1",
Labels: map[string]string{
constant.MSRNodeSelector: "true",
},
},
Status: corev1.NodeStatus{
Addresses: []corev1.NodeAddress{
Expand Down
38 changes: 18 additions & 20 deletions pkg/msr/msr2/msr2.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import (
)

// CollectFacts gathers the current status of the installed MSR setup.
func CollectFacts(h *api.Host) (*api.MSRMetadata, error) {
func CollectFacts(h *api.Host) (*api.MSR2Metadata, error) {
rethinkdbContainerID, err := h.ExecOutput(h.Configurer.DockerCommandf(`ps -aq --filter name=dtr-rethinkdb`))
if err != nil {
return nil, fmt.Errorf("failed to get MSR container ID: %w", err)
}
if rethinkdbContainerID == "" {
return &api.MSRMetadata{Installed: false}, nil
return &api.MSR2Metadata{Installed: false}, nil
}

version, err := h.ExecOutput(h.Configurer.DockerCommandf(`inspect %s --format '{{ index .Config.Labels "com.docker.dtr.version"}}'`, rethinkdbContainerID))
Expand Down Expand Up @@ -55,13 +55,11 @@ func CollectFacts(h *api.Host) (*api.MSRMetadata, error) {
bootstrapimage = fmt.Sprintf("%s/dtr:%s", repo, version)
}

msrMeta := &api.MSRMetadata{
Installed: true,
InstalledVersion: version,
MSR2: api.MSR2Metadata{
InstalledBootstrapImage: bootstrapimage,
ReplicaID: replicaID,
},
msrMeta := &api.MSR2Metadata{
Installed: true,
InstalledVersion: version,
InstalledBootstrapImage: bootstrapimage,
ReplicaID: replicaID,
}

return msrMeta, nil
Expand Down Expand Up @@ -115,8 +113,8 @@ func FormatReplicaID(num uint64) string {
// BuildMKEFlags builds the mkeFlags []string consisting of mke installFlags
// that are shared with MSR.
func BuildMKEFlags(config *api.ClusterConfig) common.Flags {
mkeUser := config.Spec.MSR.V2.InstallFlags.GetValue("--ucp-username")
mkePass := config.Spec.MSR.V2.InstallFlags.GetValue("--ucp-password")
mkeUser := config.Spec.MSR2.InstallFlags.GetValue("--ucp-username")
mkePass := config.Spec.MSR2.InstallFlags.GetValue("--ucp-password")

if mkeUser == "" {
mkeUser = config.Spec.MKE.AdminUsername
Expand Down Expand Up @@ -149,7 +147,7 @@ func mkeURLHost(config *api.ClusterConfig) string {
// installer if it fails.
func Destroy(h *api.Host, config *api.ClusterConfig) error {
mkeFlags := BuildMKEFlags(config)
cmd := fmt.Sprintf("run -it --rm mirantis/dtr:%s destroy --ucp-insecure-tls --ucp-url %s --ucp-username %s --ucp-password %s --replica-id %s", h.MSRMetadata.InstalledVersion, mkeFlags.GetValue("--ucp-url"), mkeFlags.GetValue("--ucp-username"), mkeFlags.GetValue("--ucp-password"), h.MSRMetadata.MSR2.ReplicaID)
cmd := fmt.Sprintf("run -it --rm mirantis/dtr:%s destroy --ucp-insecure-tls --ucp-url %s --ucp-username %s --ucp-password %s --replica-id %s", h.MSR2Metadata.InstalledVersion, mkeFlags.GetValue("--ucp-url"), mkeFlags.GetValue("--ucp-username"), mkeFlags.GetValue("--ucp-password"), h.MSR2Metadata.ReplicaID)
if err := h.Exec(h.Configurer.DockerCommandf(cmd)); err != nil {
return fmt.Errorf("failed to run MSR destroy: %w", err)
}
Expand Down Expand Up @@ -203,18 +201,18 @@ var errMaxReplicaID = fmt.Errorf("max sequential msr replica id exceeded")

// AssignSequentialReplicaIDs goes through all the MSR hosts, finds the highest replica id and assigns sequential ones starting from that to all the hosts without replica ids.
func AssignSequentialReplicaIDs(c *api.ClusterConfig) error {
msrHosts := c.Spec.MSRs()
msrHosts := c.Spec.MSR2s()

// find the largest replica id
var maxReplicaID uint64
err := msrHosts.Each(func(h *api.Host) error {
if h.MSRMetadata == nil {
h.MSRMetadata = &api.MSRMetadata{}
if h.MSR2Metadata == nil {
h.MSR2Metadata = &api.MSR2Metadata{}
}
if h.MSRMetadata.MSR2.ReplicaID != "" {
ri, err := strconv.ParseUint(h.MSRMetadata.MSR2.ReplicaID, 16, 48)
if h.MSR2Metadata.ReplicaID != "" {
ri, err := strconv.ParseUint(h.MSR2Metadata.ReplicaID, 16, 48)
if err != nil {
return fmt.Errorf("%s: invalid MSR replicaID '%s': %w", h, h.MSRMetadata.MSR2.ReplicaID, err)
return fmt.Errorf("%s: invalid MSR replicaID '%s': %w", h, h.MSR2Metadata.ReplicaID, err)
}
if maxReplicaID < ri {
maxReplicaID = ri
Expand All @@ -230,9 +228,9 @@ func AssignSequentialReplicaIDs(c *api.ClusterConfig) error {
}

err = msrHosts.Each(func(h *api.Host) error {
if h.MSRMetadata.MSR2.ReplicaID == "" {
if h.MSR2Metadata.ReplicaID == "" {
maxReplicaID++
h.MSRMetadata.MSR2.ReplicaID = FormatReplicaID(maxReplicaID)
h.MSR2Metadata.ReplicaID = FormatReplicaID(maxReplicaID)
}
return nil
})
Expand Down
20 changes: 9 additions & 11 deletions pkg/msr/msr2/msr_test.go → pkg/msr/msr2/msr2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func TestBuildMKEFlags(t *testing.T) {
"--san ucp.acme.com",
},
},
MSR: &api.MSRConfig{},
MSR2: &api.MSR2Config{},
},
}

Expand Down Expand Up @@ -89,19 +89,17 @@ func TestSequentialReplicaIDs(t *testing.T) {
config := &api.ClusterConfig{
Spec: &api.ClusterSpec{
Hosts: []*api.Host{
{Role: "msr"},
{Role: "msr", MSRMetadata: &api.MSRMetadata{MSR2: api.MSR2Metadata{
{Role: "msr2"},
{Role: "msr2", MSR2Metadata: &api.MSR2Metadata{
ReplicaID: "00000000001f",
}}},
{Role: "msr"},
},
MSR: &api.MSRConfig{
V2: api.MSR2Config{ReplicaIDs: "sequential"},
}},
{Role: "msr2"},
},
MSR2: &api.MSR2Config{ReplicaIDs: "sequential"},
},
}
require.NoError(t, AssignSequentialReplicaIDs(config))
require.Equal(t, "000000000020", config.Spec.Hosts[0].MSRMetadata.MSR2.ReplicaID)
require.Equal(t, "00000000001f", config.Spec.Hosts[1].MSRMetadata.MSR2.ReplicaID)
require.Equal(t, "000000000021", config.Spec.Hosts[2].MSRMetadata.MSR2.ReplicaID)
require.Equal(t, "000000000020", config.Spec.Hosts[0].MSR2Metadata.ReplicaID)
require.Equal(t, "00000000001f", config.Spec.Hosts[1].MSR2Metadata.ReplicaID)
require.Equal(t, "000000000021", config.Spec.Hosts[2].MSR2Metadata.ReplicaID)
}
Loading

0 comments on commit 95a2422

Please sign in to comment.