Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.
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
49 changes: 27 additions & 22 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,40 +1,45 @@
version: "2"
run:
timeout: 5m
allow-parallel-runners: true

issues:
# don't skip warning about doc comments
# don't exclude the default set of lint
exclude-use-default: false
# restore some of the defaults
# (fill in the rest as needed)
exclude-rules:
- path: "api/*"
linters:
- lll
- path: "internal/*"
linters:
- dupl
- lll
linters:
disable-all: true
default: none
enable:
- copyloopvar
- dupl
- errcheck
- copyloopvar
- ginkgolinter
- goconst
- gocyclo
- gofmt
- goimports
- gosimple
- govet
- ineffassign
- lll
- misspell
- nakedret
- staticcheck
- typecheck
- unconvert
- unparam
- unused
exclusions:
generated: lax
rules:
- linters:
- lll
path: api/*
- linters:
- dupl
- lll
path: internal/*
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ GRPCURL = $(LOCALBIN)/grpcurl
KUSTOMIZE_VERSION ?= v5.4.1
CONTROLLER_TOOLS_VERSION ?= v0.16.3
ENVTEST_VERSION ?= release-0.18
GOLANGCI_LINT_VERSION ?= v1.61.0
GOLANGCI_LINT_VERSION ?= v2.1.2
KIND_VERSION ?= v0.27.0
GRPCURL_VERSION ?= v1.9.2

Expand All @@ -202,7 +202,7 @@ $(ENVTEST): $(LOCALBIN)
.PHONY: golangci-lint
golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
$(GOLANGCI_LINT): $(LOCALBIN)
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))

.PHONY: grpcurl
grpcurl: $(GRPCURL) ## Download grpcurl locally if necessary.
Expand Down
1 change: 1 addition & 0 deletions api/v1alpha1/exporter_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type ExporterStatus struct {
Credential *corev1.LocalObjectReference `json:"credential,omitempty"`
Devices []Device `json:"devices,omitempty"`
LeaseRef *corev1.LocalObjectReference `json:"leaseRef,omitempty"`
LastSeen metav1.Time `json:"lastSeen,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
}

Expand Down
1 change: 1 addition & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ spec:
type: array
endpoint:
type: string
lastSeen:
format: date-time
type: string
leaseRef:
description: |-
LocalObjectReference contains enough information to let you locate the
Expand Down
3 changes: 1 addition & 2 deletions internal/controller/client_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
kclient "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"

Expand All @@ -33,7 +32,7 @@ import (

// ClientReconciler reconciles a Client object
type ClientReconciler struct {
client.Client
kclient.Client
Scheme *runtime.Scheme
Signer *oidc.Signer
}
Expand Down
68 changes: 67 additions & 1 deletion internal/controller/exporter_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ package controller
import (
"context"
"fmt"
"time"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -72,6 +75,11 @@ func (r *ExporterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
return ctrl.Result{}, err
}

result, err := r.reconcileStatusConditionsOnline(ctx, &exporter)
if err != nil {
return ctrl.Result{}, err
}

if err := r.reconcileStatusEndpoint(ctx, &exporter); err != nil {
return ctrl.Result{}, err
}
Expand All @@ -80,7 +88,7 @@ func (r *ExporterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
return RequeueConflict(logger, ctrl.Result{}, err)
}

return ctrl.Result{}, nil
return result, nil
}

func (r *ExporterReconciler) reconcileStatusCredential(
Expand Down Expand Up @@ -144,6 +152,64 @@ func (r *ExporterReconciler) reconcileStatusEndpoint(
return nil
}

// nolint:unparam
func (r *ExporterReconciler) reconcileStatusConditionsOnline(
_ context.Context,
exporter *jumpstarterdevv1alpha1.Exporter,
) (ctrl.Result, error) {
var requeueAfter time.Duration = 0

if exporter.Status.LastSeen.IsZero() {
meta.SetStatusCondition(&exporter.Status.Conditions, metav1.Condition{
Type: string(jumpstarterdevv1alpha1.ExporterConditionTypeOnline),
Status: metav1.ConditionFalse,
ObservedGeneration: exporter.Generation,
Reason: "Seen",
Message: "Never seen",
})
// marking the exporter offline, no need to requeue
} else if time.Since(exporter.Status.LastSeen.Time) > time.Minute {
meta.SetStatusCondition(&exporter.Status.Conditions, metav1.Condition{
Type: string(jumpstarterdevv1alpha1.ExporterConditionTypeOnline),
Status: metav1.ConditionFalse,
ObservedGeneration: exporter.Generation,
Reason: "Seen",
Message: "Last seen more than 1 minute ago",
})
// marking the exporter offline, no need to requeue
} else {
meta.SetStatusCondition(&exporter.Status.Conditions, metav1.Condition{
Type: string(jumpstarterdevv1alpha1.ExporterConditionTypeOnline),
Status: metav1.ConditionTrue,
ObservedGeneration: exporter.Generation,
Reason: "Seen",
Message: "Lase seen less than 1 minute ago",
})
// marking the exporter online, requeue after 30 seconds
requeueAfter = time.Second * 30
}

if exporter.Status.Devices == nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, ok, so no devices ... = Unregistered. Makes sense.

meta.SetStatusCondition(&exporter.Status.Conditions, metav1.Condition{
Type: string(jumpstarterdevv1alpha1.ExporterConditionTypeRegistered),
Status: metav1.ConditionFalse,
ObservedGeneration: exporter.Generation,
Reason: "Unregister",
})
} else {
meta.SetStatusCondition(&exporter.Status.Conditions, metav1.Condition{
Type: string(jumpstarterdevv1alpha1.ExporterConditionTypeRegistered),
Status: metav1.ConditionTrue,
ObservedGeneration: exporter.Generation,
Reason: "Register",
})
}

return ctrl.Result{
RequeueAfter: requeueAfter,
}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *ExporterReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
Expand Down
16 changes: 7 additions & 9 deletions internal/controller/lease_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,15 +465,13 @@ func filterOutOfflineExporters(matchingExporters []jumpstarterdevv1alpha1.Export
onlineExporters := slices.DeleteFunc(
matchingExporters,
func(exporter jumpstarterdevv1alpha1.Exporter) bool {
return !(true &&
Comment thread
NickCao marked this conversation as resolved.
meta.IsStatusConditionTrue(
exporter.Status.Conditions,
string(jumpstarterdevv1alpha1.ExporterConditionTypeRegistered),
) &&
meta.IsStatusConditionTrue(
exporter.Status.Conditions,
string(jumpstarterdevv1alpha1.ExporterConditionTypeOnline),
))
return !true || !meta.IsStatusConditionTrue(
exporter.Status.Conditions,
string(jumpstarterdevv1alpha1.ExporterConditionTypeRegistered),
) || !meta.IsStatusConditionTrue(
exporter.Status.Conditions,
string(jumpstarterdevv1alpha1.ExporterConditionTypeOnline),
)
},
)
return onlineExporters
Expand Down
7 changes: 7 additions & 0 deletions internal/controller/lease_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,13 @@ func setExporterOnlineConditions(ctx context.Context, name string, status metav1
Status: status,
Reason: "dummy",
})
if status == metav1.ConditionTrue {
exporter.Status.Devices = []jumpstarterdevv1alpha1.Device{{}}
exporter.Status.LastSeen = metav1.Now()
} else {
exporter.Status.Devices = nil
exporter.Status.LastSeen = metav1.NewTime(metav1.Now().Add(-time.Minute * 2))
}
Expect(k8sClient.Status().Update(ctx, exporter)).To(Succeed())
}

Expand Down
3 changes: 1 addition & 2 deletions internal/controller/secret_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/jumpstarter-dev/jumpstarter-controller/internal/oidc"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
apisv1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -23,7 +22,7 @@ func ensureSecret(
scheme *runtime.Scheme,
signer *oidc.Signer,
subject string,
owner apisv1.Object,
owner metav1.Object,
) (*corev1.Secret, error) {
logger := log.FromContext(ctx).WithName("ensureSecret")
var secret corev1.Secret
Expand Down
2 changes: 1 addition & 1 deletion internal/protocol/jumpstarter/v1/jumpstarter.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading