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
34 changes: 34 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Lint

on:
pull_request:
branches:
- main
- 'release-*'
paths:
- '**.go'
- 'go.mod'
- 'go.sum'
- '.golangci.yml'
- '.github/workflows/lint.yml'

permissions:
contents: read
# Optional: allow read access to pull request. Use with `only-new-issues` option.
pull-requests: read

jobs:
golangci:
name: golangci-lint
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.24'

- name: Run golangci-lint
run: make lint
55 changes: 55 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# golangci-lint configuration file
# Documentation: https://golangci-lint.run/usage/configuration/

version: 2

run:
# Timeout for analysis
timeout: 5m

# Exit code when at least one issue was found
issues-exit-code: 1

# Include test files
tests: true

# output configuration options
output:
formats:
- format: colored-line-number
print-issued-lines: true
print-linter-name: true
sort-results: true

# all available settings of specific linters
linters-settings:
errcheck:
check-type-assertions: true
check-blank: false
gocyclo:
min-complexity: 15
misspell:
locale: US
unused:
check-exported: false
unparam:
check-exported: false
staticcheck:
checks: ["all"]

linters:
disable:
- gosimple
enable:
- errcheck
- govet
- ineffassign
- staticcheck
- unused
- misspell
- revive
- gosec
- unconvert
- unparam
- goconst
- gocyclo
13 changes: 10 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ vet: ## Run go vet against code.
go vet ./...

# Run linter
lint:
@echo "Running linter..."
golangci-lint run ./...
.PHONY: lint
lint: golangci-lint ## Run golangci-lint
$(GOLANGCI_LINT) run ./...

# Install to system
install: build
Expand Down Expand Up @@ -273,15 +273,22 @@ $(LOCALBIN):

## Tool Binaries
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
GOLANGCI_LINT ?= $(LOCALBIN)/golangci-lint

## Tool Versions
CONTROLLER_TOOLS_VERSION ?= v0.17.2
GOLANGCI_LINT_VERSION ?= v1.64.1

.PHONY: controller-gen
controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
$(CONTROLLER_GEN): $(LOCALBIN)
$(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION))

.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))

# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
# $1 - target path with name of binary
# $2 - package url which can be installed
Expand Down
2 changes: 1 addition & 1 deletion cmd/agentd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func main() {

err = ctrl.NewControllerManagedBy(mgr).
For(&sandboxv1alpha1.Sandbox{}).
Complete(&agentd.AgentdReconciler{
Complete(&agentd.Reconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
})
Expand Down
8 changes: 4 additions & 4 deletions pkg/agentd/agentd.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ var (
SessionExpirationTimeout = 15 * time.Minute
)

// AgentdReconciler reconciles a Sandbox object
type AgentdReconciler struct {
// Reconciler reconciles a Sandbox object
type Reconciler struct {
client.Client
Scheme *runtime.Scheme
}

func (r *AgentdReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
sandbox := &sandboxv1alpha1.Sandbox{}
err := r.Get(ctx, req.NamespacedName, sandbox)
if err != nil {
Expand Down Expand Up @@ -58,7 +58,7 @@ func (r *AgentdReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
}

// SetupWithManager sets up the controller with the Manager.
func (r *AgentdReconciler) SetupWithManager(mgr ctrl.Manager) error {
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&sandboxv1alpha1.Sandbox{}).
Complete(r)
Expand Down
4 changes: 2 additions & 2 deletions pkg/agentd/agentd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

func TestAgentdReconciler_Reconcile_WithLastActivity(t *testing.T) {
func TestReconciler_Reconcile_WithLastActivity(t *testing.T) {
testScheme := runtime.NewScheme()
utilruntime.Must(scheme.AddToScheme(testScheme))
utilruntime.Must(sandboxv1alpha1.AddToScheme(testScheme))
Expand Down Expand Up @@ -107,7 +107,7 @@ func TestAgentdReconciler_Reconcile_WithLastActivity(t *testing.T) {
WithObjects(tt.sandbox).
Build()

reconciler := &AgentdReconciler{
reconciler := &Reconciler{
Client: fakeClient,
Scheme: testScheme,
}
Expand Down
65 changes: 0 additions & 65 deletions pkg/redis/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,71 +325,6 @@ func (c *client) ListInactiveSandboxes(ctx context.Context, before time.Time, li
return c.loadSandboxesBySessionIDs(ctx, ids)
}

// loadSandboxesByIDs loads sandbox objects for the given sandbox IDs.
func (c *client) loadSandboxesByIDs(ctx context.Context, sandboxIDs []string) ([]*types.SandboxRedis, error) {
if len(sandboxIDs) == 0 {
return nil, nil
}

sessionIDCmds := make([]*redisv9.StringCmd, len(sandboxIDs))
pipe := c.rdb.Pipeline()
for i, id := range sandboxIDs {
sessionKey := c.sandboxKey(id)
sessionIDCmds[i] = pipe.Get(ctx, sessionKey)
}
_, _ = pipe.Exec(ctx)

type pair struct {
sandboxID string
sessionID string
}
pairs := make([]pair, 0, len(sandboxIDs))

for i, cmd := range sessionIDCmds {
sessionID, err := cmd.Result()
if errors.Is(err, redisv9.Nil) {
continue
}
if err != nil {
return nil, fmt.Errorf("loadSandboxesByIDs: get sessionID for sandbox %s: %w", sandboxIDs[i], err)
}
pairs = append(pairs, pair{
sandboxID: sandboxIDs[i],
sessionID: sessionID,
})
}

if len(pairs) == 0 {
return nil, nil
}

sandboxCmds := make([]*redisv9.StringCmd, len(pairs))
pipe = c.rdb.Pipeline()
for i, p := range pairs {
sessionKey := c.sessionKey(p.sessionID)
sandboxCmds[i] = pipe.Get(ctx, sessionKey)
}
_, _ = pipe.Exec(ctx)

result := make([]*types.SandboxRedis, 0, len(pairs))
for i, cmd := range sandboxCmds {
data, err := cmd.Bytes()
if errors.Is(err, redisv9.Nil) {
continue
}
if err != nil {
return nil, fmt.Errorf("loadSandboxesByIDs: get sandbox JSON for session %s: %w", pairs[i].sessionID, err)
}
var sandboxRedis types.SandboxRedis
if err := json.Unmarshal(data, &sandboxRedis); err != nil {
return nil, fmt.Errorf("loadSandboxesByIDs: unmarshal sandbox for session %s: %w", pairs[i].sessionID, err)
}
result = append(result, &sandboxRedis)
}

return result, nil
}

// loadSandboxesBySessionIDs loads sandbox objects for the given session IDs.
func (c *client) loadSandboxesBySessionIDs(ctx context.Context, sessionIDs []string) ([]*types.SandboxRedis, error) {
if len(sessionIDs) == 0 {
Expand Down
6 changes: 0 additions & 6 deletions pkg/workloadmanager/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,6 @@ func (s *Server) validateServiceAccountToken(ctx context.Context, token string)
return true, username, nil
}

// checkSandboxAccess checks if the current user has access to the sandbox
// Returns true if the user is the creator of the sandbox
func (s *Server) checkSandboxAccess(sandbox *Sandbox, serviceAccountName string) bool {
return sandbox.CreatorServiceAccount == serviceAccountName
}

// extractUserInfo extracts user information from request context
// Returns userToken, userNamespace, serviceAccount, serviceAccountName
// If extraction fails, returns empty strings
Expand Down
5 changes: 3 additions & 2 deletions pkg/workloadmanager/client_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func NewClientCache(maxSize int) *ClientCache {
// Get retrieves a client from cache based on key (service account)
// Returns the client if found and cached token is not expired, nil otherwise
// Different tokens for the same service account can share the same client
func (c *ClientCache) Get(key, token string) *UserK8sClient {
func (c *ClientCache) Get(key string) *UserK8sClient {
c.mu.Lock()
defer c.mu.Unlock()

Expand Down Expand Up @@ -158,6 +158,7 @@ func (c *ClientCache) evictOldest() {
return
}

// nolint:errcheck
entry := back.Value.(*clientCacheEntry)
c.lruList.Remove(back)
delete(c.cache, entry.key)
Expand Down Expand Up @@ -280,7 +281,7 @@ func (c *TokenCache) evictOldest() {
if back == nil {
return
}

// nolint:errcheck
entry := back.Value.(*tokenCacheEntry)
c.lruList.Remove(back)
delete(c.cache, entry.token)
Expand Down
2 changes: 1 addition & 1 deletion pkg/workloadmanager/codeinterpreter_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ func (r *CodeInterpreterReconciler) deleteSandboxTemplate(ctx context.Context, c
}

// convertToPodTemplate converts CodeInterpreterSandboxTemplate to sandboxv1alpha1.PodTemplate
func (r *CodeInterpreterReconciler) convertToPodTemplate(template *runtimev1alpha1.CodeInterpreterSandboxTemplate, ci *runtimev1alpha1.CodeInterpreter) sandboxv1alpha1.PodTemplate {
func (r *CodeInterpreterReconciler) convertToPodTemplate(template *runtimev1alpha1.CodeInterpreterSandboxTemplate, _ *runtimev1alpha1.CodeInterpreter) sandboxv1alpha1.PodTemplate {
// Build pod spec
podSpec := corev1.PodSpec{
Containers: []corev1.Container{
Expand Down
8 changes: 2 additions & 6 deletions pkg/workloadmanager/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func (s *Server) handleHealth(c *gin.Context) {
}

// handleCreateSandbox handles sandbox creation requests
// nolint :gocyclo
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

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

The nolint directive has incorrect syntax. There should be no space between nolint and the colon. Change // nolint :gocyclo to // nolint:gocyclo for golangci-lint to properly recognize and apply the directive.

Suggested change
// nolint :gocyclo
// nolint:gocyclo

Copilot uses AI. Check for mistakes.
func (s *Server) handleCreateSandbox(c *gin.Context) {
createAgentRequest := &types.CreateSandboxRequest{}
if err := c.ShouldBindJSON(createAgentRequest); err != nil {
Expand Down Expand Up @@ -162,12 +163,7 @@ func (s *Server) handleCreateSandbox(c *gin.Context) {
return
}

redisCacheInfo, err := convertSandboxToRedisCache(createdSandbox, podIP, externalInfo)
if err != nil {
log.Printf("convert to redis cache info failed: %v", err)
respondError(c, http.StatusInternalServerError, "SANDBOX_BUILD_FAILED", err.Error())
return
}
redisCacheInfo := convertSandboxToRedisCache(createdSandbox, podIP, externalInfo)

response := &types.CreateSandboxResponse{
SessionID: externalInfo.SessionID,
Expand Down
4 changes: 2 additions & 2 deletions pkg/workloadmanager/k8s_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const (

var (
// SessionIdLabelKey labels key for session id
SessionIdLabelKey = "runtime.agentcube.io/session-id"
SessionIdLabelKey = "runtime.agentcube.io/session-id" // revive:disable-line:var-naming - keep label backward compatible
// WorkloadNameLabelKey labels key for workload name
WorkloadNameLabelKey = "runtime.agentcube.io/workload-name"
// Annotation key for last activity time
Expand Down Expand Up @@ -142,7 +142,7 @@ func (c *K8sClient) GetOrCreateUserK8sClient(userToken, namespace, serviceAccoun
cacheKey := makeCacheKey(namespace, serviceAccountName)

// Try to get from cache
if cachedClient := c.clientCache.Get(cacheKey, userToken); cachedClient != nil {
if cachedClient := c.clientCache.Get(cacheKey); cachedClient != nil {
return cachedClient, nil
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/workloadmanager/sandbox_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (r *SandboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
return ctrl.Result{}, nil
}

func (r *SandboxReconciler) WatchSandboxOnce(ctx context.Context, namespace, name string) <-chan SandboxStatusUpdate {
func (r *SandboxReconciler) WatchSandboxOnce(_ context.Context, namespace, name string) <-chan SandboxStatusUpdate {
resultChan := make(chan SandboxStatusUpdate, 1)
key := types.NamespacedName{Namespace: namespace, Name: name}

Expand Down
10 changes: 5 additions & 5 deletions pkg/workloadmanager/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,17 @@ func (s *SandboxStore) List() []*Sandbox {
}

// InitializeWithInformer initializes the store with an informer and performs initial sync
func (s *SandboxStore) InitializeWithInformer(ctx context.Context, informer cache.SharedInformer, k8sClient *K8sClient) error {
func (s *SandboxStore) InitializeWithInformer(ctx context.Context, informer cache.SharedInformer, _ *K8sClient) error {
s.mu.Lock()
s.informer = informer
s.mu.Unlock()

// Set up event handlers
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
_, _ = informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
s.onSandboxAdd(obj)
},
UpdateFunc: func(oldObj, newObj interface{}) {
UpdateFunc: func(_, newObj interface{}) {
s.onSandboxUpdate(newObj)
},
DeleteFunc: func(obj interface{}) {
Expand Down Expand Up @@ -289,7 +289,7 @@ func buildSandboxRedisCachePlaceHolder(sandboxCRD *sandboxv1alpha1.Sandbox, exte
return sandboxRedis
}

func convertSandboxToRedisCache(sandboxCRD *sandboxv1alpha1.Sandbox, podIP string, externalInfo *sandboxExternalInfo) (*types.SandboxRedis, error) {
func convertSandboxToRedisCache(sandboxCRD *sandboxv1alpha1.Sandbox, podIP string, externalInfo *sandboxExternalInfo) *types.SandboxRedis {
createdAt := sandboxCRD.GetCreationTimestamp().Time
expiresAt := createdAt.Add(DefaultSandboxTTL)
if sandboxCRD.Spec.ShutdownTime != nil {
Expand All @@ -316,7 +316,7 @@ func convertSandboxToRedisCache(sandboxCRD *sandboxv1alpha1.Sandbox, podIP strin
if externalInfo.SandboxClaimName != "" {
sandboxRedis.SandboxClaimName = externalInfo.SandboxClaimName
}
return sandboxRedis, nil
return sandboxRedis
}

// getSandboxStatus extracts status from Sandbox CRD conditions
Expand Down
Loading
Loading