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 2, 2022
1 parent a9bfa4c commit 867dc93
Show file tree
Hide file tree
Showing 14 changed files with 778 additions and 22 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ jobs:
- name: Generate all files
run: nix-shell --run 'make gen'
- name: goimports
run: go get golang.org/x/tools/cmd/goimports && goimports -d . | (! grep .)
run: go install golang.org/x/tools/cmd/goimports@latest && goimports -d . | (! grep .)
- name: go vet
run: go mod tidy && go vet ./...
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
uses: golangci/golangci-lint-action@v3
with:
args: -v
- name: go test
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ deploy/stack/state/webroot/workflow/*
!deploy/stack/state/webroot/workflow/.keep
deploy/stack/state/webroot/*.gz
workflow_id.txt
hack/tools/
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ all: help
boots: cmd/boots/boots ## Compile boots for host OS and Architecture

crosscompile: $(crossbinaries) ## Compile boots for all architectures

gen: $(generated_go_files) ## Generate go generate'd files

IMAGE_TAG ?= boots:latest
Expand Down
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 867dc93

Please sign in to comment.