Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/image-build-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Image
on:
push:
branches:
- "main"
tags:
- "*"

permissions:
contents: write
actions: read

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Install devbox
uses: jetify-com/devbox-install-action@v0.14.0

- name: Build and push image
run: devbox run make image
12 changes: 0 additions & 12 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,6 @@ jobs:
with:
fetch-depth: 0

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Install devbox
uses: jetify-com/devbox-install-action@v0.14.0

- name: Build and push image
run: devbox run make image

- name: Create Release Artifacts
run: devbox run make release
env:
Expand Down
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ helm-install: ## install karpenter onto an existing cluster (requires k8s contex
@helm upgrade --install --namespace karpenter --create-namespace karpenter charts/karpenter \
--set controller.image.repository=$(KO_DOCKER_REPO) \
--set region=${LINODE_REGION} \
--set settings.clusterID=${CLUSTER_ID} \
--set settings.clusterName=${CLUSTER_NAME} \
--set apiToken=${LINODE_TOKEN}

Expand Down
1 change: 0 additions & 1 deletion charts/karpenter/templates/karpl-credentials.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ stringData:
LINODE_TOKEN: {{ required "The API token needs to be set" .Values.apiToken }}
LINODE_URL: {{ .Values.apiURL }}
LINODE_API_VERSION: {{ .Values.apiVersion }}
CLUSTER_ID: {{ required "The LKE cluster ID needs to be set" .Values.settings.clusterID | quote }}
# TODO: region discovery
CLUSTER_REGION: {{ required "The cluster region currently needs to be set" .Values.region }}
2 changes: 0 additions & 2 deletions charts/karpenter/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,6 @@ settings:
clusterCABundle: ""
# -- Cluster name.
clusterName: ""
# -- LKE cluster ID (required when mode is 'lke').
clusterID: ""
# -- Cluster endpoint.
clusterEndpoint: ""
# -- Operating mode: 'lke' for LKE NodePool provisioning, 'instance' for direct Linode instances.
Expand Down
6 changes: 5 additions & 1 deletion cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ import (
func main() {
ctx1, op1 := coreoperator.NewOperator()
linodeClientConfig := validateEnvironment(ctx1)
ctx, op := operator.NewOperator(ctx1, op1, linodeClientConfig)
ctx, op, err := operator.NewOperator(ctx1, op1, linodeClientConfig)
if err != nil {
log.FromContext(ctx1).Error(err, "Failed to create Linode operator")
os.Exit(1)
}

linodeCloudProvider := cloudprovider.New(
op.InstanceTypesProvider,
Expand Down
2 changes: 1 addition & 1 deletion hack/boilerplate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set -eu -o pipefail

for i in $(
find ./tools ./cmd ./pkg ./test ./hack -name "*.go"
find ./cmd ./pkg ./hack -name "*.go"
); do
if ! grep -q "Apache License" $i; then
cat hack/boilerplate.go.txt $i >$i.new && mv $i.new $i
Expand Down
5 changes: 5 additions & 0 deletions pkg/fake/linodeapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -671,3 +671,8 @@ func (l *LinodeClient) GetLKENodePoolNode(_ context.Context, clusterID int, node
}
return *node, err
}

func (l *LinodeClient) ListLKEClusters(_ context.Context, _ *linodego.ListOptions) ([]linodego.LKECluster, error) {
// For simplicity, return a canned response
return []linodego.LKECluster{{ID: DefaultClusterID}}, nil
}
3 changes: 3 additions & 0 deletions pkg/linode/sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ type LinodeAPI interface {
CreateTag(ctx context.Context, opts linodego.TagCreateOptions) (*linodego.Tag, error)
CreateInstance(ctx context.Context, opts linodego.InstanceCreateOptions) (*linodego.Instance, error)

// LKE Cluster methods for LKE cluster management
ListLKEClusters(ctx context.Context, opts *linodego.ListOptions) ([]linodego.LKECluster, error)
// NodePool methods for LKE cluster management

CreateLKENodePool(ctx context.Context, clusterID int, opts linodego.LKENodePoolCreateOptions) (*linodego.LKENodePool, error)
ListLKENodePools(ctx context.Context, clusterID int, opts *linodego.ListOptions) ([]linodego.LKENodePool, error)
GetLKENodePool(ctx context.Context, clusterID, poolID int) (*linodego.LKENodePool, error)
Expand Down
24 changes: 21 additions & 3 deletions pkg/operator/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"fmt"
"net"

"github.com/linode/linodego"
"github.com/patrickmn/go-cache"
"github.com/samber/lo"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -32,6 +33,7 @@ import (
"github.com/linode/karpenter-provider-linode/pkg/providers/instance"
"github.com/linode/karpenter-provider-linode/pkg/providers/instancetype"
"github.com/linode/karpenter-provider-linode/pkg/providers/lke"
"github.com/linode/karpenter-provider-linode/pkg/utils"
)

func init() {
Expand All @@ -47,7 +49,7 @@ type Operator struct {
LinodeClient sdk.LinodeAPI
}

func NewOperator(ctx context.Context, operator *operator.Operator, linodeClientConfig sdk.ClientConfig) (context.Context, *Operator) {
func NewOperator(ctx context.Context, operator *operator.Operator, linodeClientConfig sdk.ClientConfig) (context.Context, *Operator, error) {
linodeClient := lo.Must(sdk.CreateLinodeClient(linodeClientConfig))
unavailableOfferingsCache := linodecache.NewUnavailableOfferings()
kubeDNSIP, err := KubeDNSIP(ctx, operator.KubernetesInterface)
Expand Down Expand Up @@ -79,8 +81,24 @@ func NewOperator(ctx context.Context, operator *operator.Operator, linodeClientC
switch opts.Mode {
case "lke":
log.FromContext(ctx).Info("initializing in LKE mode")
listFilter := utils.Filter{Label: opts.ClusterName}
filter, err := listFilter.String()
if err != nil {
return nil, nil, err
}
// Get cluster ID from the cluster name (label)
clusterList, err := linodeClient.ListLKEClusters(ctx, &linodego.ListOptions{
Filter: filter,
})
if err != nil {
return nil, nil, err
}
if len(clusterList) != 1 {
return nil, nil, fmt.Errorf("could not determine LKE cluster with name: %s", opts.ClusterName)
}

nodeProvider = lke.NewDefaultProvider(
opts.ClusterID,
clusterList[0].ID,
opts.ClusterRegion,
operator.EventRecorder,
linodeClient,
Expand All @@ -105,7 +123,7 @@ func NewOperator(ctx context.Context, operator *operator.Operator, linodeClientC
InstanceTypesProvider: instanceTypeProvider,
NodeProvider: nodeProvider,
LinodeClient: linodeClient,
}
}, nil
}

func KubeDNSIP(ctx context.Context, kubernetesInterface kubernetes.Interface) (net.IP, error) {
Expand Down
2 changes: 0 additions & 2 deletions pkg/operator/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ type optionsKey struct{}

type Options struct {
ClusterName string
ClusterID int
ClusterRegion string
ClusterEndpoint string
VMMemoryOverheadPercent float64
Expand All @@ -45,7 +44,6 @@ type Options struct {

func (o *Options) AddFlags(fs *coreoptions.FlagSet) {
fs.StringVar(&o.ClusterName, "cluster-name", env.WithDefaultString("CLUSTER_NAME", ""), "[REQUIRED] The kubernetes cluster name for resource discovery.")
fs.IntVar(&o.ClusterID, "cluster-id", env.WithDefaultInt("CLUSTER_ID", 0), "The LKE cluster ID for LKE node pool operations. Required when mode is 'lke'.")
fs.StringVar(&o.ClusterRegion, "cluster-region", env.WithDefaultString("CLUSTER_REGION", "us-east"), "The region the kubernetes cluster is deployed in.")
fs.StringVar(&o.ClusterEndpoint, "cluster-endpoint", env.WithDefaultString("CLUSTER_ENDPOINT", ""), "The external kubernetes cluster endpoint for new nodes to connect with. If not specified, will discover the cluster endpoint using DescribeCluster API.")
fs.Float64Var(&o.VMMemoryOverheadPercent, "vm-memory-overhead-percent", utils.WithDefaultFloat64("VM_MEMORY_OVERHEAD_PERCENT", 0.075), "The VM memory overhead as a percent that will be subtracted from the total memory for all instance types when cached information is unavailable.")
Expand Down
3 changes: 0 additions & 3 deletions pkg/operator/options/options_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,6 @@ func (o *Options) validateRequiredFields() error {
if o.ClusterName == "" {
return fmt.Errorf("missing field, cluster-name")
}
if o.Mode == "lke" && o.ClusterID <= 0 {
return fmt.Errorf("missing or invalid field, cluster-id must be a positive integer when mode is 'lke'")
}
return nil
}

Expand Down
8 changes: 2 additions & 6 deletions pkg/operator/options/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,12 @@ var _ = Describe("Options", func() {
opts.AddFlags(fs)
err := opts.Parse(fs,
"--cluster-name", "env-cluster",
"--cluster-id", "12345",
"--cluster-endpoint", "https://env-cluster",
"--cluster-region", "us-west",
"--vm-memory-overhead-percent", "0.1")
Expect(err).ToNot(HaveOccurred())
expectOptionsEqual(opts, test.Options(test.OptionsFields{
ClusterName: lo.ToPtr("env-cluster"),
ClusterID: lo.ToPtr(12345),
ClusterEndpoint: lo.ToPtr("https://env-cluster"),
ClusterRegion: lo.ToPtr("us-west"),
VMMemoryOverheadPercent: lo.ToPtr[float64](0.1),
Expand All @@ -84,7 +82,6 @@ var _ = Describe("Options", func() {
Expect(err).ToNot(HaveOccurred())
expectOptionsEqual(opts, test.Options(test.OptionsFields{
ClusterName: lo.ToPtr("env-cluster"),
ClusterID: lo.ToPtr(12345),
ClusterEndpoint: lo.ToPtr("https://env-cluster"),
ClusterRegion: lo.ToPtr("us-west"),
VMMemoryOverheadPercent: lo.ToPtr[float64](0.1),
Expand All @@ -100,11 +97,11 @@ var _ = Describe("Options", func() {
Expect(err).To(HaveOccurred())
})
It("should fail when clusterEndpoint is invalid (not absolute)", func() {
err := opts.Parse(fs, "--cluster-name", "test-cluster", "--cluster-id", "12345", "--cluster-endpoint", "00000000000000000000000.test.us-west.linode.com")
err := opts.Parse(fs, "--cluster-name", "test-cluster", "--cluster-endpoint", "00000000000000000000000.test.us-west.linode.com")
Expect(err).To(HaveOccurred())
})
It("should fail when vmMemoryOverheadPercent is negative", func() {
err := opts.Parse(fs, "--cluster-name", "test-cluster", "--cluster-id", "12345", "--vm-memory-overhead-percent", "-0.01")
err := opts.Parse(fs, "--cluster-name", "test-cluster", "--vm-memory-overhead-percent", "-0.01")
Expect(err).To(HaveOccurred())
})
})
Expand All @@ -113,7 +110,6 @@ var _ = Describe("Options", func() {
func expectOptionsEqual(optsA *options.Options, optsB *options.Options) {
GinkgoHelper()
Expect(optsA.ClusterName).To(Equal(optsB.ClusterName))
Expect(optsA.ClusterID).To(Equal(optsB.ClusterID))
Expect(optsA.ClusterEndpoint).To(Equal(optsB.ClusterEndpoint))
Expect(optsA.VMMemoryOverheadPercent).To(Equal(optsB.VMMemoryOverheadPercent))
Expect(optsA.ClusterRegion).To(Equal(optsB.ClusterRegion))
Expand Down
2 changes: 0 additions & 2 deletions pkg/test/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (

type OptionsFields struct {
ClusterName *string
ClusterID *int
ClusterEndpoint *string
ClusterRegion *string
VMMemoryOverheadPercent *float64
Expand All @@ -42,7 +41,6 @@ func Options(overrides ...OptionsFields) *options.Options {
}
return &options.Options{
ClusterName: lo.FromPtrOr(opts.ClusterName, "123456789012"),
ClusterID: lo.FromPtrOr(opts.ClusterID, fake.DefaultClusterID),
ClusterEndpoint: lo.FromPtrOr(opts.ClusterEndpoint, "https://test-cluster"),
ClusterRegion: lo.FromPtrOr(opts.ClusterRegion, fake.DefaultRegion),
VMMemoryOverheadPercent: lo.FromPtrOr(opts.VMMemoryOverheadPercent, 0.075),
Expand Down
Loading