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
4 changes: 1 addition & 3 deletions .github/workflows/golangci-lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,4 @@ jobs:
go-version: 'stable'
- uses: actions/checkout@v4
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
args: --timeout=5m
run: make lint
21 changes: 17 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
export API_TAGS ?= ExternalClusterAPI,AuthTokenAPI,OperationsAPI,AutoscalerAPI
export SWAGGER_LOCATION ?= https://api.cast.ai/v1/spec/openapi.json

GO_INSTALL = ./hack/go-install.sh

TOOLS_DIR=bin
ROOT_DIR=$(abspath .)
TOOLS_GOBIN_DIR := $(abspath $(TOOLS_DIR))

GOLANGCI_LINT_VER := v1.62.2
GOLANGCI_LINT_BIN := golangci-lint
GOLANGCI_LINT := $(TOOLS_GOBIN_DIR)/$(GOLANGCI_LINT_BIN)-$(GOLANGCI_LINT_VER)

$(GOLANGCI_LINT):
GOBIN=$(TOOLS_GOBIN_DIR) $(GO_INSTALL) github.com/golangci/golangci-lint/cmd/golangci-lint $(GOLANGCI_LINT_BIN) $(GOLANGCI_LINT_VER)

build:
CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w" -o bin/castai-cluster-controller-amd64 .
docker build -t us-docker.pkg.dev/castai-hub/library/cluster-controller:$(VERSION) .
Expand All @@ -10,12 +23,12 @@ push:

release: build push

lint:
golangci-lint run ./...
lint: $(GOLANGCI_LINT)
$(GOLANGCI_LINT) run --timeout 20m ./...
.PHONY: lint

fix:
golangci-lint run --fix ./...
fix: $(GOLANGCI_LINT)
$(GOLANGCI_LINT) run --fix ./...
.PHONY: fix

test:
Expand Down
48 changes: 48 additions & 0 deletions hack/go-install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env bash

# Originally copied from
# https://github.com/kubernetes-sigs/cluster-api-provider-gcp/blob/c26a68b23e9317323d5d37660fe9d29b3d2ff40c/scripts/go_install.sh

set -o errexit
set -o nounset
set -o pipefail

if [[ -z "${1:-}" ]]; then
echo "must provide module as first parameter"
exit 1
fi

if [[ -z "${2:-}" ]]; then
echo "must provide binary name as second parameter"
exit 1
fi

if [[ -z "${3:-}" ]]; then
echo "must provide version as third parameter"
exit 1
fi

if [[ -z "${GOBIN:-}" ]]; then
echo "GOBIN is not set. Must set GOBIN to install the bin in a specified directory."
exit 1
fi

mkdir -p "${GOBIN}"

tmp_dir=$(mktemp -d -t goinstall_XXXXXXXXXX)
function clean {
rm -rf "${tmp_dir}"
}
trap clean EXIT

rm "${GOBIN}/${2}"* > /dev/null 2>&1 || true

cd "${tmp_dir}"

# create a new module in the tmp directory
go mod init fake/mod

# install the golang module specified as the first argument
go install -tags kcptools "${1}@${3}"
mv "${GOBIN}/${2}" "${GOBIN}/${2}-${3}"
ln -sf "${GOBIN}/${2}-${3}" "${GOBIN}/${2}"
18 changes: 10 additions & 8 deletions internal/actions/create_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ func (h *CreateHandler) Handle(ctx context.Context, action *castai.ClusterAction
}

newObj := &unstructured.Unstructured{Object: req.Object}
if newObj.GetNamespace() == "" {
return fmt.Errorf("namespace not provided %w", errAction)
}

log := h.log.WithFields(logrus.Fields{
ActionIDLogField: action.ID,
Expand All @@ -53,21 +50,26 @@ func (h *CreateHandler) Handle(ctx context.Context, action *castai.ClusterAction
"name": newObj.GetName(),
})

r := h.client.Resource(schema.GroupVersionResource{
gvkResource := h.client.Resource(schema.GroupVersionResource{
Group: req.Group,
Version: req.Version,
Resource: req.Resource,
}).Namespace(newObj.GetNamespace())
})

var resource dynamic.ResourceInterface = gvkResource
if newObj.GetNamespace() != "" {
resource = gvkResource.Namespace(newObj.GetNamespace())
}

log.Info("creating new resource")
_, err := r.Create(ctx, newObj, metav1.CreateOptions{})
_, err := resource.Create(ctx, newObj, metav1.CreateOptions{})
if err != nil && !apierrors.IsAlreadyExists(err) {
return fmt.Errorf("creating resource %v: %w", req.Resource, err)
}

if apierrors.IsAlreadyExists(err) {
log.Info("resource already exists, patching")
obj, err := r.Get(ctx, newObj.GetName(), metav1.GetOptions{})
obj, err := resource.Get(ctx, newObj.GetName(), metav1.GetOptions{})
if err != nil {
return fmt.Errorf("getting old resource: %w", err)
}
Expand Down Expand Up @@ -106,7 +108,7 @@ func (h *CreateHandler) Handle(ctx context.Context, action *castai.ClusterAction
}

log.Infof("patching resource: %s", patch)
_, err = r.Patch(ctx, obj.GetName(), k8s_types.MergePatchType, patch, metav1.PatchOptions{})
_, err = resource.Patch(ctx, obj.GetName(), k8s_types.MergePatchType, patch, metav1.PatchOptions{})
if err != nil {
return fmt.Errorf("patching resource %v: %w", obj.GetName(), err)
}
Expand Down
75 changes: 56 additions & 19 deletions internal/actions/create_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
func Test_newCreateHandler(t *testing.T) {
scheme := runtime.NewScheme()
_ = appsv1.AddToScheme(scheme)
_ = v1.AddToScheme(scheme)
ctx := context.Background()

now := metav1.Time{Time: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.Local)}
Expand All @@ -30,7 +31,7 @@ func Test_newCreateHandler(t *testing.T) {
action *castai.ClusterAction
convertFn func(i map[string]interface{}) client.Object
err error
want *appsv1.Deployment
want runtime.Object
}{
"should return error when action is of a different type": {
action: &castai.ClusterAction{
Expand Down Expand Up @@ -72,17 +73,17 @@ func Test_newCreateHandler(t *testing.T) {
Version: appsv1.SchemeGroupVersion.Version,
Resource: "deployments",
},
Object: getObj(t, newDeployment(func(d *appsv1.Deployment) {
d.Labels = map[string]string{"changed": "true"}
Object: getObj(t, newDeployment(func(d runtime.Object) {
d.(*appsv1.Deployment).Labels = map[string]string{"changed": "true"}
})),
},
},
objs: []runtime.Object{newDeployment(func(d *appsv1.Deployment) {
d.CreationTimestamp = now
objs: []runtime.Object{newDeployment(func(d runtime.Object) {
d.(*appsv1.Deployment).CreationTimestamp = now
})},
want: newDeployment(func(d *appsv1.Deployment) {
d.CreationTimestamp = now
d.Labels = map[string]string{"changed": "true"}
want: newDeployment(func(d runtime.Object) {
d.(*appsv1.Deployment).CreationTimestamp = now
d.(*appsv1.Deployment).Labels = map[string]string{"changed": "true"}
}),
convertFn: func(i map[string]interface{}) client.Object {
out := &appsv1.Deployment{}
Expand All @@ -98,24 +99,42 @@ func Test_newCreateHandler(t *testing.T) {
Version: appsv1.SchemeGroupVersion.Version,
Resource: "deployments",
},
Object: getObj(t, newDeployment(func(d *appsv1.Deployment) {
Object: getObj(t, newDeployment(func(d runtime.Object) {
})),
},
},
objs: []runtime.Object{newDeployment(func(d *appsv1.Deployment) {
d.CreationTimestamp = now
d.Finalizers = []string{"autoscaling.cast.ai/recommendation"}
objs: []runtime.Object{newDeployment(func(d runtime.Object) {
d.(*appsv1.Deployment).CreationTimestamp = now
d.(*appsv1.Deployment).Finalizers = []string{"autoscaling.cast.ai/recommendation"}
})},
want: newDeployment(func(d *appsv1.Deployment) {
d.CreationTimestamp = now
d.Finalizers = []string{"autoscaling.cast.ai/recommendation"}
want: newDeployment(func(d runtime.Object) {
d.(*appsv1.Deployment).CreationTimestamp = now
d.(*appsv1.Deployment).Finalizers = []string{"autoscaling.cast.ai/recommendation"}
}),
convertFn: func(i map[string]interface{}) client.Object {
out := &appsv1.Deployment{}
_ = runtime.DefaultUnstructuredConverter.FromUnstructured(i, out)
return out
},
},
"should create new namespace": {
action: &castai.ClusterAction{
ActionCreate: &castai.ActionCreate{
GroupVersionResource: castai.GroupVersionResource{
Group: v1.SchemeGroupVersion.Group,
Version: v1.SchemeGroupVersion.Version,
Resource: "namespaces",
},
Object: getObj(t, newNamespace()),
},
},
want: newNamespace(),
convertFn: func(i map[string]interface{}) client.Object {
out := &v1.Namespace{}
_ = runtime.DefaultUnstructuredConverter.FromUnstructured(i, out)
return out
},
},
}

for name, test := range tests {
Expand Down Expand Up @@ -156,8 +175,8 @@ func getObj(t *testing.T, obj runtime.Object) map[string]interface{} {
return unstructured
}

func newDeployment(opts ...func(d *appsv1.Deployment)) *appsv1.Deployment {
out := &appsv1.Deployment{
func newDeployment(opts ...func(d runtime.Object)) runtime.Object {
out := appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "apps/v1",
Expand All @@ -170,8 +189,26 @@ func newDeployment(opts ...func(d *appsv1.Deployment)) *appsv1.Deployment {
Template: v1.PodTemplateSpec{},
},
}
var obj runtime.Object = &out
for _, opt := range opts {
opt(obj)
}
return obj
}

func newNamespace(opts ...func(d runtime.Object)) runtime.Object {
out := v1.Namespace{
TypeMeta: metav1.TypeMeta{
Kind: "Namespace",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "bob-namespace",
},
}
var obj runtime.Object = &out
for _, opt := range opts {
opt(out)
opt(obj)
}
return out
return obj
}
2 changes: 1 addition & 1 deletion internal/actions/delete_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (h *DeleteHandler) Handle(ctx context.Context, action *castai.ClusterAction
})

var res dynamic.ResourceInterface = r
if req.ID.Namespace != nil {
if req.ID.Namespace != nil && *req.ID.Namespace != "" {
res = r.Namespace(*req.ID.Namespace)
}

Expand Down
12 changes: 9 additions & 3 deletions internal/actions/delete_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ func Test_newDeleteHandler(t *testing.T) {
},
},
objs: []runtime.Object{
newDeployment(func(d *appsv1.Deployment) { d.SetName("nginx-1") }),
newDeployment(func(d runtime.Object) {
d.(*appsv1.Deployment).SetName("nginx-1")
}),
},
want: 1,
},
Expand All @@ -70,8 +72,12 @@ func Test_newDeleteHandler(t *testing.T) {
},
objs: []runtime.Object{
newDeployment(),
newDeployment(func(d *appsv1.Deployment) { d.SetName("nginx-1") }),
newDeployment(func(d *appsv1.Deployment) { d.SetName("nginx-2") }),
newDeployment(func(d runtime.Object) {
d.(*appsv1.Deployment).SetName("nginx-1")
}),
newDeployment(func(d runtime.Object) {
d.(*appsv1.Deployment).SetName("nginx-2")
}),
},
want: 2,
},
Expand Down
Loading