Merge "Removes BMH specific logic in the airshipctl move command. Also removes pkg dependencies that were introduced by the BMH logic."

This commit is contained in:
Zuul 2020-10-05 16:43:01 +00:00 committed by Gerrit Code Review
commit 8c180daf4e
4 changed files with 50 additions and 1103 deletions

15
go.mod
View File

@ -3,10 +3,16 @@ module opendev.org/airship/airshipctl
go 1.13
require (
github.com/Azure/go-autorest/autorest v0.11.7 // indirect
github.com/Masterminds/goutils v1.1.0 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible
github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/chai2010/gettext-go v0.0.0-20170215093142-bf70f2a70fb1 // indirect
github.com/cheggaaa/pb/v3 v3.0.4
github.com/containerd/containerd v1.4.1 // indirect
github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect
@ -16,12 +22,12 @@ require (
github.com/gorilla/mux v1.7.4 // indirect
github.com/gregjones/httpcache v0.0.0-20190212212710-3befbb6ad0cc // indirect
github.com/huandu/xstrings v1.3.1 // indirect
github.com/metal3-io/baremetal-operator v0.0.0-20200501205115-2c0dc9997bfa
github.com/onsi/gomega v1.9.0
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.0.0
github.com/stretchr/testify v1.4.0
golang.org/x/tools v0.0.0-20200619210111-0f592d2728bb // indirect
k8s.io/api v0.17.4
k8s.io/apiextensions-apiserver v0.17.4
k8s.io/apimachinery v0.17.4
@ -37,10 +43,7 @@ require (
sigs.k8s.io/yaml v1.2.0
)
// Required by baremetal-operator:
replace (
github.com/Azure/go-autorest => github.com/Azure/go-autorest v13.3.2+incompatible // Required by OLM
github.com/russross/blackfriday => github.com/russross/blackfriday v1.5.2
k8s.io/client-go => k8s.io/client-go v0.0.0-20191114101535-6c5935290e33
k8s.io/kubectl => k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd
sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.4.1

677
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -15,46 +15,21 @@
package client
import (
"context"
"opendev.org/airship/airshipctl/pkg/log"
bmoapis "github.com/metal3-io/baremetal-operator/pkg/apis"
bmh "github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clusterctlclient "sigs.k8s.io/cluster-api/cmd/clusterctl/client"
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
"sigs.k8s.io/controller-runtime/pkg/client"
)
func init() {
//nolint:errcheck
bmoapis.AddToScheme(cluster.Scheme)
}
// Move implements interface to Clusterctl
func (c *Client) Move(fromKubeconfigPath, fromKubeconfigContext,
toKubeconfigPath, toKubeconfigContext, namespace string) error {
ctx := context.TODO()
var err error
// ephemeral cluster client
pFrom := cluster.New(cluster.Kubeconfig{
Path: fromKubeconfigPath,
Context: fromKubeconfigContext}, nil).Proxy()
cFrom, err := pFrom.NewClient()
if err != nil {
return errors.Wrap(err, "failed to create ephemeral cluster client")
}
// target cluster client
pTo := cluster.New(cluster.Kubeconfig{
Path: toKubeconfigPath,
Context: toKubeconfigContext}, nil).Proxy()
cTo, err := pTo.NewClient()
if err != nil {
return errors.Wrap(err, "failed to create target cluster client")
}
// If namespace is empty, try to detect it.
if namespace == "" {
var currentNamespace string
@ -64,11 +39,6 @@ func (c *Client) Move(fromKubeconfigPath, fromKubeconfigContext,
}
namespace = currentNamespace
}
// Pause
err = pauseUnpauseBMHs(ctx, cFrom, namespace, true)
if err != nil {
return errors.Wrap(err, "failed to pause BareMetalHost objects")
}
// clusterctl move
c.moveOptions = clusterctlclient.MoveOptions{
@ -80,90 +50,5 @@ func (c *Client) Move(fromKubeconfigPath, fromKubeconfigContext,
if err != nil {
return errors.Wrapf(err, "error during clusterctl move")
}
// Update BMH Status
err = copyBMHStatus(ctx, cFrom, cTo, namespace)
if err != nil {
return errors.Wrap(err, "failed to copy BareMetalHost Status")
}
// Unpause
err = pauseUnpauseBMHs(ctx, cFrom, namespace, false)
if err != nil {
return errors.Wrap(err, "failed to unpause BareMetalHost objects")
}
return err
}
// copyBMHStatus will copy the BareMetalHost Status field from a specific
// cluser to a target cluster.
func copyBMHStatus(ctx context.Context, cFrom client.Client, cTo client.Client, namespace string) error {
fromHosts, err := getBMHs(ctx, cFrom, namespace)
if err != nil {
return errors.Wrap(err, "failed to list BareMetalHost objects")
}
toHosts, err := getBMHs(ctx, cTo, namespace)
if err != nil {
return errors.Wrap(err, "failed to list BMH objects")
}
// Copy the Status field from old BMH to new BMH
log.Debugf("Copying BareMetalHost status to target cluster")
for i := range toHosts.Items {
var found bool
t := metav1.Now()
for _, fromHost := range fromHosts.Items {
if fromHost.Name == toHosts.Items[i].Name {
toHosts.Items[i].Status = fromHost.Status
found = true
break
}
}
if !found {
return errors.Errorf("BMH with the same name %s/%s not found in the source cluster",
toHosts.Items[i].Name, namespace)
}
toHosts.Items[i].Status.LastUpdated = &t
err = cTo.Status().Update(ctx, &toHosts.Items[i])
if err != nil {
return errors.Wrap(err, "failed to update BareMetalHost status")
}
}
return nil
}
// pauseUnpauseBMHs will add/remove the pause annotation from the
// BareMetalHost objects.
func pauseUnpauseBMHs(ctx context.Context, crClient client.Client, namespace string, pause bool) error {
hosts, err := getBMHs(ctx, crClient, namespace)
if err != nil {
return errors.Wrap(err, "failed to list BMH objects")
}
for i := range hosts.Items {
annotations := hosts.Items[i].GetAnnotations()
if annotations == nil {
hosts.Items[i].Annotations = map[string]string{}
}
if pause {
log.Debugf("Pausing BareMetalHost object %s/%s", hosts.Items[i].Name, namespace)
hosts.Items[i].Annotations[bmh.PausedAnnotation] = "true"
} else {
log.Debugf("Unpausing BareMetalHost object %s/%s", hosts.Items[i].Name, namespace)
delete(hosts.Items[i].Annotations, bmh.PausedAnnotation)
}
if err := crClient.Update(ctx, &hosts.Items[i]); err != nil {
return errors.Wrapf(err, "error updating BareMetalHost %q %s/%s",
hosts.Items[i].GroupVersionKind(), hosts.Items[i].GetNamespace(), hosts.Items[i].GetName())
}
}
return nil
}
// getBMHs will return all BareMetalHost objects in the specified namespace.
// It also checks to see if the BareMetalHost resource is installed, if not,
// it will return false.
func getBMHs(ctx context.Context, crClient client.Client, namespace string) (bmh.BareMetalHostList, error) {
hosts := bmh.BareMetalHostList{}
opts := &client.ListOptions{
Namespace: namespace,
}
err := crClient.List(ctx, &hosts, opts)
return hosts, err
}

View File

@ -1,344 +0,0 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package client
import (
"context"
"testing"
. "github.com/onsi/gomega"
bmoapis "github.com/metal3-io/baremetal-operator/pkg/apis"
bmh "github.com/metal3-io/baremetal-operator/pkg/apis/metal3/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
)
var bmh1 = &bmh.BareMetalHost{
TypeMeta: metav1.TypeMeta{
APIVersion: "metal3.io/v1alpha1",
Kind: "BareMetalHost",
},
ObjectMeta: metav1.ObjectMeta{
Name: "bmh1",
Namespace: "ns1",
},
Spec: bmh.BareMetalHostSpec{
Online: false,
BootMACAddress: "00:2e:30:d7:11:19",
},
Status: bmh.BareMetalHostStatus{
HardwareProfile: "bmh1-hw-profile",
},
}
var bmh2 = &bmh.BareMetalHost{
TypeMeta: metav1.TypeMeta{
APIVersion: "metal3.io/v1alpha1",
Kind: "BareMetalHost",
},
ObjectMeta: metav1.ObjectMeta{
Name: "bmh2",
Namespace: "ns1",
},
Spec: bmh.BareMetalHostSpec{
Online: false,
BootMACAddress: "01:23:45:67:89:ab",
},
Status: bmh.BareMetalHostStatus{
HardwareProfile: "bmh2-hw-profile",
},
}
func newClientWithBMHObject() client.Client {
scheme := runtime.NewScheme()
//nolint:errcheck
bmoapis.AddToScheme(scheme)
return fake.NewFakeClientWithScheme(scheme, bmh1)
}
func newClientWithTwoBMHObjects() client.Client {
scheme := runtime.NewScheme()
//nolint:errcheck
bmoapis.AddToScheme(scheme)
return fake.NewFakeClientWithScheme(scheme, bmh1, bmh2)
}
func newClientWithNoBMHObject() client.Client {
scheme := runtime.NewScheme()
//nolint:errcheck
bmoapis.AddToScheme(scheme)
return fake.NewFakeClientWithScheme(scheme)
}
func Test_move_getBMHs(t *testing.T) {
type args struct {
c client.Client
namespace string
}
type want struct {
bmhList bmh.BareMetalHostList
}
tests := []struct {
name string
args args
wantErr bool
want want
}{
{
name: "returns a BareMetalHost object",
args: args{
c: newClientWithBMHObject(),
namespace: "ns1",
},
wantErr: false,
want: want{
bmhList: bmh.BareMetalHostList{
Items: []bmh.BareMetalHost{*bmh1},
},
},
},
{
name: "returns multiple BareMetalHost object",
args: args{
c: newClientWithTwoBMHObjects(),
namespace: "ns1",
},
wantErr: false,
want: want{
bmhList: bmh.BareMetalHostList{
Items: []bmh.BareMetalHost{*bmh1, *bmh2},
},
},
},
{
name: "returns an empty list of BareMetalHost objects",
args: args{
c: newClientWithNoBMHObject(),
namespace: "ns2",
},
wantErr: false,
want: want{
bmhList: bmh.BareMetalHostList{},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
bmhList, err := getBMHs(context.TODO(), tt.args.c, tt.args.namespace)
if tt.wantErr {
g.Expect(err).To(HaveOccurred())
return
}
g.Expect(err).NotTo(HaveOccurred())
g.Expect(len(bmhList.Items)).To(BeEquivalentTo(len(tt.want.bmhList.Items)))
})
}
}
func Test_move_pauseUnpauseBMHs(t *testing.T) {
type args struct {
c client.Client
namespace string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "pause and unpause a single BareMetalHost object",
args: args{
c: newClientWithBMHObject(),
namespace: "ns1",
},
wantErr: false,
},
{
name: "pause and unpause multiple BareMetalHost objects",
args: args{
c: newClientWithTwoBMHObjects(),
namespace: "ns1",
},
wantErr: false,
},
{
name: "pause and unpause should do nothing when there is no BareMetalHost object present",
args: args{
c: newClientWithNoBMHObject(),
namespace: "ns2",
},
wantErr: false,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
err := pauseUnpauseBMHs(context.TODO(), tt.args.c, tt.args.namespace, true)
if tt.wantErr {
g.Expect(err).To(HaveOccurred())
return
}
g.Expect(err).NotTo(HaveOccurred())
bmhList, err := getBMHs(context.TODO(), tt.args.c, tt.args.namespace)
g.Expect(err).NotTo(HaveOccurred())
for _, host := range bmhList.Items {
g.Expect(host.Annotations[bmh.PausedAnnotation]).To(Equal("true"))
}
err = pauseUnpauseBMHs(context.TODO(), tt.args.c, tt.args.namespace, false)
if tt.wantErr {
g.Expect(err).To(HaveOccurred())
return
}
g.Expect(err).NotTo(HaveOccurred())
bmhList, err = getBMHs(context.TODO(), tt.args.c, tt.args.namespace)
g.Expect(err).NotTo(HaveOccurred())
for _, host := range bmhList.Items {
_, present := host.Annotations[bmh.PausedAnnotation]
g.Expect(present).To(Equal(false))
}
})
}
}
var bmh1NoStatus = &bmh.BareMetalHost{
TypeMeta: metav1.TypeMeta{
APIVersion: "metal3.io/v1alpha1",
Kind: "BareMetalHost",
},
ObjectMeta: metav1.ObjectMeta{
Name: "bmh1",
Namespace: "ns1",
},
Spec: bmh.BareMetalHostSpec{
Online: false,
BootMACAddress: "00:2e:30:d7:11:19",
},
}
var bmh2NoStatus = &bmh.BareMetalHost{
TypeMeta: metav1.TypeMeta{
APIVersion: "metal3.io/v1alpha1",
Kind: "BareMetalHost",
},
ObjectMeta: metav1.ObjectMeta{
Name: "bmh2",
Namespace: "ns1",
},
Spec: bmh.BareMetalHostSpec{
Online: false,
BootMACAddress: "01:23:45:67:89:ab",
},
}
func newClientFromCluster() client.Client {
scheme := runtime.NewScheme()
//nolint:errcheck
bmoapis.AddToScheme(scheme)
return fake.NewFakeClientWithScheme(scheme, bmh1, bmh2)
}
func newClientToCluster() client.Client {
scheme := runtime.NewScheme()
//nolint:errcheck
bmoapis.AddToScheme(scheme)
return fake.NewFakeClientWithScheme(scheme, bmh1NoStatus, bmh2NoStatus)
}
func Test_move_copyBMHStatus(t *testing.T) {
type args struct {
cFrom client.Client
cTo client.Client
namespace string
}
type want struct {
bmhList bmh.BareMetalHostList
}
tests := []struct {
name string
args args
wantErr bool
want want
}{
{
name: "copies the status field of multiple BareMetalHost objects",
args: args{
cFrom: newClientFromCluster(),
cTo: newClientToCluster(),
namespace: "ns1",
},
wantErr: false,
want: want{
bmhList: bmh.BareMetalHostList{
Items: []bmh.BareMetalHost{*bmh1, *bmh2},
},
},
},
{
name: "no copy occurs b/c no BareMetalHost objects are present",
args: args{
cFrom: newClientWithNoBMHObject(),
cTo: newClientWithNoBMHObject(),
namespace: "ns1",
},
wantErr: false,
want: want{
bmhList: bmh.BareMetalHostList{
Items: []bmh.BareMetalHost{},
},
},
},
{
name: "error should occur b/c BareMetalHost does not exist in the source cluster",
args: args{
cFrom: newClientWithNoBMHObject(),
cTo: newClientToCluster(),
namespace: "ns1",
},
wantErr: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
err := copyBMHStatus(context.TODO(), tt.args.cFrom, tt.args.cTo, tt.args.namespace)
if tt.wantErr {
g.Expect(err).To(HaveOccurred())
return
}
g.Expect(err).NotTo(HaveOccurred())
bmhList, err := getBMHs(context.TODO(), tt.args.cTo, tt.args.namespace)
g.Expect(err).NotTo(HaveOccurred())
NEXTHOST:
for _, host := range bmhList.Items {
for _, wantHost := range tt.want.bmhList.Items {
if host.Name == wantHost.Name {
g.Expect(host.Status.HardwareProfile).To(Equal(wantHost.Status.HardwareProfile))
continue NEXTHOST
}
}
t.Errorf("unexpected host %s", host.Name)
}
})
}
}