Skip to content

Commit

Permalink
Add kubernetes-based backend
Browse files Browse the repository at this point in the history
Signed-off-by: Micah Hausler <mhausler@amazon.com>
  • Loading branch information
micahhausler committed May 4, 2022
1 parent 56f67aa commit b55762c
Show file tree
Hide file tree
Showing 12 changed files with 769 additions and 19 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
/cmd/boots/boots
/cmd/boots/boots-*-*
coverage.txt
hack/tools/
173 changes: 173 additions & 0 deletions client/kubernetes/hardware_finder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package kubernetes

import (
"context"
"net"

"github.com/packethost/pkg/log"
"github.com/pkg/errors"
"github.com/tinkerbell/boots/client"
"github.com/tinkerbell/tink/pkg/apis/core/v1alpha1"
"github.com/tinkerbell/tink/pkg/controllers"
"github.com/tinkerbell/tink/pkg/convert"
"github.com/tinkerbell/tink/protos/workflow"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
crclient "sigs.k8s.io/controller-runtime/pkg/client"
)

// HardwareFinder is a type that looks up hardware from Kubernetes
type HardwareFinder struct {
clientFunc func() crclient.Client
logger log.Logger
namespace string
}

// NewHardwareFinder returns a HardwareFinder that discovers hardware from Kubernetes.
func NewHardwareFinder(logger log.Logger, k8sAPI, kubeconfig string) (*HardwareFinder, error) {
ccfg := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig},
&clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: k8sAPI}})

config, err := ccfg.ClientConfig()
if err != nil {
return nil, err
}

namespace, _, err := ccfg.Namespace()
if err != nil {
return nil, errors.WithStack(err)
}

manager, err := controllers.NewManager(config, controllers.GetServerOptions())
if err != nil {
return nil, errors.WithStack(err)
}

go manager.Start(context.Background())

return &HardwareFinder{
clientFunc: manager.GetClient,
logger: logger,
namespace: namespace,
}, nil
}

// ByIP returns a Discoverer for a particular IP.
func (f *HardwareFinder) ByIP(ctx context.Context, ip net.IP) (client.Discoverer, error) {
hardwareList := &v1alpha1.HardwareList{}

err := f.clientFunc().List(ctx, hardwareList, &crclient.MatchingFields{
controllers.HardwareIPAddrIndex: ip.String(),
})
if err != nil {
return nil, errors.Wrap(err, "failed listing hardware")
}

if len(hardwareList.Items) == 0 {
return nil, errors.New("no hardware found")
}

if len(hardwareList.Items) > 1 {
return nil, errors.Errorf("got %d hardware for ip %s, expected only 1", len(hardwareList.Items), ip)
}

return NewK8sDiscoverer(&hardwareList.Items[0]), nil
}

// ByMAC returns a Discoverer for a particular MAC address.
func (f *HardwareFinder) ByMAC(ctx context.Context, mac net.HardwareAddr, _ net.IP, _ string) (client.Discoverer, error) {
hardwareList := &v1alpha1.HardwareList{}

err := f.clientFunc().List(ctx, hardwareList, &crclient.MatchingFields{
controllers.HardwareMACAddrIndex: mac.String(),
})
if err != nil {
return nil, errors.Wrap(err, "failed listing hardware")
}

if len(hardwareList.Items) == 0 {
return nil, errors.New("no hardware found")
}

if len(hardwareList.Items) > 1 {
return nil, errors.Errorf("got %d hardware for mac %s, expected only 1", len(hardwareList.Items), mac)
}

return NewK8sDiscoverer(&hardwareList.Items[0]), nil
}

// WorkflowFinder is a type for finding if a hardware ID has active workflows.
type WorkflowFinder struct {
clientFunc func() crclient.Client
logger log.Logger
namespace string
}

// NewWorkflowFinder returns a *WorkflowFinder that satisfies client.WorkflowFinder.
func NewWorkflowFinder(logger log.Logger, k8sAPI, kubeconfig string) (*WorkflowFinder, error) {
ccfg := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig},
&clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: k8sAPI}})

config, err := ccfg.ClientConfig()
if err != nil {
return nil, err
}

namespace, _, err := ccfg.Namespace()
if err != nil {
return nil, errors.WithStack(err)
}

manager, err := controllers.NewManager(config, controllers.GetServerOptions())
if err != nil {
return nil, errors.WithStack(err)
}

go manager.Start(context.Background())

return &WorkflowFinder{
clientFunc: manager.GetClient,
logger: logger,
namespace: namespace,
}, nil
}

// HasActiveWorkflow finds if an active workflow exists for a particular hardware ID.
func (f *WorkflowFinder) HasActiveWorkflow(ctx context.Context, hwID client.HardwareID) (bool, error) {
if hwID == "" {
return false, errors.New("missing hardware id")
}

// labels := prometheus.Labels{"from": "dhcp"}
// cacherTimer := prometheus.NewTimer(metrics.CacherDuration.With(labels))
// metrics.CacherRequestsInProgress.With(labels).Inc()
// metrics.CacherTotal.With(labels).Inc()
stored := &v1alpha1.WorkflowList{}
err := f.clientFunc().List(ctx, stored, &crclient.MatchingFields{
controllers.WorkflowWorkerNonTerminalStateIndex: hwID.String(),
})
if err != nil {
return false, errors.Wrap(err, "failed to list workflows")
}

wfContexts := []*workflow.WorkflowContext{}
for _, wf := range stored.Items {
wfContexts = append(wfContexts, convert.WorkflowToWorkflowContext(&wf))
}

wcl := &workflow.WorkflowContextList{
WorkflowContexts: wfContexts,
}
// cacherTimer.ObserveDuration()
// metrics.CacherRequestsInProgress.With(labels).Dec()

for _, wf := range (*wcl).WorkflowContexts {
if wf.CurrentActionState == workflow.State_STATE_PENDING || wf.CurrentActionState == workflow.State_STATE_RUNNING {
return true, nil
}
}

return false, nil
}
Loading

0 comments on commit b55762c

Please sign in to comment.