diff --git a/examples/baremetal-tinkerbell-machinedeployment.yaml b/examples/baremetal-tinkerbell-machinedeployment.yaml deleted file mode 100644 index ff3ed3c6e..000000000 --- a/examples/baremetal-tinkerbell-machinedeployment.yaml +++ /dev/null @@ -1,115 +0,0 @@ -apiVersion: "cluster.k8s.io/v1alpha1" -kind: MachineDeployment -metadata: - name: << MACHINE_NAME >> - namespace: kube-system -spec: - replicas: 1 - strategy: - type: RollingUpdate - rollingUpdate: - maxSurge: 1 - maxUnavailable: 0 - selector: - matchLabels: - name: << MACHINE_NAME >> - template: - metadata: - labels: - name: << MACHINE_NAME >> - spec: - providerSpec: - value: - sshPublicKeys: - - "<< YOUR_PUBLIC_KEY >>" - cloudProvider: "baremetal" - cloudProviderSpec: - driver: "tinkerbell" - metadataClientConfig: - endpoint: - secretKeyRef: - namespace: kube-system - name: machine-controller-baremetal-tb - key: endpoint - authMethod: - secretKeyRef: - namespace: kube-system - name: machine-controller-baremetal-tb - key: authMethod - username: - secretKeyRef: - namespace: kube-system - name: machine-controller-baremetal-tb - key: username - password: - secretKeyRef: - namespace: kube-system - name: machine-controller-baremetal-tb - key: password - token: - secretKeyRef: - namespace: kube-system - name: machine-controller-baremetal-tb - key: token - driverSpec: - provisionerIPAddress: << PROVISIONER_IP_ADDRESS >> - mirrorHost: << MIRROR_HOST >> - hardware: - id: << MACHINE_NAME >> - metadata: - facility: - facilitycode: << FACILITY_CODE >> - planslug: << PLAN_SLUG >> - state: "" - instance: - operatingsystemversion: - distro: << OS_NAME >> - imagetag: << IMAGE_TAG >> - osslug: << OS_NAME >> - slug: << OS_NAME >> - version: << OS_VERSION >> - storage: - disks: - - device: /dev/sda - wipetable: true - partitions: - - size: 4096 - label: BIOS - number: 1 - - size: 3993600 - label: SWAP - number: 2 - - size: 0 - label: ROOT - number: 3 - filesystems: - - mount: - point: / - create: - options: - - -L - - ROOT - device: /dev/sda3 - format: ext4 - - mount: - point: none - create: - options: - - -L - - SWAP - device: /dev/sda2 - format: swap - network: - interfaces: - - dhcp: - arch: x86_64 - uefi: false - netboot: - allowpxe: false - allowworkflow: false - operatingSystem: "<< OS_NAME >>" - operatingSystemSpec: - distUpgradeOnBoot: false - disableAutoUpdate: true - versions: - kubelet: "<< KUBERNETES_VERSION >>" diff --git a/examples/operating-system-manager.yaml b/examples/operating-system-manager.yaml index addd31559..0bf8da089 100644 --- a/examples/operating-system-manager.yaml +++ b/examples/operating-system-manager.yaml @@ -201,7 +201,6 @@ spec: - aws - openstack - fake - - baremetal - external type: string spec: @@ -919,7 +918,6 @@ spec: - aws - openstack - fake - - baremetal - external type: string spec: diff --git a/pkg/cloudprovider/provider.go b/pkg/cloudprovider/provider.go index 52e4a6aea..d52f93c5c 100644 --- a/pkg/cloudprovider/provider.go +++ b/pkg/cloudprovider/provider.go @@ -21,7 +21,6 @@ import ( cloudprovidercache "github.com/kubermatic/machine-controller/pkg/cloudprovider/cache" "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/aws" - "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/baremetal" "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/fake" "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/openstack" cloudprovidertypes "github.com/kubermatic/machine-controller/pkg/cloudprovider/types" @@ -45,10 +44,6 @@ var ( providerconfigtypes.CloudProviderFake: func(cvr *providerconfig.ConfigVarResolver) cloudprovidertypes.Provider { return fake.New(cvr) }, - providerconfigtypes.CloudProviderBaremetal: func(cvr *providerconfig.ConfigVarResolver) cloudprovidertypes.Provider { - // TODO(MQ): add a baremetal driver. - return baremetal.New(cvr) - }, } ) diff --git a/pkg/cloudprovider/provider/baremetal/plugins/driver.go b/pkg/cloudprovider/provider/baremetal/plugins/driver.go deleted file mode 100644 index 0147e1907..000000000 --- a/pkg/cloudprovider/provider/baremetal/plugins/driver.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2021 The Machine Controller Authors. - -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 - - http://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 plugins - -import ( - "context" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" -) - -type Driver string - -const Tinkerbell Driver = "tinkerbell" - -type CloudConfigSettings struct { - Token string - Namespace string - SecretName string - ClusterHost string -} - -// PluginDriver manages the communications between the machine controller cloud provider and the bare metal env. -type PluginDriver interface { - GetServer(context.Context, types.UID, runtime.RawExtension) (Server, error) - Validate(runtime.RawExtension) error - ProvisionServer(context.Context, types.UID, *CloudConfigSettings, runtime.RawExtension) (Server, error) - DeprovisionServer(context.Context, types.UID) error -} - -// Server represents the server/instance which exists in the bare metal env. -type Server interface { - GetName() string - GetID() string - GetIPAddress() string - GetMACAddress() string - GetStatus() string -} diff --git a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/common.go b/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/common.go deleted file mode 100644 index 524168e09..000000000 --- a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/common.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2021 The Machine Controller Authors. - -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 - - http://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 contains a client wrapper for Tinkerbell. -package client - -import ( - "errors" -) - -// ErrNotFound is returned if a requested resource is not found. -var ErrNotFound = errors.New("resource not found") - -// than parsing for these specific error message. -const ( - sqlErrorString = "rpc error: code = Unknown desc = sql: no rows in result set" - sqlErrorStringAlt = "rpc error: code = Unknown desc = SELECT: sql: no rows in result set" - sqlErrorNotFound = "rpc error: code = NotFound desc = not found" -) diff --git a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/hardware.go b/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/hardware.go deleted file mode 100644 index c8ccf6c98..000000000 --- a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/hardware.go +++ /dev/null @@ -1,111 +0,0 @@ -/* -Copyright 2021 The Machine Controller Authors. - -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 - - http://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" - "errors" - "fmt" - "strings" - - "github.com/google/uuid" - "github.com/tinkerbell/tink/protos/hardware" - "google.golang.org/grpc" -) - -// Hardware client for Tinkerbell. -type Hardware struct { - client hardware.HardwareServiceClient -} - -// NewHardwareClient returns a Hardware client. -func NewHardwareClient(client hardware.HardwareServiceClient) *Hardware { - return &Hardware{client: client} -} - -// Create Tinkerbell Hardware. -func (t *Hardware) Create(ctx context.Context, h *hardware.Hardware) error { - if h == nil { - return errors.New("hardware should not be nil") - } - - if h.GetId() == "" { - h.Id = uuid.New().String() - } - - if _, err := t.client.Push(ctx, &hardware.PushRequest{Data: h}); err != nil { - return fmt.Errorf("creating hardware in Tinkerbell: %w", err) - } - - return nil -} - -// Update Tinkerbell Hardware. -func (t *Hardware) Update(ctx context.Context, h *hardware.Hardware) error { - if _, err := t.client.Push(ctx, &hardware.PushRequest{Data: h}); err != nil { - return fmt.Errorf("updating template in Tinkerbell: %w", err) - } - - return nil -} - -// Get returns a Tinkerbell Hardware. -func (t *Hardware) Get(ctx context.Context, id, ip, mac string) (*hardware.Hardware, error) { - var method func(context.Context, *hardware.GetRequest, ...grpc.CallOption) (*hardware.Hardware, error) - - req := &hardware.GetRequest{} - - switch { - case id != "": - req.Id = id - method = t.client.ByID - case mac != "": - req.Mac = mac - method = t.client.ByMAC - case ip != "": - req.Ip = ip - method = t.client.ByIP - default: - return nil, errors.New("need to specify either id, ip, or mac") - } - - tinkHardware, err := method(ctx, req) - if err != nil { - if err.Error() == sqlErrorString || err.Error() == sqlErrorStringAlt { - return nil, fmt.Errorf("hardware %w", ErrNotFound) - } - - return nil, fmt.Errorf("getting hardware from Tinkerbell: %w", err) - } - - return tinkHardware, nil -} - -// Delete a Tinkerbell Hardware. -func (t *Hardware) Delete(ctx context.Context, id string) error { - if _, err := t.client.Delete(ctx, &hardware.DeleteRequest{Id: id}); err != nil { - if err.Error() == sqlErrorString || - err.Error() == sqlErrorStringAlt || - strings.Contains(err.Error(), sqlErrorNotFound) { - return fmt.Errorf("hardware %w", ErrNotFound) - } - - return fmt.Errorf("deleting hardware from Tinkerbell: %w", err) - } - - return nil -} diff --git a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/interface.go b/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/interface.go deleted file mode 100644 index 00b5c6410..000000000 --- a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/interface.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2021 The Machine Controller Authors. - -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 - - http://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" - - "github.com/tinkerbell/tink/protos/hardware" - "github.com/tinkerbell/tink/protos/template" -) - -type HardwareClient interface { - Get(context.Context, string, string, string) (*hardware.Hardware, error) - Delete(context.Context, string) error - Create(context.Context, *hardware.Hardware) error -} - -type TemplateClient interface { - Get(context.Context, string, string) (*template.WorkflowTemplate, error) - Create(context.Context, *template.WorkflowTemplate) error -} - -type WorkflowClient interface { - Create(context.Context, string, string) (string, error) -} diff --git a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/template.go b/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/template.go deleted file mode 100644 index 79e9f00be..000000000 --- a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/template.go +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright 2021 The Machine Controller Authors. - -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 - - http://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" - "fmt" - - "github.com/tinkerbell/tink/protos/template" -) - -// Template client for Tinkerbell. -type Template struct { - client template.TemplateServiceClient -} - -// NewTemplateClient returns a Template client. -func NewTemplateClient(client template.TemplateServiceClient) *Template { - return &Template{client: client} -} - -// Get returns a Tinkerbell Template. -func (t *Template) Get(ctx context.Context, id, name string) (*template.WorkflowTemplate, error) { - req := &template.GetRequest{} - if id != "" { - req.GetBy = &template.GetRequest_Id{Id: id} - } else { - req.GetBy = &template.GetRequest_Name{Name: name} - } - - tinkTemplate, err := t.client.GetTemplate(ctx, req) - if err != nil { - if err.Error() == sqlErrorString || err.Error() == sqlErrorStringAlt { - return nil, fmt.Errorf("template %w", ErrNotFound) - } - - return nil, fmt.Errorf("getting template from Tinkerbell: %w", err) - } - - return tinkTemplate, nil -} - -// Update a Tinkerbell Template. -func (t *Template) Update(ctx context.Context, template *template.WorkflowTemplate) error { - if _, err := t.client.UpdateTemplate(ctx, template); err != nil { - return fmt.Errorf("updating template in Tinkerbefll: %w", err) - } - - return nil -} - -// Create a Tinkerbell Template. -func (t *Template) Create(ctx context.Context, template *template.WorkflowTemplate) error { - resp, err := t.client.CreateTemplate(ctx, template) - if err != nil { - return fmt.Errorf("creating template in Tinkerbell: %w", err) - } - - template.Id = resp.GetId() - - return nil -} - -// Delete a Tinkerbell Template. -func (t *Template) Delete(ctx context.Context, id string) error { - req := &template.GetRequest{ - GetBy: &template.GetRequest_Id{Id: id}, - } - if _, err := t.client.DeleteTemplate(ctx, req); err != nil { - if err.Error() == sqlErrorString || err.Error() == sqlErrorStringAlt { - return fmt.Errorf("template %w", ErrNotFound) - } - - return fmt.Errorf("deleting template from Tinkerbell: %w", err) - } - - return nil -} diff --git a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/workflow.go b/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/workflow.go deleted file mode 100644 index 0c6e682eb..000000000 --- a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client/workflow.go +++ /dev/null @@ -1,220 +0,0 @@ -/* -Copyright 2021 The Machine Controller Authors. - -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 - - http://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" - "encoding/json" - "errors" - "fmt" - "io" - - "github.com/tinkerbell/tink/protos/hardware" - "github.com/tinkerbell/tink/protos/workflow" -) - -// Workflow client for Tinkerbell. -type Workflow struct { - client workflow.WorkflowServiceClient - hardwareClient *Hardware -} - -// NewWorkflowClient returns a Workflow client. -func NewWorkflowClient(client workflow.WorkflowServiceClient, hClient *Hardware) *Workflow { - return &Workflow{client: client, hardwareClient: hClient} -} - -// Get returns a Tinkerbell Workflow. -func (t *Workflow) Get(ctx context.Context, id string) (*workflow.Workflow, error) { - tinkWorkflow, err := t.client.GetWorkflow(ctx, &workflow.GetRequest{Id: id}) - if err != nil { - if err.Error() == sqlErrorString || err.Error() == sqlErrorStringAlt { - return nil, fmt.Errorf("workflow %w", ErrNotFound) - } - - return nil, fmt.Errorf("getting workflow from Tinkerbell: %w", err) - } - - return tinkWorkflow, nil -} - -// GetMetadata returns the metadata for a given Tinkerbell Workflow. -func (t *Workflow) GetMetadata(ctx context.Context, id string) ([]byte, error) { - verReq := &workflow.GetWorkflowDataRequest{WorkflowId: id} - - verResp, err := t.client.GetWorkflowDataVersion(ctx, verReq) - if err != nil { - if err.Error() == sqlErrorString || err.Error() == sqlErrorStringAlt { - return nil, fmt.Errorf("workflow %w", ErrNotFound) - } - - return nil, fmt.Errorf("getting workflow version from Tinkerbell: %w", err) - } - - req := &workflow.GetWorkflowDataRequest{WorkflowId: id, Version: verResp.GetVersion()} - - resp, err := t.client.GetWorkflowMetadata(ctx, req) - if err != nil { - if err.Error() == sqlErrorString || err.Error() == sqlErrorStringAlt { - return nil, fmt.Errorf("workflow %w", ErrNotFound) - } - - return nil, fmt.Errorf("getting workflow metadata from Tinkerbell: %w", err) - } - - return resp.GetData(), nil -} - -// GetActions returns the actions for a given Tinkerbell Workflow. -func (t *Workflow) GetActions(ctx context.Context, id string) ([]*workflow.WorkflowAction, error) { - req := &workflow.WorkflowActionsRequest{WorkflowId: id} - - resp, err := t.client.GetWorkflowActions(ctx, req) - if err != nil { - if err.Error() == sqlErrorString || err.Error() == sqlErrorStringAlt { - return nil, fmt.Errorf("workflow %w", ErrNotFound) - } - - return nil, fmt.Errorf("getting workflow actions from Tinkerbell: %w", err) - } - - return resp.GetActionList(), nil -} - -// GetEvents returns the events for a given Tinkerbell Workflow. -func (t *Workflow) GetEvents(ctx context.Context, id string) ([]*workflow.WorkflowActionStatus, error) { - req := &workflow.GetRequest{Id: id} - - resp, err := t.client.ShowWorkflowEvents(ctx, req) - if err != nil { - if err.Error() == sqlErrorString || err.Error() == sqlErrorStringAlt { - return nil, fmt.Errorf("workflow %w", ErrNotFound) - } - - return nil, fmt.Errorf("getting workflow events from Tinkerbell: %w", err) - } - - result := []*workflow.WorkflowActionStatus{} - - for { - e, err := resp.Recv() - if errors.Is(err, io.EOF) { - break - } - - if err != nil { - return nil, fmt.Errorf("getting workflow event from Tinkerbell: %w", err) - } - - result = append(result, e) - } - - return result, nil -} - -// GetState returns the state for a given Tinkerbell Workflow. -func (t *Workflow) GetState(ctx context.Context, id string) (workflow.State, error) { - req := &workflow.GetRequest{Id: id} - - resp, err := t.client.GetWorkflowContext(ctx, req) - if err != nil { - if err.Error() == sqlErrorString || err.Error() == sqlErrorStringAlt { - return 0, fmt.Errorf("workflow %w", ErrNotFound) - } - - return 0, fmt.Errorf("getting workflow state from Tinkerbell: %w", err) - } - - currIndex := resp.GetCurrentActionIndex() - total := resp.GetTotalNumberOfActions() - currState := resp.GetCurrentActionState() - - switch { - case total == 0: - // If there are no actions, let's just call it pending - return workflow.State_STATE_PENDING, nil - case currIndex+1 == total: - // If we are on the last action, just report it's state - return currState, nil - case currState != workflow.State_STATE_SUCCESS: - // If the state of the last action is anything other than - // success, just report it's state. - return currState, nil - default: - // We are not on the last action, and the last action - // was successful, we should report pending - return workflow.State_STATE_PENDING, nil - } -} - -// Create a Tinkerbell Workflow. -func (t *Workflow) Create(ctx context.Context, templateID, hardwareID string) (string, error) { - h, err := t.hardwareClient.Get(ctx, hardwareID, "", "") - if err != nil { - return "", err - } - - hardwareString, err := HardwareToJSON(h) - if err != nil { - return "", err - } - - req := &workflow.CreateRequest{ - Template: templateID, - Hardware: hardwareString, - } - - resp, err := t.client.CreateWorkflow(ctx, req) - if err != nil { - return "", fmt.Errorf("creating workflow in Tinkerbell: %w", err) - } - - return resp.GetId(), nil -} - -// Delete a Tinkerbell Workflow. -func (t *Workflow) Delete(ctx context.Context, id string) error { - if _, err := t.client.DeleteWorkflow(ctx, &workflow.GetRequest{Id: id}); err != nil { - if err.Error() == sqlErrorString || err.Error() == sqlErrorStringAlt { - return fmt.Errorf("workflow %w", ErrNotFound) - } - - return fmt.Errorf("deleting workflow from Tinkerbell: %w", err) - } - - return nil -} - -// HardwareToJSON converts Hardware to a string suitable for use in a -// Workflow Request for the raw Tinkerbell client. -func HardwareToJSON(h *hardware.Hardware) (string, error) { - hardwareInterfaces := h.GetNetwork().GetInterfaces() - hardwareInfo := make(map[string]string, len(hardwareInterfaces)) - - for i, hi := range hardwareInterfaces { - if mac := hi.GetDhcp().GetMac(); mac != "" { - hardwareInfo[fmt.Sprintf("device_%d", i+1)] = mac - } - } - - hardwareJSON, err := json.Marshal(hardwareInfo) - if err != nil { - return "", fmt.Errorf("marshaling hardware info into json: %w", err) - } - - return string(hardwareJSON), nil -} diff --git a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/driver.go b/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/driver.go deleted file mode 100644 index 8c08ff8c4..000000000 --- a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/driver.go +++ /dev/null @@ -1,222 +0,0 @@ -/* -Copyright 2021 The Machine Controller Authors. - -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 - - http://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 tinkerbell - -import ( - "context" - "encoding/json" - "errors" - "fmt" - - tinkclient "github.com/tinkerbell/tink/client" - tinkpkg "github.com/tinkerbell/tink/pkg" - "github.com/tinkerbell/tink/protos/hardware" - tinktmpl "github.com/tinkerbell/tink/protos/template" - "gopkg.in/yaml.v3" - - cloudprovidererrors "github.com/kubermatic/machine-controller/pkg/cloudprovider/errors" - "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/baremetal/plugins" - tinkerbellclient "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client" - metadataclient "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/metadata" - "github.com/kubermatic/machine-controller/pkg/cloudprovider/util" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" -) - -type ClientFactory func() (metadataclient.Client, tinkerbellclient.HardwareClient, tinkerbellclient.TemplateClient, tinkerbellclient.WorkflowClient) - -type driver struct { - TinkServerAddress string - ImageRepoAddress string - - metadataClient metadataclient.Client - hardwareClient tinkerbellclient.HardwareClient - templateClient tinkerbellclient.TemplateClient - workflowClient tinkerbellclient.WorkflowClient -} - -// NewTinkerbellDriver returns a new TinkerBell driver with a configured tinkserver address and a client timeout. -func NewTinkerbellDriver(mdConfig *metadataclient.Config, factory ClientFactory, tinkServerAddress, imageRepoAddress string) (plugins.PluginDriver, error) { - if tinkServerAddress == "" || imageRepoAddress == "" { - return nil, errors.New("tink-server address, ImageRepoAddress cannot be empty") - } - - var ( - mdClient metadataclient.Client - hwClient tinkerbellclient.HardwareClient - tmplClient tinkerbellclient.TemplateClient - wflClient tinkerbellclient.WorkflowClient - err error - ) - - if factory == nil { - mdClient, err = metadataclient.NewMetadataClient(mdConfig) - if err != nil { - return nil, fmt.Errorf("failed to create metadata client: %w", err) - } - - if err := tinkclient.Setup(); err != nil { - return nil, fmt.Errorf("failed to setup tink-server client: %w", err) - } - - hwClient = tinkerbellclient.NewHardwareClient(tinkclient.HardwareClient) - tmplClient = tinkerbellclient.NewTemplateClient(tinkclient.TemplateClient) - wflClient = tinkerbellclient.NewWorkflowClient(tinkclient.WorkflowClient, tinkerbellclient.NewHardwareClient(tinkclient.HardwareClient)) - } else { - mdClient, hwClient, tmplClient, wflClient = factory() - } - - d := &driver{ - TinkServerAddress: tinkServerAddress, - ImageRepoAddress: imageRepoAddress, - metadataClient: mdClient, - hardwareClient: hwClient, - templateClient: tmplClient, - workflowClient: wflClient, - } - - return d, nil -} - -func (d *driver) GetServer(ctx context.Context, uid types.UID, hwSpec runtime.RawExtension) (plugins.Server, error) { - hw := HardwareSpec{} - if err := json.Unmarshal(hwSpec.Raw, &hw); err != nil { - return nil, fmt.Errorf("failed to unmarshal tinkerbell hardware spec: %w", err) - } - - fetchedHW, err := d.hardwareClient.Get(ctx, string(uid), hw.GetIPAddress(), - hw.GetMACAddress()) - if err != nil { - if resourceNotFoundErr(err) { - return nil, cloudprovidererrors.ErrInstanceNotFound - } - - return nil, fmt.Errorf("failed to get hardware: %w", err) - } - - return &HardwareSpec{ - Hardware: tinkpkg.HardwareWrapper{ - Hardware: fetchedHW, - }, - }, nil -} - -func (d *driver) ProvisionServer(ctx context.Context, uid types.UID, cfg *plugins.CloudConfigSettings, hwSpec runtime.RawExtension) (plugins.Server, error) { - hw := HardwareSpec{} - if err := json.Unmarshal(hwSpec.Raw, &hw); err != nil { - return nil, fmt.Errorf("failed to unmarshal tinkerbell hardware spec: %w", err) - } - hw.Hardware.Id = string(uid) - _, err := d.hardwareClient.Get(ctx, hw.Hardware.Id, "", "") - if err != nil { - if resourceNotFoundErr(err) { - cfg, err := d.metadataClient.GetMachineMetadata() - if err != nil { - return nil, fmt.Errorf("failed to get metadata configs: %w", err) - } - - hw.Hardware.Network.Interfaces[0].Dhcp.Mac = cfg.MACAddress - - ip, netmask, _, err := util.CIDRToIPAndNetMask(cfg.CIDR) - if err != nil { - return nil, fmt.Errorf("failed to parse CIDR: %w", err) - } - dhcpIP := &hardware.Hardware_DHCP_IP{ - Address: ip, - Netmask: netmask, - Gateway: cfg.Gateway, - } - hw.Hardware.Network.Interfaces[0].Dhcp.Ip = dhcpIP - - if err := d.hardwareClient.Create(ctx, hw.Hardware.Hardware); err != nil { - return nil, fmt.Errorf("failed to register hardware to tink-server: %w", err) - } - } - } - - // cfg.SecretName has the same name as the machine name - workflowTemplate, err := d.templateClient.Get(ctx, "", cfg.SecretName) - if err != nil { - if resourceNotFoundErr(err) { - tmpl := createTemplate(d.TinkServerAddress, d.ImageRepoAddress, cfg) - payload, err := yaml.Marshal(tmpl) - if err != nil { - return nil, fmt.Errorf("failed marshalling workflow template: %w", err) - } - - workflowTemplate = &tinktmpl.WorkflowTemplate{ - Name: tmpl.Name, - Id: tmpl.ID, - Data: string(payload), - } - - if err := d.templateClient.Create(ctx, workflowTemplate); err != nil { - return nil, fmt.Errorf("failed to create workflow template: %w", err) - } - } - } - - if _, err := d.workflowClient.Create(ctx, workflowTemplate.Id, hw.GetID()); err != nil { - return nil, fmt.Errorf("failed to provision server id %s running template id %s: %w", workflowTemplate.Id, hw.GetID(), err) - } - - return &hw, nil -} - -func (d *driver) Validate(hwSpec runtime.RawExtension) error { - hw := HardwareSpec{} - if err := json.Unmarshal(hwSpec.Raw, &hw); err != nil { - return fmt.Errorf("failed to unmarshal tinkerbell hardware spec: %w", err) - } - - if hw.Hardware.Hardware == nil { - return fmt.Errorf("tinkerbell hardware data can not be empty") - } - - if hw.Hardware.Network == nil { - return fmt.Errorf("tinkerbell hardware network configs can not be empty") - } - - if hw.Hardware.Metadata == "" { - return fmt.Errorf("tinkerbell hardware metadata can not be empty") - } - - return nil -} - -func (d *driver) DeprovisionServer(ctx context.Context, uid types.UID) error { - if err := d.hardwareClient.Delete(ctx, string(uid)); err != nil { - if resourceNotFoundErr(err) { - return nil - } - return fmt.Errorf("failed to delete tinkerbell hardware data: %w", err) - } - - return nil -} - -func resourceNotFoundErr(err error) bool { - switch err.Error() { - case fmt.Sprintf("hardware %s", tinkerbellclient.ErrNotFound.Error()): - return true - case fmt.Sprintf("template %s", tinkerbellclient.ErrNotFound.Error()): - return true - } - - return false -} diff --git a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/driver_test.go b/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/driver_test.go deleted file mode 100644 index 229dd6817..000000000 --- a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/driver_test.go +++ /dev/null @@ -1,346 +0,0 @@ -/* -Copyright 2021 The Machine Controller Authors. - -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 - - http://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 tinkerbell - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "reflect" - "testing" - - "github.com/tinkerbell/tink/protos/hardware" - "github.com/tinkerbell/tink/protos/template" - "github.com/tinkerbell/tink/workflow" - - cloudprovidererrors "github.com/kubermatic/machine-controller/pkg/cloudprovider/errors" - "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/baremetal/plugins" - tinkerbellclient "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/client" - "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/metadata" - - "k8s.io/apimachinery/pkg/runtime" -) - -func TestNewTinkerbellDriver(t *testing.T) { - var testCases = []struct { - name string - tinkServer string - imageRepoServer string - clientFactor ClientFactory - errorIsExpected bool - }{ - { - name: "create new tinkerbell driver failure, missing image repo server", - tinkServer: "10.129.8.102", - imageRepoServer: "", - errorIsExpected: true, - }, - { - name: "create new tinkerbell driver failure, missing tink server", - tinkServer: "", - imageRepoServer: "10.129.8.102:8080", - errorIsExpected: true, - }, - { - name: "create new tinkerbell driver success", - tinkServer: "10.129.8.102", - imageRepoServer: "10.129.8.102:8080", - clientFactor: func() (metadata.Client, tinkerbellclient.HardwareClient, tinkerbellclient.TemplateClient, tinkerbellclient.WorkflowClient) { - return &fakeMetadataClient{}, &fakeHardwareClient{}, &fakeTemplateClient{}, &fakeWorkflowClient{} - }, - errorIsExpected: false, - }, - } - - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - _, err := NewTinkerbellDriver(nil, test.clientFactor, test.tinkServer, test.imageRepoServer) - if err != nil { - if test.errorIsExpected { - return - } - - t.Fatalf("failed to create tinkerbell client: %v", err) - } - }) - } -} - -func TestDriver_GetServer(t *testing.T) { - var testCases = []struct { - name string - tinkServer string - imageRepoServer string - hardwareSpec runtime.RawExtension - clientFactor ClientFactory - expectedHardwareSpec string - errorIsExpected bool - expectedError error - }{ - { - name: "failed to get server", - tinkServer: "10.129.8.102", - imageRepoServer: "10.129.8.102:8080", - hardwareSpec: runtime.RawExtension{Raw: []byte("{\n \"hardware\": {\n \"network\": {\n \"interfaces\": [\n {\n \"dhcp\": {\n \"ip\": {\n \"address\": \"10.129.8.90\"\n },\n \"mac\": \"18:C0:4D:B1:18:E3\"\n }\n }\n ]\n }\n }\n}")}, - clientFactor: func() (metadata.Client, tinkerbellclient.HardwareClient, tinkerbellclient.TemplateClient, tinkerbellclient.WorkflowClient) { - return &fakeMetadataClient{}, &fakeHardwareClient{ - err: &resourceError{ - resource: "hardware", - }, - }, &fakeTemplateClient{}, &fakeWorkflowClient{} - }, - errorIsExpected: true, - expectedError: cloudprovidererrors.ErrInstanceNotFound, - }, - { - name: "get server success", - tinkServer: "10.129.8.102", - imageRepoServer: "10.129.8.102:8080", - hardwareSpec: runtime.RawExtension{Raw: []byte("{\n \"hardware\": {\n \"network\": {\n \"interfaces\": [\n {\n \"dhcp\": {\n \"ip\": {\n \"address\": \"10.129.8.90\"\n },\n \"mac\": \"18:C0:4D:B1:18:E3\"\n }\n }\n ]\n }\n }\n}")}, - clientFactor: func() (metadata.Client, tinkerbellclient.HardwareClient, tinkerbellclient.TemplateClient, tinkerbellclient.WorkflowClient) { - return &fakeMetadataClient{}, &fakeHardwareClient{}, &fakeTemplateClient{}, &fakeWorkflowClient{} - }, - errorIsExpected: false, - expectedHardwareSpec: "{\n \"hardware\": {\n \"metadata\": {\n \"facility\": {\n \"facility_code\": \"ewr1\",\n \"plan_slug\": \"c2.medium.x86\",\n \"plan_version_slug\": \"\"\n },\n \"instance\": {\n \"operating_system_version\": {\n \"distro\": \"ubuntu\",\n \"os_slug\": \"ubuntu_18_04\",\n \"version\": \"18.04\"\n }\n },\n \"state\": \"\"\n },\n \"network\": {\n \"interfaces\": [\n {\n \"dhcp\": {\n \"arch\": \"x86_64\",\n \"ip\": {\n \"address\": \"10.129.8.90\",\n \"gateway\": \"10.129.8.89\",\n \"netmask\": \"255.255.255.252\"\n },\n \"mac\": \"18:C0:4D:B1:18:E3\",\n \"uefi\": false\n },\n \"netboot\": {\n \"allow_pxe\": true,\n \"allow_workflow\": true\n }\n }\n ]\n }\n }\n}", - }, - } - - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - d, err := NewTinkerbellDriver(nil, test.clientFactor, test.tinkServer, test.imageRepoServer) - if err != nil { - t.Fatalf("failed to create tinkerbell driver: %v", err) - } - - ctx := context.Background() - s, err := d.GetServer(ctx, "0eba0bf8-3772-4b4a-ab9f-6ebe93b90a94", test.hardwareSpec) - if err != nil { - if test.errorIsExpected && errors.Is(err, test.expectedError) { - return - } - - t.Fatalf("failed to execute get server: %v", err) - } - - hw := &HardwareSpec{} - if err := json.Unmarshal([]byte(test.expectedHardwareSpec), hw); err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(hw, s) { - t.Fatal("server spec and hardware spec mismatched") - } - }) - } -} - -func TestDriver_ProvisionServer(t *testing.T) { - var testCases = []struct { - name string - tinkServer string - imageRepoServer string - hardwareSpec runtime.RawExtension - clientFactory ClientFactory - cloudConfig *plugins.CloudConfigSettings - expectedHardwareSpec string - errorIsExpected bool - expectedError error - }{ - { - name: "provision server success", - tinkServer: "10.129.8.102", - imageRepoServer: "10.129.8.102:8080", - hardwareSpec: runtime.RawExtension{Raw: []byte("{\n \"hardware\": {\n \"metadata\": {\n \"facility\": {\n \"facility_code\": \"ewr1\",\n \"plan_slug\": \"c2.medium.x86\",\n \"plan_version_slug\": \"\"\n },\n \"instance\": {\n \"operating_system_version\": {\n \"distro\": \"ubuntu\",\n \"os_slug\": \"ubuntu_18_04\",\n \"version\": \"18.04\"\n }\n },\n \"state\": \"\"\n },\n \"network\": {\n \"interfaces\": [\n {\n \"dhcp\": {\n \"arch\": \"x86_64\",\n \"ip\": {\n \"address\": \"10.129.8.90\",\n \"gateway\": \"10.129.8.89\",\n \"netmask\": \"255.255.255.252\"\n },\n \"mac\": \"18:C0:4D:B1:18:E3\",\n \"uefi\": false\n },\n \"netboot\": {\n \"allow_pxe\": true,\n \"allow_workflow\": true\n }\n }\n ]\n }\n }\n}")}, - clientFactory: func() (metadata.Client, tinkerbellclient.HardwareClient, tinkerbellclient.TemplateClient, tinkerbellclient.WorkflowClient) { - return &fakeMetadataClient{}, &fakeHardwareClient{ - err: &resourceError{ - resource: "hardware", - }, - }, &fakeTemplateClient{}, &fakeWorkflowClient{} - }, - cloudConfig: &plugins.CloudConfigSettings{ - Token: "test-token", - Namespace: "kube-system", - SecretName: "test-secret", - ClusterHost: "10.10.10.10", - }, - expectedHardwareSpec: "{\n \"hardware\": {\n \"id\": \"0eba0bf8-3772-4b4a-ab9f-6ebe93b90a94\",\n \"metadata\": {\n \"facility\": {\n \"facility_code\": \"ewr1\",\n \"plan_slug\": \"c2.medium.x86\",\n \"plan_version_slug\": \"\"\n },\n \"instance\": {\n \"operating_system_version\": {\n \"distro\": \"ubuntu\",\n \"os_slug\": \"ubuntu_18_04\",\n \"version\": \"18.04\"\n }\n },\n \"state\": \"\"\n },\n \"network\": {\n \"interfaces\": [\n {\n \"dhcp\": {\n \"arch\": \"x86_64\",\n \"ip\": {\n \"address\": \"10.129.8.90\",\n \"gateway\": \"10.129.8.89\",\n \"netmask\": \"255.255.255.252\"\n },\n \"mac\": \"18:C0:4D:B1:18:E3\",\n \"uefi\": false\n },\n \"netboot\": {\n \"allow_pxe\": true,\n \"allow_workflow\": true\n }\n }\n ]\n }\n }\n}", - errorIsExpected: false, - }, - } - - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - d, err := NewTinkerbellDriver(nil, test.clientFactory, test.tinkServer, test.imageRepoServer) - if err != nil { - t.Fatalf("failed to create tinkerbell driver: %v", err) - } - - ctx := context.Background() - s, err := d.ProvisionServer(ctx, "0eba0bf8-3772-4b4a-ab9f-6ebe93b90a94", test.cloudConfig, test.hardwareSpec) - if err != nil { - t.Fatalf("failed to execute provision server: %v", err) - } - - hw := &HardwareSpec{} - if err := json.Unmarshal([]byte(test.expectedHardwareSpec), hw); err != nil { - t.Fatal(err) - } - - if !reflect.DeepEqual(hw, s) { - t.Fatal("server spec and hardware spec mismatched") - } - }) - } -} - -type fakeMetadataClient struct{} - -func (f *fakeMetadataClient) GetMachineMetadata() (*metadata.MachineMetadata, error) { - return &metadata.MachineMetadata{ - CIDR: "10.129.8.90/30", - MACAddress: "18:C0:4D:B1:18:E3", - Gateway: "10.129.8.89", - }, nil -} - -type fakeHardwareClient struct { - err *resourceError -} - -func (f *fakeHardwareClient) Get(_ context.Context, _ string, _ string, _ string) (*hardware.Hardware, error) { - if f.err != nil { - return nil, f.err - } - - return &hardware.Hardware{ - Metadata: "{\"facility\":{\"facility_code\":\"ewr1\",\"plan_slug\":\"c2.medium.x86\",\"plan_version_slug\":\"\"},\"instance\":{\"operating_system_version\":{\"distro\":\"ubuntu\",\"os_slug\":\"ubuntu_18_04\",\"version\":\"18.04\"}},\"state\":\"\"}", - Network: &hardware.Hardware_Network{ - Interfaces: []*hardware.Hardware_Network_Interface{ - { - Dhcp: &hardware.Hardware_DHCP{ - Arch: "x86_64", - Uefi: false, - Mac: "18:C0:4D:B1:18:E3", - Ip: &hardware.Hardware_DHCP_IP{ - Address: "10.129.8.90", - Netmask: "255.255.255.252", - Gateway: "10.129.8.89", - }, - }, - Netboot: &hardware.Hardware_Netboot{ - AllowPxe: true, - AllowWorkflow: true, - }, - }, - }, - }, - }, nil -} - -func (f *fakeHardwareClient) Delete(_ context.Context, _ string) error { - return nil -} - -func (f *fakeHardwareClient) Create(_ context.Context, hw *hardware.Hardware) error { - expectedHW := &hardware.Hardware{ - Id: "0eba0bf8-3772-4b4a-ab9f-6ebe93b90a94", - Metadata: "{\"facility\":{\"facility_code\":\"ewr1\",\"plan_slug\":\"c2.medium.x86\",\"plan_version_slug\":\"\"},\"instance\":{\"operating_system_version\":{\"distro\":\"ubuntu\",\"os_slug\":\"ubuntu_18_04\",\"version\":\"18.04\"}},\"state\":\"\"}", - Network: &hardware.Hardware_Network{ - Interfaces: []*hardware.Hardware_Network_Interface{ - { - Dhcp: &hardware.Hardware_DHCP{ - Arch: "x86_64", - Uefi: false, - Mac: "18:C0:4D:B1:18:E3", - Ip: &hardware.Hardware_DHCP_IP{ - Address: "10.129.8.90", - Netmask: "255.255.255.252", - Gateway: "10.129.8.89", - }, - }, - Netboot: &hardware.Hardware_Netboot{ - AllowPxe: true, - AllowWorkflow: true, - }, - }, - }, - }, - } - - if !reflect.DeepEqual(hw, expectedHW) { - return errors.New("unexpected hardware data") - } - - return nil -} - -type fakeTemplateClient struct{} - -func (f *fakeTemplateClient) Get(_ context.Context, _ string, _ string) (*template.WorkflowTemplate, error) { - wfl := &workflow.Workflow{ - Version: "0.1", - Name: "fake_template", - GlobalTimeout: 6000, - Tasks: []workflow.Task{ - { - Name: "disk-wipe", - WorkerAddr: "{{.device_1}}", - Volumes: []string{ - "/dev:/dev", - "/dev/console:/dev/console", - "/lib/firmware:/lib/firmware:ro", - }, - Actions: []workflow.Action{ - { - Name: "disk-wipe", - Image: "disk-wipe:v1", - Timeout: 90, - }, - }, - }, - }, - } - - payload, err := json.Marshal(wfl) - if err != nil { - return nil, err - } - - return &template.WorkflowTemplate{ - Data: string(payload), - }, nil -} - -func (f *fakeTemplateClient) Create(_ context.Context, _ *template.WorkflowTemplate) error { - return nil -} - -type fakeWorkflowClient struct{} - -func (f *fakeWorkflowClient) Create(_ context.Context, _ string, _ string) (string, error) { - return "", nil -} - -type resourceError struct { - resource string -} - -func (re *resourceError) Error() string { - return fmt.Sprintf("%s %s", re.resource, tinkerbellclient.ErrNotFound.Error()) -} diff --git a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/hardware.go b/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/hardware.go deleted file mode 100644 index 22ecd02a7..000000000 --- a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/hardware.go +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright 2021 The Machine Controller Authors. - -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 - - http://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 tinkerbell - -import ( - "encoding/json" - - "github.com/tinkerbell/tink/pkg" -) - -type HardwareSpec struct { - Hardware pkg.HardwareWrapper `json:"hardware"` -} - -func (h *HardwareSpec) GetName() string { - return "" -} - -func (h *HardwareSpec) GetID() string { - return h.Hardware.Id -} - -func (h *HardwareSpec) GetIPAddress() string { - interfaces := h.Hardware.Network.Interfaces - if len(interfaces) > 0 && interfaces[0].Dhcp.Ip != nil { - return h.Hardware.Network.Interfaces[0].Dhcp.Ip.Address - } - - return "" -} - -func (h *HardwareSpec) GetMACAddress() string { - if len(h.Hardware.Network.Interfaces) > 0 { - return h.Hardware.Network.Interfaces[0].Dhcp.Mac - } - - return "" -} - -func (h *HardwareSpec) GetStatus() string { - metadata := struct { - State string `json:"state"` - }{} - - if err := json.Unmarshal([]byte(h.Hardware.Metadata), &metadata); err != nil { - return "" - } - - return metadata.State -} diff --git a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/metadata/client.go b/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/metadata/client.go deleted file mode 100644 index 29ce3d9c8..000000000 --- a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/metadata/client.go +++ /dev/null @@ -1,123 +0,0 @@ -/* -Copyright 2021 The Machine Controller Authors. - -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 - - http://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 metadata - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io" - "net/http" - "time" -) - -type MachineMetadata struct { - CIDR string `json:"cidr,omitempty"` - MACAddress string `json:"mac_address,omitempty"` - Gateway string `json:"gateway,omitempty"` - Status string `json:"status,omitempty"` -} - -type Config struct { - Endpoint string `json:"endpoint,omitempty"` - AuthConfig *AuthConfig `json:"authConfig,omitempty"` -} - -type AuthMethod string - -const ( - BasicAuth AuthMethod = "BasicAuth" - BearerToken AuthMethod = "BearerToken" - - defaultTimeout = 30 * time.Second -) - -type AuthConfig struct { - AuthMethod AuthMethod `json:"authMethod"` - Username string `json:"username"` - Password string `json:"password"` - Token string `json:"token"` -} - -type Client interface { - GetMachineMetadata() (*MachineMetadata, error) -} - -type defaultClient struct { - metadataEndpoint string - authConfig *AuthConfig - client *http.Client -} - -func NewMetadataClient(cfg *Config) (Client, error) { - if cfg.Endpoint == "" { - return nil, errors.New("machine metadata endpoint cannot be empty") - } - - client := http.DefaultClient - client.Timeout = defaultTimeout - - return &defaultClient{ - metadataEndpoint: cfg.Endpoint, - authConfig: cfg.AuthConfig, - client: client, - }, nil -} - -func (d *defaultClient) GetMachineMetadata() (*MachineMetadata, error) { - req, err := http.NewRequest(http.MethodGet, d.metadataEndpoint, nil) - // TODO: Fix this - req = req.WithContext(context.TODO()) - if err != nil { - return nil, fmt.Errorf("failed to create a get metadata request: %w", err) - } - - req.Header.Set("Content-Type", "application/json") - d.getAuthMethod(req) - - res, err := d.client.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to execute get metadata request: %w", err) - } - - defer res.Body.Close() - - if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf("failed to execute get metadata request with status code: %v", res.StatusCode) - } - data, err := io.ReadAll(res.Body) - if err != nil { - return nil, fmt.Errorf("failed to read response body: %w", err) - } - - mdConfig := &MachineMetadata{} - if err := json.Unmarshal(data, mdConfig); err != nil { - return nil, fmt.Errorf("failed to unmarshal metadata config: %w", err) - } - - return mdConfig, nil -} - -func (d *defaultClient) getAuthMethod(req *http.Request) { - switch d.authConfig.AuthMethod { - case BasicAuth: - req.SetBasicAuth(d.authConfig.Username, d.authConfig.Password) - case BearerToken: - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.authConfig.Token)) - } -} diff --git a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/template.go b/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/template.go deleted file mode 100644 index 5d0902d26..000000000 --- a/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/template.go +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright 2021 The Machine Controller Authors. - -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 - - http://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 tinkerbell - -import ( - "github.com/tinkerbell/tink/workflow" - - "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/baremetal/plugins" -) - -func createTemplate(tinkServerAddress, imageRepoAddress string, cfg *plugins.CloudConfigSettings) *workflow.Workflow { - return &workflow.Workflow{ - Version: "0.1", - Name: cfg.SecretName, - ID: "", - GlobalTimeout: 6000, - Tasks: []workflow.Task{ - { - Name: "os-installation", - WorkerAddr: "{{.device_1}}", - Volumes: []string{ - "/dev:/dev", - "/dev/console:/dev/console", - "/lib/firmware:/lib/firmware:ro", - }, - Actions: []workflow.Action{ - { - Name: "disk-wipe", - Image: "disk-wipe:v1", - Timeout: 90, - }, - { - Name: "disk-partition", - Image: "disk-partition:v1", - Timeout: 180, - Environment: map[string]string{ - "MIRROR_HOST": tinkServerAddress, - }, - Volumes: []string{ - "/statedir:/statedir", - }, - }, - { - Name: "install-root-fs", - Image: "install-root-fs:v1", - Timeout: 600, - Environment: map[string]string{ - "MIRROR_HOST": imageRepoAddress, - }, - Volumes: nil, - }, - { - Name: "install-grub", - Image: "install-grub:v1", - Timeout: 600, - Environment: map[string]string{ - "MIRROR_HOST": imageRepoAddress, - }, - Volumes: []string{ - "/statedir:/statedir", - }, - }, - { - Name: "cloud-init", - Image: "cloud-init:v1", - Timeout: 600, - Environment: map[string]string{ - "MIRROR_HOST": imageRepoAddress, - "CLOUD_INIT_TOKEN": cfg.Token, - "CLOUD_INIT_SETTINGS_NAMESPACE": cfg.Namespace, - "SECRET_NAME": cfg.SecretName, - "CLUSTER_HOST": cfg.ClusterHost, - }, - }, - }, - }, - }, - } -} diff --git a/pkg/cloudprovider/provider/baremetal/provider.go b/pkg/cloudprovider/provider/baremetal/provider.go deleted file mode 100644 index 9ce05a126..000000000 --- a/pkg/cloudprovider/provider/baremetal/provider.go +++ /dev/null @@ -1,300 +0,0 @@ -/* -Copyright 2021 The Machine Controller Authors. - -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 - - http://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 baremetal - -import ( - "context" - "encoding/json" - "errors" - "fmt" - - "go.uber.org/zap" - - "github.com/kubermatic/machine-controller/pkg/apis/cluster/common" - clusterv1alpha1 "github.com/kubermatic/machine-controller/pkg/apis/cluster/v1alpha1" - cloudprovidererrors "github.com/kubermatic/machine-controller/pkg/cloudprovider/errors" - "github.com/kubermatic/machine-controller/pkg/cloudprovider/instance" - "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/baremetal/plugins" - "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell" - "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/baremetal/plugins/tinkerbell/metadata" - baremetaltypes "github.com/kubermatic/machine-controller/pkg/cloudprovider/provider/baremetal/types" - cloudprovidertypes "github.com/kubermatic/machine-controller/pkg/cloudprovider/types" - "github.com/kubermatic/machine-controller/pkg/cloudprovider/util" - "github.com/kubermatic/machine-controller/pkg/providerconfig" - providerconfigtypes "github.com/kubermatic/machine-controller/pkg/providerconfig/types" - - corev1 "k8s.io/api/core/v1" - kerrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" -) - -type bareMetalServer struct { - server plugins.Server -} - -func (b bareMetalServer) HostID() string { - return "" -} - -func (b bareMetalServer) Name() string { - return b.server.GetName() -} - -func (b bareMetalServer) ID() string { - return b.server.GetID() -} - -// TODO: Tinkerbell doesn't have a CCM. -func (b bareMetalServer) ProviderID() string { - return "" -} - -func (b bareMetalServer) Addresses() map[string]corev1.NodeAddressType { - return map[string]corev1.NodeAddressType{ - b.server.GetIPAddress(): corev1.NodeInternalIP, - } -} - -func (b bareMetalServer) Status() instance.Status { - return instance.StatusRunning -} - -type provider struct { - configVarResolver *providerconfig.ConfigPointerVarResolver -} - -// New returns a new BareMetal provider. -func New(configVarResolver *providerconfig.ConfigVarResolver) cloudprovidertypes.Provider { - return &provider{ - configVarResolver: &providerconfig.ConfigPointerVarResolver{ - Cvr: configVarResolver, - }, - } -} - -type Config struct { - driver plugins.PluginDriver - driverName plugins.Driver - driverSpec runtime.RawExtension -} - -func (p *provider) getConfig(provSpec clusterv1alpha1.ProviderSpec) (*Config, *providerconfigtypes.Config, error) { - if provSpec.Value == nil { - return nil, nil, fmt.Errorf("machine.spec.providerconfig.value is nil") - } - - pconfig, err := providerconfigtypes.GetConfig(provSpec) - if err != nil { - return nil, nil, err - } - - if pconfig.OperatingSystemSpec.Raw == nil { - return nil, nil, errors.New("operatingSystemSpec in the MachineDeployment cannot be empty") - } - - rawConfig, err := baremetaltypes.GetConfig(*pconfig) - if err != nil { - return nil, nil, fmt.Errorf("failed to unmarshal: %w", err) - } - - c := Config{} - endpoint, err := p.configVarResolver.GetConfigVarStringValueOrEnv(rawConfig.MetadataClient.Endpoint, "METADATA_SERVER_ENDPOINT") - if err != nil { - return nil, nil, fmt.Errorf(`failed to get value of \"endpoint\" field: %w`, err) - } - authMethod, err := p.configVarResolver.GetConfigVarStringValueOrEnv(rawConfig.MetadataClient.AuthMethod, "METADATA_SERVER_AUTH_METHOD") - if err != nil { - return nil, nil, fmt.Errorf(`failed to get value of \"authMethod\" field: %w`, err) - } - username, err := p.configVarResolver.GetConfigVarStringValueOrEnv(rawConfig.MetadataClient.Username, "METADATA_SERVER_USERNAME") - if err != nil { - return nil, nil, fmt.Errorf(`failed to get value of \"username\" field: %w`, err) - } - password, err := p.configVarResolver.GetConfigVarStringValueOrEnv(rawConfig.MetadataClient.Password, "METADATA_SERVER_PASSWORD") - if err != nil { - return nil, nil, fmt.Errorf(`failed to get value of \"password\" field: %w`, err) - } - token, err := p.configVarResolver.GetConfigVarStringValueOrEnv(rawConfig.MetadataClient.Token, "METADATA_SERVER_TOKEN") - if err != nil { - return nil, nil, fmt.Errorf(`failed to get value of \"token\" field: %w`, err) - } - - mdCfg := &metadata.Config{ - Endpoint: endpoint, - AuthConfig: &metadata.AuthConfig{ - AuthMethod: metadata.AuthMethod(authMethod), - Username: username, - Password: password, - Token: token, - }, - } - - driverName, err := p.configVarResolver.GetConfigVarStringValue(rawConfig.Driver) - if err != nil { - return nil, nil, fmt.Errorf("failed to get baremetal provider's driver name: %w", err) - } - c.driverName = plugins.Driver(driverName) - - c.driverSpec = rawConfig.DriverSpec - - switch c.driverName { - case plugins.Tinkerbell: - driverConfig := struct { - ProvisionerIPAddress string `json:"provisionerIPAddress"` - MirrorHost string `json:"mirrorHost"` - }{} - - if err := json.Unmarshal(c.driverSpec.Raw, &driverConfig); err != nil { - return nil, nil, fmt.Errorf("failed to unmarshal tinkerbell driver spec: %w", err) - } - - c.driver, err = tinkerbell.NewTinkerbellDriver(mdCfg, nil, driverConfig.ProvisionerIPAddress, driverConfig.MirrorHost) - if err != nil { - return nil, nil, fmt.Errorf("failed to create a tinkerbell driver: %w", err) - } - default: - return nil, nil, fmt.Errorf("unsupported baremetal driver: %s", pconfig.CloudProvider) - } - - return &c, pconfig, err -} - -func (p provider) AddDefaults(_ *zap.SugaredLogger, spec clusterv1alpha1.MachineSpec) (clusterv1alpha1.MachineSpec, error) { - _, _, err := p.getConfig(spec.ProviderSpec) - return spec, err -} - -func (p provider) Validate(_ context.Context, _ *zap.SugaredLogger, spec clusterv1alpha1.MachineSpec) error { - c, _, err := p.getConfig(spec.ProviderSpec) - if err != nil { - return fmt.Errorf("failed to parse config: %w", err) - } - - if c.driverName == "" { - return fmt.Errorf("baremetal provider's driver name cannot be empty") - } - - if c.driverSpec.Raw == nil { - return fmt.Errorf("baremetal provider's driver spec cannot be empty") - } - - return nil -} - -func (p provider) Get(ctx context.Context, _ *zap.SugaredLogger, machine *clusterv1alpha1.Machine, _ *cloudprovidertypes.ProviderData) (instance.Instance, error) { - c, _, err := p.getConfig(machine.Spec.ProviderSpec) - if err != nil { - return nil, cloudprovidererrors.TerminalError{ - Reason: common.InvalidConfigurationMachineError, - Message: fmt.Sprintf("Failed to parse MachineSpec, due to %v", err), - } - } - - server, err := c.driver.GetServer(ctx, machine.UID, c.driverSpec) - if err != nil { - if errors.Is(err, cloudprovidererrors.ErrInstanceNotFound) { - return nil, cloudprovidererrors.ErrInstanceNotFound - } - - return nil, fmt.Errorf("failed to fetch server with the id %s: %w", machine.Name, err) - } - - return &bareMetalServer{ - server: server, - }, nil -} - -func (p provider) GetCloudConfig(_ clusterv1alpha1.MachineSpec) (config string, name string, err error) { - return "", "", nil -} - -func (p provider) Create(ctx context.Context, _ *zap.SugaredLogger, machine *clusterv1alpha1.Machine, data *cloudprovidertypes.ProviderData, userdata string) (instance.Instance, error) { - c, _, err := p.getConfig(machine.Spec.ProviderSpec) - if err != nil { - return nil, cloudprovidererrors.TerminalError{ - Reason: common.InvalidConfigurationMachineError, - Message: fmt.Sprintf("Failed to parse MachineSpec, due to %v", err), - } - } - - if err := util.CreateMachineCloudInitSecret(ctx, userdata, machine.Name, data.Client); err != nil { - return nil, fmt.Errorf("failed to create cloud-init secret for machine %s: %w", machine.Name, err) - } - - token, apiServer, err := util.ExtractTokenAndAPIServer(ctx, userdata, data.Client) - if err != nil { - return nil, fmt.Errorf("failed to extarct token and api server address: %w", err) - } - - cfg := &plugins.CloudConfigSettings{ - Token: token, - Namespace: util.CloudInitNamespace, - SecretName: machine.Name, - ClusterHost: apiServer, - } - - server, err := c.driver.ProvisionServer(ctx, machine.UID, cfg, c.driverSpec) - if err != nil { - return nil, fmt.Errorf("failed to provision server: %w", err) - } - - return &bareMetalServer{ - server: server, - }, nil -} - -func (p provider) Cleanup(ctx context.Context, _ *zap.SugaredLogger, machine *clusterv1alpha1.Machine, data *cloudprovidertypes.ProviderData) (bool, error) { - c, _, err := p.getConfig(machine.Spec.ProviderSpec) - if err != nil { - return false, cloudprovidererrors.TerminalError{ - Reason: common.InvalidConfigurationMachineError, - Message: fmt.Sprintf("Failed to parse MachineSpec, due to %v", err), - } - } - - if err := c.driver.DeprovisionServer(ctx, machine.UID); err != nil { - return false, fmt.Errorf("failed to de-provision server: %w", err) - } - - secret := &corev1.Secret{} - if err := data.Client.Get(ctx, types.NamespacedName{Namespace: util.CloudInitNamespace, Name: machine.Name}, secret); err != nil { - if !kerrors.IsNotFound(err) { - return false, fmt.Errorf("failed to fetching secret for userdata: %w", err) - } - - return true, nil - } - - if err := data.Client.Delete(ctx, secret); err != nil { - return false, fmt.Errorf("failed to cleanup secret for userdata: %w", err) - } - - return true, nil -} - -func (p provider) MachineMetricsLabels(_ *clusterv1alpha1.Machine) (map[string]string, error) { - return nil, nil -} - -func (p provider) MigrateUID(_ context.Context, _ *zap.SugaredLogger, _ *clusterv1alpha1.Machine, _ types.UID) error { - return nil -} - -func (p provider) SetMetricsForMachines(_ clusterv1alpha1.MachineList) error { - return nil -} diff --git a/pkg/cloudprovider/provider/baremetal/types/types.go b/pkg/cloudprovider/provider/baremetal/types/types.go deleted file mode 100644 index 07036c2a5..000000000 --- a/pkg/cloudprovider/provider/baremetal/types/types.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright 2021 The Machine Controller Authors. - -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 - - http://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 types - -import ( - "github.com/kubermatic/machine-controller/pkg/jsonutil" - providerconfigtypes "github.com/kubermatic/machine-controller/pkg/providerconfig/types" - - "k8s.io/apimachinery/pkg/runtime" -) - -type RawConfig struct { - MetadataClient *MetadataClientConfig `json:"metadataClientConfig"` - Driver *providerconfigtypes.ConfigVarString `json:"driver"` - DriverSpec runtime.RawExtension `json:"driverSpec"` -} - -type MetadataClientConfig struct { - Endpoint *providerconfigtypes.ConfigVarString `json:"endpoint,omitempty"` - AuthMethod *providerconfigtypes.ConfigVarString `json:"authMethod,omitempty"` - Username *providerconfigtypes.ConfigVarString `json:"username,omitempty"` - Password *providerconfigtypes.ConfigVarString `json:"password,omitempty"` - Token *providerconfigtypes.ConfigVarString `json:"token,omitempty"` -} - -func GetConfig(pconfig providerconfigtypes.Config) (*RawConfig, error) { - rawConfig := &RawConfig{} - - return rawConfig, jsonutil.StrictUnmarshal(pconfig.CloudProviderSpec.Raw, rawConfig) -} diff --git a/pkg/providerconfig/types/types.go b/pkg/providerconfig/types/types.go index 41a1bee4e..7847439e1 100644 --- a/pkg/providerconfig/types/types.go +++ b/pkg/providerconfig/types/types.go @@ -47,7 +47,6 @@ const ( CloudProviderAWS CloudProvider = "aws" CloudProviderOpenstack CloudProvider = "openstack" CloudProviderFake CloudProvider = "fake" - CloudProviderBaremetal CloudProvider = "baremetal" CloudProviderExternal CloudProvider = "external" ) @@ -65,7 +64,6 @@ var ( CloudProviderAWS, CloudProviderOpenstack, CloudProviderFake, - CloudProviderBaremetal, } ) diff --git a/test/e2e/provisioning/testdata/machinedeployment-baremetal-tinkerbell.yaml b/test/e2e/provisioning/testdata/machinedeployment-baremetal-tinkerbell.yaml deleted file mode 100644 index 0a3797b8c..000000000 --- a/test/e2e/provisioning/testdata/machinedeployment-baremetal-tinkerbell.yaml +++ /dev/null @@ -1,97 +0,0 @@ -apiVersion: "cluster.k8s.io/v1alpha1" -kind: MachineDeployment -metadata: - name: << MACHINE_NAME >> - namespace: kube-system - annotations: - k8c.io/operating-system-profile: osp-<< OS_NAME >> -spec: - replicas: 1 - strategy: - type: RollingUpdate - rollingUpdate: - maxSurge: 1 - maxUnavailable: 0 - selector: - matchLabels: - name: << MACHINE_NAME >> - template: - metadata: - labels: - name: << MACHINE_NAME >> - spec: - providerSpec: - value: - sshPublicKeys: - - "<< YOUR_PUBLIC_KEY >>" - cloudProvider: "baremetal" - cloudProviderSpec: - driver: "tinkerbell" - metadataClientConfig: - endpoint: << METADATA_SERVER_ENDPOINT >> - authMethod: << METADATA_SERVER_AUTH_METHOD >> - username: << METADATA_SERVER_USERNAME >> - password: << METADATA_SERVER_PASSWORD >> - token: << METADATA_SERVER_TOKEN >> - driverSpec: - provisionerIPAddress: << PROVISIONER_IP_ADDRESS >> - mirrorHost: << MIRROR_HOST >> - hardware: - id: << MACHINE_NAME >> - metadata: - facility: - facilitycode: << FACILITY_CODE >> - planslug: << PLAN_SLUG >> - state: "" - instance: - operatingsystemversion: - distro: << OS_NAME >> - imagetag: << IMAGE_TAG >> - osslug: << OS_NAME >> - slug: << OS_NAME >> - version: << OS_VERSION >> - storage: - disks: - - device: /dev/sda - wipetable: true - partitions: - - size: 4096 - label: BIOS - number: 1 - - size: 3993600 - label: SWAP - number: 2 - - size: 0 - label: ROOT - number: 3 - filesystems: - - mount: - point: / - create: - options: - - -L - - ROOT - device: /dev/sda3 - format: ext4 - - mount: - point: none - create: - options: - - -L - - SWAP - device: /dev/sda2 - format: swap - network: - interfaces: - - dhcp: - arch: x86_64 - uefi: false - netboot: - allowpxe: false - allowworkflow: false - operatingSystem: "<< OS_NAME >>" - operatingSystemSpec: - distUpgradeOnBoot: false - disableAutoUpdate: true - versions: - kubelet: "<< KUBERNETES_VERSION >>"