feat: full kubeclaw operator — CRDs, controllers, deploy manifests, Helm chart
- 7 CRDs (Agent, AgentConfig, AgentGroup, Channel, Provider, WorkflowTemplate, CompliancePolicy) - 3 controllers (Agent, WorkflowTemplate, CompliancePolicy) - Generated deepcopy methods (controller-gen v0.16.5) - Dockerfile (multi-stage Go build) - Nix build-image.nix (dockerTools.streamLayeredImage) - Helm chart with RBAC, deployment, service account - Deploy manifests (RBAC + controller deployment) - Sample manifests for all 7 CRDs - Quickstart script - Successfully deployed to k3s on n97-mini: - Controller running and reconciling - All 7 CRDs registered - Agent CR creates Pod (ErrImagePull expected — no agent image yet) - Controller logs show reconciliation working
This commit is contained in:
parent
7607086664
commit
ece81f651c
47 changed files with 4038 additions and 13 deletions
13
Dockerfile
Normal file
13
Dockerfile
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
FROM golang:1.24-alpine AS builder
|
||||
WORKDIR /workspace
|
||||
COPY go.mod go.sum* ./
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o manager ./cmd/manager
|
||||
|
||||
FROM alpine:3.21
|
||||
RUN apk add --no-cache ca-certificates tzdata
|
||||
WORKDIR /
|
||||
COPY --from=builder /workspace/manager .
|
||||
USER 65532:65532
|
||||
ENTRYPOINT ["/manager"]
|
||||
|
|
@ -4,8 +4,9 @@ Copyright 2025 Scott Scoble.
|
|||
Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
// Package v1alpha1 contains API Schema definitions for the scobleclaw v1alpha1 API group.
|
||||
// +groupName=scobleclaw.ai
|
||||
// Package v1alpha1 contains API Schema definitions for the kubeclaw v1alpha1 API group.
|
||||
// +groupName=kubeclaw.scoble.me
|
||||
// +kubebuilder:object:generate=true
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
|
|
@ -15,7 +16,7 @@ import (
|
|||
|
||||
var (
|
||||
GroupVersion = schema.GroupVersion{
|
||||
Group: "scobleclaw.ai",
|
||||
Group: "kubeclaw.scoble.me",
|
||||
Version: "v1alpha1",
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ package v1alpha1
|
|||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// WorkflowTemplateSpec defines a workflow template that agents can instantiate.
|
||||
|
|
@ -22,14 +23,14 @@ type WorkflowTemplateSpec struct {
|
|||
Steps []WorkflowStep `json:"steps"`
|
||||
|
||||
// ParametersSchema defines the JSON Schema for allowed parameters.
|
||||
// +kubebuilder:validation:Schemaless
|
||||
// +optional
|
||||
// +kubebuilder:pruning:PreserveUnknownFields
|
||||
ParametersSchema map[string]interface{} `json:"parametersSchema"`
|
||||
ParametersSchema runtime.RawExtension `json:"parametersSchema,omitempty"`
|
||||
|
||||
// OutputSchema defines the expected output shape (JSON Schema).
|
||||
// +kubebuilder:validation:Schemaless
|
||||
// +optional
|
||||
// +kubebuilder:pruning:PreserveUnknownFields
|
||||
OutputSchema map[string]interface{} `json:"outputSchema"`
|
||||
OutputSchema runtime.RawExtension `json:"outputSchema,omitempty"`
|
||||
|
||||
// ComplianceRequirements lists compliance frameworks this template satisfies.
|
||||
// +optional
|
||||
|
|
|
|||
1230
api/v1alpha1/zz_generated.deepcopy.go
Normal file
1230
api/v1alpha1/zz_generated.deepcopy.go
Normal file
File diff suppressed because it is too large
Load diff
BIN
bin/manager
Executable file
BIN
bin/manager
Executable file
Binary file not shown.
18
charts/kubeclaw/Chart.yaml
Normal file
18
charts/kubeclaw/Chart.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: v2
|
||||
name: kubeclaw
|
||||
description: OpenClaw operator for Kubernetes — run Scobleclaw agents as K8s workloads
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "0.1.0"
|
||||
keywords:
|
||||
- openclaw
|
||||
- agent
|
||||
- llm
|
||||
- ai
|
||||
- operator
|
||||
home: https://github.com/sscoble/kubeclaw
|
||||
sources:
|
||||
- https://github.com/sscoble/kubeclaw
|
||||
maintainers:
|
||||
- name: Scott Scoble
|
||||
email: scott@scoble.me
|
||||
104
charts/kubeclaw/crds/kubeclaw.scoble.me_agentconfigs.yaml
Normal file
104
charts/kubeclaw/crds/kubeclaw.scoble.me_agentconfigs.yaml
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: agentconfigs.kubeclaw.scoble.me
|
||||
labels:
|
||||
app.kubernetes.io/name: kubeclaw
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
spec:
|
||||
group: kubeclaw.scoble.me
|
||||
names:
|
||||
kind: AgentConfig
|
||||
listKind: AgentConfigList
|
||||
plural: agentconfigs
|
||||
singular: agentconfig
|
||||
shortNames:
|
||||
- acfg
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
workspace:
|
||||
type: object
|
||||
properties:
|
||||
repo:
|
||||
type: string
|
||||
branch:
|
||||
type: string
|
||||
memory:
|
||||
type: object
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
autoCommit:
|
||||
type: boolean
|
||||
heartbeat:
|
||||
type: object
|
||||
properties:
|
||||
intervalSeconds:
|
||||
type: integer
|
||||
prompt:
|
||||
type: string
|
||||
sandbox:
|
||||
type: string
|
||||
enum: [inherit, require, none]
|
||||
thinking:
|
||||
type: string
|
||||
enum: ["off", "low", "medium", "high"]
|
||||
model:
|
||||
type: string
|
||||
fallbacks:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
toolsAllow:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
toolsDeny:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
env:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
value:
|
||||
type: string
|
||||
valueFrom:
|
||||
type: object
|
||||
properties:
|
||||
secretKeyRef:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
key:
|
||||
type: string
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
ready:
|
||||
type: boolean
|
||||
usedByAgents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
107
charts/kubeclaw/crds/kubeclaw.scoble.me_agentgroups.yaml
Normal file
107
charts/kubeclaw/crds/kubeclaw.scoble.me_agentgroups.yaml
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: agentgroups.kubeclaw.scoble.me
|
||||
labels:
|
||||
app.kubernetes.io/name: kubeclaw
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
spec:
|
||||
group: kubeclaw.scoble.me
|
||||
names:
|
||||
kind: AgentGroup
|
||||
listKind: AgentGroupList
|
||||
plural: agentgroups
|
||||
singular: agentgroup
|
||||
shortNames:
|
||||
- agg
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: Members
|
||||
type: integer
|
||||
jsonPath: .status.memberCount
|
||||
- name: Ready
|
||||
type: boolean
|
||||
jsonPath: .status.ready
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: [members]
|
||||
properties:
|
||||
sharedConfigRef:
|
||||
type: string
|
||||
sharedProviders:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
sharedChannels:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
members:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [name, agentId]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
agentId:
|
||||
type: string
|
||||
model:
|
||||
type: string
|
||||
thinking:
|
||||
type: string
|
||||
configOverrides:
|
||||
type: object
|
||||
properties:
|
||||
model:
|
||||
type: string
|
||||
thinking:
|
||||
type: string
|
||||
allowedTemplates:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
resources:
|
||||
type: object
|
||||
properties:
|
||||
requests:
|
||||
type: object
|
||||
properties:
|
||||
cpu:
|
||||
type: string
|
||||
memory:
|
||||
type: string
|
||||
limits:
|
||||
type: object
|
||||
properties:
|
||||
cpu:
|
||||
type: string
|
||||
memory:
|
||||
type: string
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
ready:
|
||||
type: boolean
|
||||
memberCount:
|
||||
type: integer
|
||||
memberReady:
|
||||
type: integer
|
||||
agentRefs:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
147
charts/kubeclaw/crds/kubeclaw.scoble.me_agents.yaml
Normal file
147
charts/kubeclaw/crds/kubeclaw.scoble.me_agents.yaml
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: agents.kubeclaw.scoble.me
|
||||
labels:
|
||||
app.kubernetes.io/name: kubeclaw
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
spec:
|
||||
group: kubeclaw.scoble.me
|
||||
names:
|
||||
kind: Agent
|
||||
listKind: AgentList
|
||||
plural: agents
|
||||
singular: agent
|
||||
shortNames:
|
||||
- ag
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: AgentID
|
||||
type: string
|
||||
jsonPath: .spec.agentId
|
||||
- name: Model
|
||||
type: string
|
||||
jsonPath: .spec.model
|
||||
- name: Ready
|
||||
type: boolean
|
||||
jsonPath: .status.ready
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: [agentId]
|
||||
properties:
|
||||
agentId:
|
||||
type: string
|
||||
description: OpenClaw agent identifier (main, engineering, etc.)
|
||||
model:
|
||||
type: string
|
||||
thinking:
|
||||
type: string
|
||||
agentConfigRef:
|
||||
type: string
|
||||
description: Reference to an AgentConfig resource
|
||||
workspace:
|
||||
type: object
|
||||
properties:
|
||||
configMapRef:
|
||||
type: string
|
||||
secretRefs:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pvcName:
|
||||
type: string
|
||||
providers:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
channelRefs:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
channels:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [type, botTokenSecretRef]
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
botTokenSecretRef:
|
||||
type: object
|
||||
required: [name, key]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
key:
|
||||
type: string
|
||||
guildID:
|
||||
type: string
|
||||
defaultChannel:
|
||||
type: string
|
||||
defaultTemplate:
|
||||
type: string
|
||||
allowedTemplates:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
allowedDataSources:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
allowedDestinations:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
compliancePolicyRefs:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
resources:
|
||||
type: object
|
||||
properties:
|
||||
requests:
|
||||
type: object
|
||||
properties:
|
||||
cpu:
|
||||
type: string
|
||||
memory:
|
||||
type: string
|
||||
limits:
|
||||
type: object
|
||||
properties:
|
||||
cpu:
|
||||
type: string
|
||||
memory:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
replicas:
|
||||
type: integer
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
ready:
|
||||
type: boolean
|
||||
phase:
|
||||
type: string
|
||||
deployments:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
lastTransition:
|
||||
type: string
|
||||
format: date-time
|
||||
75
charts/kubeclaw/crds/kubeclaw.scoble.me_channels.yaml
Normal file
75
charts/kubeclaw/crds/kubeclaw.scoble.me_channels.yaml
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: channels.kubeclaw.scoble.me
|
||||
labels:
|
||||
app.kubernetes.io/name: kubeclaw
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
spec:
|
||||
group: kubeclaw.scoble.me
|
||||
names:
|
||||
kind: Channel
|
||||
listKind: ChannelList
|
||||
plural: channels
|
||||
singular: channel
|
||||
shortNames:
|
||||
- ch
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: Type
|
||||
type: string
|
||||
jsonPath: .spec.type
|
||||
- name: Ready
|
||||
type: boolean
|
||||
jsonPath: .status.ready
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: [type, botTokenSecretRef]
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
description: Channel type (discord, slack, telegram)
|
||||
botTokenSecretRef:
|
||||
type: object
|
||||
required: [name, key]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
key:
|
||||
type: string
|
||||
webhookURL:
|
||||
type: string
|
||||
guildID:
|
||||
type: string
|
||||
defaultChannel:
|
||||
type: string
|
||||
allowedChannels:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
ready:
|
||||
type: boolean
|
||||
connected:
|
||||
type: boolean
|
||||
condition:
|
||||
type: string
|
||||
lastConnected:
|
||||
type: string
|
||||
format: date-time
|
||||
106
charts/kubeclaw/crds/kubeclaw.scoble.me_compliancepolicies.yaml
Normal file
106
charts/kubeclaw/crds/kubeclaw.scoble.me_compliancepolicies.yaml
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: compliancepolicies.kubeclaw.scoble.me
|
||||
labels:
|
||||
app.kubernetes.io/name: kubeclaw
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
spec:
|
||||
group: kubeclaw.scoble.me
|
||||
names:
|
||||
kind: CompliancePolicy
|
||||
listKind: CompliancePolicyList
|
||||
plural: compliancepolicies
|
||||
singular: compliancepolicy
|
||||
shortNames:
|
||||
- cp
|
||||
- compol
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: Framework
|
||||
type: string
|
||||
jsonPath: .spec.framework
|
||||
- name: Escalation
|
||||
type: string
|
||||
jsonPath: .spec.escalation.mode
|
||||
- name: Ready
|
||||
type: boolean
|
||||
jsonPath: .status.ready
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: [displayName, framework, rules, escalation]
|
||||
properties:
|
||||
displayName:
|
||||
type: string
|
||||
framework:
|
||||
type: string
|
||||
rules:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [name, description, severity, constraint]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
severity:
|
||||
type: string
|
||||
enum: [block, warn, log]
|
||||
constraint:
|
||||
type: string
|
||||
escalation:
|
||||
type: object
|
||||
required: [mode]
|
||||
properties:
|
||||
mode:
|
||||
type: string
|
||||
enum: [auto_deny, human_review, warn_only]
|
||||
humanChannelRef:
|
||||
type: string
|
||||
timeoutSeconds:
|
||||
type: integer
|
||||
targetSelector:
|
||||
type: object
|
||||
properties:
|
||||
matchTemplates:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
matchNamespaces:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
matchLabels:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
prompt:
|
||||
type: string
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
ready:
|
||||
type: boolean
|
||||
activeTemplates:
|
||||
type: integer
|
||||
totalJudgments:
|
||||
type: integer
|
||||
format: int64
|
||||
lastJudgment:
|
||||
type: string
|
||||
format: date-time
|
||||
86
charts/kubeclaw/crds/kubeclaw.scoble.me_providers.yaml
Normal file
86
charts/kubeclaw/crds/kubeclaw.scoble.me_providers.yaml
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: providers.kubeclaw.scoble.me
|
||||
labels:
|
||||
app.kubernetes.io/name: kubeclaw
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
spec:
|
||||
group: kubeclaw.scoble.me
|
||||
names:
|
||||
kind: Provider
|
||||
listKind: ProviderList
|
||||
plural: providers
|
||||
singular: provider
|
||||
shortNames:
|
||||
- prov
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: Type
|
||||
type: string
|
||||
jsonPath: .spec.type
|
||||
- name: Ready
|
||||
type: boolean
|
||||
jsonPath: .status.ready
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: [type]
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
description: Provider type (ollama, openai, anthropic, deepseek)
|
||||
baseUrl:
|
||||
type: string
|
||||
description: Provider API endpoint URL
|
||||
apiKeySecretRef:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
key:
|
||||
type: string
|
||||
models:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [id]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
default:
|
||||
type: boolean
|
||||
cost:
|
||||
type: object
|
||||
properties:
|
||||
input:
|
||||
type: number
|
||||
output:
|
||||
type: number
|
||||
extraHeaders:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
ready:
|
||||
type: boolean
|
||||
condition:
|
||||
type: string
|
||||
lastChecked:
|
||||
type: string
|
||||
format: date-time
|
||||
106
charts/kubeclaw/crds/kubeclaw.scoble.me_workflowtemplates.yaml
Normal file
106
charts/kubeclaw/crds/kubeclaw.scoble.me_workflowtemplates.yaml
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: workflowtemplates.kubeclaw.scoble.me
|
||||
labels:
|
||||
app.kubernetes.io/name: kubeclaw
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
spec:
|
||||
group: kubeclaw.scoble.me
|
||||
names:
|
||||
kind: WorkflowTemplate
|
||||
listKind: WorkflowTemplateList
|
||||
plural: workflowtemplates
|
||||
singular: workflowtemplate
|
||||
shortNames:
|
||||
- wt
|
||||
- wft
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: Audit
|
||||
type: string
|
||||
jsonPath: .spec.auditLevel
|
||||
- name: Steps
|
||||
type: integer
|
||||
jsonPath: .spec.steps
|
||||
- name: Ready
|
||||
type: boolean
|
||||
jsonPath: .status.ready
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: [displayName, description, steps, parametersSchema, outputSchema, auditLevel]
|
||||
properties:
|
||||
displayName:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
steps:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [name, action, target]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
action:
|
||||
type: string
|
||||
target:
|
||||
type: string
|
||||
condition:
|
||||
type: string
|
||||
onFailure:
|
||||
type: string
|
||||
enum: [stop, skip, continue]
|
||||
parametersSchema:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
outputSchema:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
complianceRequirements:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
requiresApproval:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
allowedDataSources:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
allowedDestinations:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
auditLevel:
|
||||
type: string
|
||||
enum: [minimal, standard, full]
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
ready:
|
||||
type: boolean
|
||||
validated:
|
||||
type: boolean
|
||||
usedByAgents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
lastInstantiated:
|
||||
type: string
|
||||
format: date-time
|
||||
39
charts/kubeclaw/templates/_helpers.tpl
Normal file
39
charts/kubeclaw/templates/_helpers.tpl
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{{- define "kubeclaw.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "kubeclaw.fullname" -}}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "kubeclaw.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "kubeclaw.labels" -}}
|
||||
helm.sh/chart: {{ include "kubeclaw.chart" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{ include "kubeclaw.selectorLabels" . }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "kubeclaw.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "kubeclaw.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
{{- end }}
|
||||
|
||||
{{- define "kubeclaw.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "kubeclaw.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
35
charts/kubeclaw/templates/deployment.yaml
Normal file
35
charts/kubeclaw/templates/deployment.yaml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "kubeclaw.fullname" . }}
|
||||
labels: {{- include "kubeclaw.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels: {{- include "kubeclaw.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels: {{- include "kubeclaw.labels" . | nindent 8 }}
|
||||
spec:
|
||||
serviceAccountName: {{ include "kubeclaw.serviceAccountName" . }}
|
||||
containers:
|
||||
- name: manager
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
args:
|
||||
- --health-probe-bind-address=:8081
|
||||
- --metrics-bind-address=:8080
|
||||
ports:
|
||||
- name: metrics
|
||||
containerPort: 8080
|
||||
- name: health
|
||||
containerPort: 8081
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: health
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /readyz
|
||||
port: health
|
||||
resources: {{- toYaml .Values.resources | nindent 12 }}
|
||||
40
charts/kubeclaw/templates/rbac.yaml
Normal file
40
charts/kubeclaw/templates/rbac.yaml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
{{- if .Values.rbac.create }}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: {{ include "kubeclaw.fullname" . }}
|
||||
labels: {{- include "kubeclaw.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups: ["kubeclaw.scoble.me"]
|
||||
resources: ["agents", "agentconfigs", "agentgroups", "channels", "providers"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
- apiGroups: ["kubeclaw.scoble.me"]
|
||||
resources: ["workflowtemplates", "compliancepolicies"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
- apiGroups: ["kubeclaw.scoble.me"]
|
||||
resources: ["agents/status", "agentconfigs/status", "agentgroups/status", "channels/status", "providers/status", "workflowtemplates/status", "compliancepolicies/status"]
|
||||
verbs: ["get", "update", "patch"]
|
||||
- apiGroups: ["kubeclaw.scoble.me"]
|
||||
resources: ["agents/finalizers"]
|
||||
verbs: ["update"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "configmaps", "serviceaccounts"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ include "kubeclaw.fullname" . }}
|
||||
labels: {{- include "kubeclaw.labels" . | nindent 4 }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ include "kubeclaw.fullname" . }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "kubeclaw.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
5
charts/kubeclaw/templates/serviceaccount.yaml
Normal file
5
charts/kubeclaw/templates/serviceaccount.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "kubeclaw.serviceAccountName" . }}
|
||||
labels: {{- include "kubeclaw.labels" . | nindent 4 }}
|
||||
31
charts/kubeclaw/values.yaml
Normal file
31
charts/kubeclaw/values.yaml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# kubeclaw Helm chart values
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: ghcr.io/sscoble/kubeclaw
|
||||
pullPolicy: IfNotPresent
|
||||
tag: latest
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
serviceAccount:
|
||||
create: true
|
||||
name: kubeclaw-controller
|
||||
|
||||
rbac:
|
||||
create: true
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
101
cmd/manager/main.go
Normal file
101
cmd/manager/main.go
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
Copyright 2025 Scott Scoble.
|
||||
Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
|
||||
kubeclawv1alpha1 "github.com/sscoble/kubeclaw/api/v1alpha1"
|
||||
"github.com/sscoble/kubeclaw/internal/controller"
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = runtime.NewScheme()
|
||||
setupLog = ctrl.Log.WithName("setup")
|
||||
)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||
utilruntime.Must(kubeclawv1alpha1.AddToScheme(scheme))
|
||||
}
|
||||
|
||||
func main() {
|
||||
var metricsAddr string
|
||||
var enableLeaderElection bool
|
||||
var probeAddr string
|
||||
|
||||
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
|
||||
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
|
||||
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
|
||||
"Enable leader election for controller manager.")
|
||||
opts := zap.Options{Development: true}
|
||||
opts.BindFlags(flag.CommandLine)
|
||||
flag.Parse()
|
||||
|
||||
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
|
||||
|
||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||
Scheme: scheme,
|
||||
Metrics: ctrl.Options{}.Metrics,
|
||||
HealthProbeBindAddress: probeAddr,
|
||||
LeaderElection: enableLeaderElection,
|
||||
LeaderElectionID: "kubeclaw.scoble.me",
|
||||
})
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to start manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Set up Agent reconciler
|
||||
if err = (&controller.AgentReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Agent")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Set up WorkflowTemplate reconciler
|
||||
if err = (&controller.WorkflowTemplateReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "WorkflowTemplate")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Set up CompliancePolicy reconciler
|
||||
if err = (&controller.CompliancePolicyReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "CompliancePolicy")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up health check")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up ready check")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
setupLog.Info("starting manager")
|
||||
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
|
||||
setupLog.Error(err, "problem running manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
110
config/crd/bases/kubeclaw.scoble.me_agentconfigs.yaml
Normal file
110
config/crd/bases/kubeclaw.scoble.me_agentconfigs.yaml
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: agentconfigs.kubeclaw.scoble.me
|
||||
labels:
|
||||
app.kubernetes.io/name: kubeclaw
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
spec:
|
||||
group: kubeclaw.scoble.me
|
||||
names:
|
||||
kind: AgentConfig
|
||||
listKind: AgentConfigList
|
||||
plural: agentconfigs
|
||||
singular: agentconfig
|
||||
shortNames:
|
||||
- acfg
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
workspace:
|
||||
type: object
|
||||
properties:
|
||||
repo:
|
||||
type: string
|
||||
branch:
|
||||
type: string
|
||||
memory:
|
||||
type: object
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
autoCommit:
|
||||
type: boolean
|
||||
heartbeat:
|
||||
type: object
|
||||
properties:
|
||||
intervalSeconds:
|
||||
type: integer
|
||||
prompt:
|
||||
type: string
|
||||
sandbox:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
allowedExec:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
thinking:
|
||||
type: string
|
||||
enum: ["off", "low", "medium", "high"]
|
||||
model:
|
||||
type: string
|
||||
fallbacks:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
toolsAllow:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
toolsDeny:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
env:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
value:
|
||||
type: string
|
||||
valueFrom:
|
||||
type: object
|
||||
properties:
|
||||
secretKeyRef:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
key:
|
||||
type: string
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
ready:
|
||||
type: boolean
|
||||
usedByAgents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
107
config/crd/bases/kubeclaw.scoble.me_agentgroups.yaml
Normal file
107
config/crd/bases/kubeclaw.scoble.me_agentgroups.yaml
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: agentgroups.kubeclaw.scoble.me
|
||||
labels:
|
||||
app.kubernetes.io/name: kubeclaw
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
spec:
|
||||
group: kubeclaw.scoble.me
|
||||
names:
|
||||
kind: AgentGroup
|
||||
listKind: AgentGroupList
|
||||
plural: agentgroups
|
||||
singular: agentgroup
|
||||
shortNames:
|
||||
- agg
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: Members
|
||||
type: integer
|
||||
jsonPath: .status.memberCount
|
||||
- name: Ready
|
||||
type: boolean
|
||||
jsonPath: .status.ready
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: [members]
|
||||
properties:
|
||||
sharedConfigRef:
|
||||
type: string
|
||||
sharedProviders:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
sharedChannels:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
members:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [name, agentId]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
agentId:
|
||||
type: string
|
||||
model:
|
||||
type: string
|
||||
thinking:
|
||||
type: string
|
||||
configOverrides:
|
||||
type: object
|
||||
properties:
|
||||
model:
|
||||
type: string
|
||||
thinking:
|
||||
type: string
|
||||
allowedTemplates:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
resources:
|
||||
type: object
|
||||
properties:
|
||||
requests:
|
||||
type: object
|
||||
properties:
|
||||
cpu:
|
||||
type: string
|
||||
memory:
|
||||
type: string
|
||||
limits:
|
||||
type: object
|
||||
properties:
|
||||
cpu:
|
||||
type: string
|
||||
memory:
|
||||
type: string
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
ready:
|
||||
type: boolean
|
||||
memberCount:
|
||||
type: integer
|
||||
memberReady:
|
||||
type: integer
|
||||
agentRefs:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
147
config/crd/bases/kubeclaw.scoble.me_agents.yaml
Normal file
147
config/crd/bases/kubeclaw.scoble.me_agents.yaml
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: agents.kubeclaw.scoble.me
|
||||
labels:
|
||||
app.kubernetes.io/name: kubeclaw
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
spec:
|
||||
group: kubeclaw.scoble.me
|
||||
names:
|
||||
kind: Agent
|
||||
listKind: AgentList
|
||||
plural: agents
|
||||
singular: agent
|
||||
shortNames:
|
||||
- ag
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: AgentID
|
||||
type: string
|
||||
jsonPath: .spec.agentId
|
||||
- name: Model
|
||||
type: string
|
||||
jsonPath: .spec.model
|
||||
- name: Ready
|
||||
type: boolean
|
||||
jsonPath: .status.ready
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: [agentId]
|
||||
properties:
|
||||
agentId:
|
||||
type: string
|
||||
description: OpenClaw agent identifier (main, engineering, etc.)
|
||||
model:
|
||||
type: string
|
||||
thinking:
|
||||
type: string
|
||||
agentConfigRef:
|
||||
type: string
|
||||
description: Reference to an AgentConfig resource
|
||||
workspace:
|
||||
type: object
|
||||
properties:
|
||||
configMapRef:
|
||||
type: string
|
||||
secretRefs:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pvcName:
|
||||
type: string
|
||||
providers:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
channelRefs:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
channels:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [type, botTokenSecretRef]
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
botTokenSecretRef:
|
||||
type: object
|
||||
required: [name, key]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
key:
|
||||
type: string
|
||||
guildID:
|
||||
type: string
|
||||
defaultChannel:
|
||||
type: string
|
||||
defaultTemplate:
|
||||
type: string
|
||||
allowedTemplates:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
allowedDataSources:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
allowedDestinations:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
compliancePolicyRefs:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
resources:
|
||||
type: object
|
||||
properties:
|
||||
requests:
|
||||
type: object
|
||||
properties:
|
||||
cpu:
|
||||
type: string
|
||||
memory:
|
||||
type: string
|
||||
limits:
|
||||
type: object
|
||||
properties:
|
||||
cpu:
|
||||
type: string
|
||||
memory:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
replicas:
|
||||
type: integer
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
ready:
|
||||
type: boolean
|
||||
phase:
|
||||
type: string
|
||||
deployments:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
lastTransition:
|
||||
type: string
|
||||
format: date-time
|
||||
75
config/crd/bases/kubeclaw.scoble.me_channels.yaml
Normal file
75
config/crd/bases/kubeclaw.scoble.me_channels.yaml
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: channels.kubeclaw.scoble.me
|
||||
labels:
|
||||
app.kubernetes.io/name: kubeclaw
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
spec:
|
||||
group: kubeclaw.scoble.me
|
||||
names:
|
||||
kind: Channel
|
||||
listKind: ChannelList
|
||||
plural: channels
|
||||
singular: channel
|
||||
shortNames:
|
||||
- ch
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: Type
|
||||
type: string
|
||||
jsonPath: .spec.type
|
||||
- name: Ready
|
||||
type: boolean
|
||||
jsonPath: .status.ready
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: [type, botTokenSecretRef]
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
description: Channel type (discord, slack, telegram)
|
||||
botTokenSecretRef:
|
||||
type: object
|
||||
required: [name, key]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
key:
|
||||
type: string
|
||||
webhookURL:
|
||||
type: string
|
||||
guildID:
|
||||
type: string
|
||||
defaultChannel:
|
||||
type: string
|
||||
allowedChannels:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
ready:
|
||||
type: boolean
|
||||
connected:
|
||||
type: boolean
|
||||
condition:
|
||||
type: string
|
||||
lastConnected:
|
||||
type: string
|
||||
format: date-time
|
||||
106
config/crd/bases/kubeclaw.scoble.me_compliancepolicies.yaml
Normal file
106
config/crd/bases/kubeclaw.scoble.me_compliancepolicies.yaml
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: compliancepolicies.kubeclaw.scoble.me
|
||||
labels:
|
||||
app.kubernetes.io/name: kubeclaw
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
spec:
|
||||
group: kubeclaw.scoble.me
|
||||
names:
|
||||
kind: CompliancePolicy
|
||||
listKind: CompliancePolicyList
|
||||
plural: compliancepolicies
|
||||
singular: compliancepolicy
|
||||
shortNames:
|
||||
- cp
|
||||
- compol
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: Framework
|
||||
type: string
|
||||
jsonPath: .spec.framework
|
||||
- name: Escalation
|
||||
type: string
|
||||
jsonPath: .spec.escalation.mode
|
||||
- name: Ready
|
||||
type: boolean
|
||||
jsonPath: .status.ready
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: [displayName, framework, rules, escalation]
|
||||
properties:
|
||||
displayName:
|
||||
type: string
|
||||
framework:
|
||||
type: string
|
||||
rules:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [name, description, severity, constraint]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
severity:
|
||||
type: string
|
||||
enum: [block, warn, log]
|
||||
constraint:
|
||||
type: string
|
||||
escalation:
|
||||
type: object
|
||||
required: [mode]
|
||||
properties:
|
||||
mode:
|
||||
type: string
|
||||
enum: [auto_deny, human_review, warn_only]
|
||||
humanChannelRef:
|
||||
type: string
|
||||
timeoutSeconds:
|
||||
type: integer
|
||||
targetSelector:
|
||||
type: object
|
||||
properties:
|
||||
matchTemplates:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
matchNamespaces:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
matchLabels:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
prompt:
|
||||
type: string
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
ready:
|
||||
type: boolean
|
||||
activeTemplates:
|
||||
type: integer
|
||||
totalJudgments:
|
||||
type: integer
|
||||
format: int64
|
||||
lastJudgment:
|
||||
type: string
|
||||
format: date-time
|
||||
86
config/crd/bases/kubeclaw.scoble.me_providers.yaml
Normal file
86
config/crd/bases/kubeclaw.scoble.me_providers.yaml
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: providers.kubeclaw.scoble.me
|
||||
labels:
|
||||
app.kubernetes.io/name: kubeclaw
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
spec:
|
||||
group: kubeclaw.scoble.me
|
||||
names:
|
||||
kind: Provider
|
||||
listKind: ProviderList
|
||||
plural: providers
|
||||
singular: provider
|
||||
shortNames:
|
||||
- prov
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: Type
|
||||
type: string
|
||||
jsonPath: .spec.type
|
||||
- name: Ready
|
||||
type: boolean
|
||||
jsonPath: .status.ready
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: [type]
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
description: Provider type (ollama, openai, anthropic, deepseek)
|
||||
baseUrl:
|
||||
type: string
|
||||
description: Provider API endpoint URL
|
||||
apiKeySecretRef:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
key:
|
||||
type: string
|
||||
models:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [id]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
default:
|
||||
type: boolean
|
||||
cost:
|
||||
type: object
|
||||
properties:
|
||||
input:
|
||||
type: number
|
||||
output:
|
||||
type: number
|
||||
extraHeaders:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
ready:
|
||||
type: boolean
|
||||
condition:
|
||||
type: string
|
||||
lastChecked:
|
||||
type: string
|
||||
format: date-time
|
||||
106
config/crd/bases/kubeclaw.scoble.me_workflowtemplates.yaml
Normal file
106
config/crd/bases/kubeclaw.scoble.me_workflowtemplates.yaml
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: workflowtemplates.kubeclaw.scoble.me
|
||||
labels:
|
||||
app.kubernetes.io/name: kubeclaw
|
||||
app.kubernetes.io/part-of: scobleclaw
|
||||
spec:
|
||||
group: kubeclaw.scoble.me
|
||||
names:
|
||||
kind: WorkflowTemplate
|
||||
listKind: WorkflowTemplateList
|
||||
plural: workflowtemplates
|
||||
singular: workflowtemplate
|
||||
shortNames:
|
||||
- wt
|
||||
- wft
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: Audit
|
||||
type: string
|
||||
jsonPath: .spec.auditLevel
|
||||
- name: Steps
|
||||
type: integer
|
||||
jsonPath: .spec.steps
|
||||
- name: Ready
|
||||
type: boolean
|
||||
jsonPath: .status.ready
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
required: [displayName, description, steps, parametersSchema, outputSchema, auditLevel]
|
||||
properties:
|
||||
displayName:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
steps:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [name, action, target]
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
action:
|
||||
type: string
|
||||
target:
|
||||
type: string
|
||||
condition:
|
||||
type: string
|
||||
onFailure:
|
||||
type: string
|
||||
enum: [stop, skip, continue]
|
||||
parametersSchema:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
outputSchema:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
complianceRequirements:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
requiresApproval:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
allowedDataSources:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
allowedDestinations:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
auditLevel:
|
||||
type: string
|
||||
enum: [minimal, standard, full]
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
ready:
|
||||
type: boolean
|
||||
validated:
|
||||
type: boolean
|
||||
usedByAgents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
lastInstantiated:
|
||||
type: string
|
||||
format: date-time
|
||||
49
config/deploy/controller.yaml
Normal file
49
config/deploy/controller.yaml
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: kubeclaw-controller
|
||||
namespace: scobleclaw
|
||||
labels:
|
||||
app: kubeclaw
|
||||
component: controller
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: kubeclaw
|
||||
component: controller
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: kubeclaw
|
||||
component: controller
|
||||
spec:
|
||||
serviceAccountName: kubeclaw-controller
|
||||
containers:
|
||||
- name: manager
|
||||
image: kubeclaw/controller:latest
|
||||
imagePullPolicy: Never
|
||||
args:
|
||||
- --health-probe-bind-address=:8081
|
||||
- --metrics-bind-address=:8080
|
||||
ports:
|
||||
- name: metrics
|
||||
containerPort: 8080
|
||||
- name: health
|
||||
containerPort: 8081
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: health
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /readyz
|
||||
port: health
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
46
config/deploy/rbac.yaml
Normal file
46
config/deploy/rbac.yaml
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: kubeclaw-controller
|
||||
namespace: scobleclaw
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kubeclaw-controller
|
||||
rules:
|
||||
- apiGroups: ["kubeclaw.scoble.me"]
|
||||
resources: ["agents", "agentconfigs", "agentgroups", "channels", "providers",
|
||||
"workflowtemplates", "compliancepolicies"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
- apiGroups: ["kubeclaw.scoble.me"]
|
||||
resources: ["agents/status", "agentconfigs/status", "agentgroups/status",
|
||||
"channels/status", "providers/status",
|
||||
"workflowtemplates/status", "compliancepolicies/status"]
|
||||
verbs: ["get", "update", "patch"]
|
||||
- apiGroups: ["kubeclaw.scoble.me"]
|
||||
resources: ["agents/finalizers"]
|
||||
verbs: ["update"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "configmaps", "serviceaccounts"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: kubeclaw-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: kubeclaw-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kubeclaw-controller
|
||||
namespace: scobleclaw
|
||||
40
config/rbac/role.yaml
Normal file
40
config/rbac/role.yaml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
{{- if .Values.rbac.create }}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: {{ include "kubeclaw.fullname" . }}
|
||||
labels: {{- include "kubeclaw.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups: ["kubeclaw.scoble.me"]
|
||||
resources: ["agents", "agentconfigs", "agentgroups", "channels", "providers"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
- apiGroups: ["kubeclaw.scoble.me"]
|
||||
resources: ["workflowtemplates", "compliancepolicies"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
- apiGroups: ["kubeclaw.scoble.me"]
|
||||
resources: ["agents/status", "agentconfigs/status", "agentgroups/status", "channels/status", "providers/status", "workflowtemplates/status", "compliancepolicies/status"]
|
||||
verbs: ["get", "update", "patch"]
|
||||
- apiGroups: ["kubeclaw.scoble.me"]
|
||||
resources: ["agents/finalizers"]
|
||||
verbs: ["update"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "configmaps", "serviceaccounts"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ include "kubeclaw.fullname" . }}
|
||||
labels: {{- include "kubeclaw.labels" . | nindent 4 }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ include "kubeclaw.fullname" . }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "kubeclaw.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
13
config/samples/01-provider.yaml
Normal file
13
config/samples/01-provider.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Example: LLM provider pointing to a local Ollama instance
|
||||
apiVersion: kubeclaw.scoble.me/v1alpha1
|
||||
kind: Provider
|
||||
metadata:
|
||||
name: ollama-cloud
|
||||
spec:
|
||||
type: ollama
|
||||
baseUrl: https://ollama.example.com
|
||||
models:
|
||||
- id: deepseek-v4-pro:cloud
|
||||
default: true
|
||||
- id: deepseek-v4-flash:cloud
|
||||
- id: qwen3-coder-next:cloud
|
||||
11
config/samples/02-channel.yaml
Normal file
11
config/samples/02-channel.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: kubeclaw.scoble.me/v1alpha1
|
||||
kind: Channel
|
||||
metadata:
|
||||
name: discord-main
|
||||
spec:
|
||||
type: discord
|
||||
botTokenSecretRef:
|
||||
name: discord-token
|
||||
key: token
|
||||
guildID: "1169414363476672584"
|
||||
defaultChannel: "1496038116128198676"
|
||||
11
config/samples/03-agentconfig.yaml
Normal file
11
config/samples/03-agentconfig.yaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
apiVersion: kubeclaw.scoble.me/v1alpha1
|
||||
kind: AgentConfig
|
||||
metadata:
|
||||
name: default-config
|
||||
spec:
|
||||
model: deepseek-v4-pro:cloud
|
||||
thinking: "off"
|
||||
heartbeat:
|
||||
intervalSeconds: 1800
|
||||
sandbox:
|
||||
enabled: false
|
||||
40
config/samples/04-workflowtemplate.yaml
Normal file
40
config/samples/04-workflowtemplate.yaml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
apiVersion: kubeclaw.scoble.me/v1alpha1
|
||||
kind: WorkflowTemplate
|
||||
metadata:
|
||||
name: search-memory
|
||||
spec:
|
||||
displayName: "Search Memory"
|
||||
description: "Search the agent's memory store for relevant past context"
|
||||
steps:
|
||||
- name: search
|
||||
action: search_memory
|
||||
target: memory_store
|
||||
- name: format-results
|
||||
action: format_output
|
||||
target: stdout
|
||||
parametersSchema:
|
||||
type: object
|
||||
properties:
|
||||
query:
|
||||
type: string
|
||||
description: "Search query"
|
||||
maxResults:
|
||||
type: integer
|
||||
default: 5
|
||||
required: ["query"]
|
||||
outputSchema:
|
||||
type: object
|
||||
properties:
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
content:
|
||||
type: string
|
||||
score:
|
||||
type: number
|
||||
auditLevel: standard
|
||||
complianceRequirements: ["SOC2"]
|
||||
18
config/samples/05-compliancepolicy.yaml
Normal file
18
config/samples/05-compliancepolicy.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: kubeclaw.scoble.me/v1alpha1
|
||||
kind: CompliancePolicy
|
||||
metadata:
|
||||
name: soc2-data-access
|
||||
spec:
|
||||
displayName: "SOC2 Data Access Policy"
|
||||
framework: SOC2
|
||||
rules:
|
||||
- name: data-source-check
|
||||
description: "Ensure data sources are in the allowed list"
|
||||
severity: block
|
||||
constraint: "data_source in allowed_sources"
|
||||
- name: audit-trail
|
||||
description: "Require full audit trail for data writes"
|
||||
severity: warn
|
||||
constraint: "audit_level >= standard"
|
||||
escalation:
|
||||
mode: auto_deny
|
||||
25
config/samples/06-agent.yaml
Normal file
25
config/samples/06-agent.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
apiVersion: kubeclaw.scoble.me/v1alpha1
|
||||
kind: Agent
|
||||
metadata:
|
||||
name: main-agent
|
||||
labels:
|
||||
team: core
|
||||
spec:
|
||||
agentId: main
|
||||
model: deepseek-v4-pro:cloud
|
||||
agentConfigRef: default-config
|
||||
providers:
|
||||
- ollama-cloud
|
||||
channelRefs:
|
||||
- discord-main
|
||||
allowedTemplates:
|
||||
- search-memory
|
||||
allowedDataSources:
|
||||
- memory_store
|
||||
- vault
|
||||
allowedDestinations:
|
||||
- stdout
|
||||
- discord
|
||||
compliancePolicyRefs:
|
||||
- soc2-data-access
|
||||
replicas: 1
|
||||
20
config/samples/07-agentgroup.yaml
Normal file
20
config/samples/07-agentgroup.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: kubeclaw.scoble.me/v1alpha1
|
||||
kind: AgentGroup
|
||||
metadata:
|
||||
name: scobleclaw-team
|
||||
spec:
|
||||
sharedProviders:
|
||||
- ollama-cloud
|
||||
sharedChannels:
|
||||
- discord-main
|
||||
members:
|
||||
- name: cos
|
||||
agentId: main
|
||||
model: deepseek-v4-pro:cloud
|
||||
- name: engineering
|
||||
agentId: engineering
|
||||
model: qwen3-coder-next:cloud
|
||||
- name: trust
|
||||
agentId: trust
|
||||
model: deepseek-v4-flash:cloud
|
||||
thinking: low
|
||||
65
go.mod
65
go.mod
|
|
@ -1,14 +1,67 @@
|
|||
module github.com/sscoble/kubeclaw
|
||||
|
||||
go 1.24
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/onsi/ginkgo/v2 v2.23.3
|
||||
github.com/onsi/gomega v1.37.0
|
||||
k8s.io/api v0.33.0
|
||||
k8s.io/apiextensions-apiserver v0.33.0
|
||||
k8s.io/apimachinery v0.33.0
|
||||
k8s.io/client-go v0.33.0
|
||||
sigs.k8s.io/controller-runtime v0.20.4
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/zapr v1.3.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.23.3 // indirect
|
||||
github.com/onsi/gomega v1.37.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/oauth2 v0.27.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.33.0 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
|
|
|||
196
go.sum
Normal file
196
go.sum
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k=
|
||||
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
|
||||
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
|
||||
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0=
|
||||
github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
||||
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
|
||||
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU=
|
||||
k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM=
|
||||
k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs=
|
||||
k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc=
|
||||
k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ=
|
||||
k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98=
|
||||
k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU=
|
||||
sigs.k8s.io/controller-runtime v0.20.4/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
4
hack/boilerplate.go.txt
Normal file
4
hack/boilerplate.go.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
/*
|
||||
Copyright 2025 Scott Scoble.
|
||||
Licensed under the MIT License.
|
||||
*/
|
||||
190
internal/controller/agent_controller.go
Normal file
190
internal/controller/agent_controller.go
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
Copyright 2025 Scott Scoble. Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
kubeclawv1alpha1 "github.com/sscoble/kubeclaw/api/v1alpha1"
|
||||
)
|
||||
|
||||
// AgentReconciler reconciles Agent objects.
|
||||
type AgentReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
func (r *AgentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
logger := log.FromContext(ctx)
|
||||
logger.Info("reconciling Agent", "name", req.Name, "namespace", req.Namespace)
|
||||
|
||||
agent := &kubeclawv1alpha1.Agent{}
|
||||
if err := r.Get(ctx, req.NamespacedName, agent); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
// Handle deletion
|
||||
if !agent.DeletionTimestamp.IsZero() {
|
||||
if controllerutil.ContainsFinalizer(agent, "kubeclaw.scoble.me/finalizer") {
|
||||
podName := fmt.Sprintf("kubeclaw-agent-%s", agent.Spec.AgentID)
|
||||
pod := &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: podName, Namespace: req.Namespace}}
|
||||
if err := r.Delete(ctx, pod); err != nil && !errors.IsNotFound(err) {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
controllerutil.RemoveFinalizer(agent, "kubeclaw.scoble.me/finalizer")
|
||||
if err := r.Update(ctx, agent); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// Ensure finalizer
|
||||
if !controllerutil.ContainsFinalizer(agent, "kubeclaw.scoble.me/finalizer") {
|
||||
controllerutil.AddFinalizer(agent, "kubeclaw.scoble.me/finalizer")
|
||||
if err := r.Update(ctx, agent); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
return ctrl.Result{Requeue: true}, nil
|
||||
}
|
||||
|
||||
// Resolve AgentConfig
|
||||
if agent.Spec.AgentConfigRef != "" {
|
||||
acfg := &kubeclawv1alpha1.AgentConfig{}
|
||||
if err := r.Get(ctx, types.NamespacedName{Name: agent.Spec.AgentConfigRef, Namespace: req.Namespace}, acfg); err != nil {
|
||||
logger.Error(err, "cannot fetch AgentConfig", "ref", agent.Spec.AgentConfigRef)
|
||||
agent.Status.Phase = "ConfigError"
|
||||
_ = r.Status().Update(ctx, agent)
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
if agent.Spec.Model == "" && acfg.Spec.Model != "" {
|
||||
agent.Spec.Model = acfg.Spec.Model
|
||||
}
|
||||
if agent.Spec.Thinking == "" && acfg.Spec.Thinking != "" {
|
||||
agent.Spec.Thinking = acfg.Spec.Thinking
|
||||
}
|
||||
}
|
||||
|
||||
image := agent.Spec.Image
|
||||
if image == "" {
|
||||
image = "ghcr.io/sscoble/kubeclaw-agent:latest"
|
||||
}
|
||||
|
||||
podName := fmt.Sprintf("kubeclaw-agent-%s", agent.Spec.AgentID)
|
||||
|
||||
pod := &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: podName,
|
||||
Namespace: req.Namespace,
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/name": "kubeclaw-agent",
|
||||
"app.kubernetes.io/instance": podName,
|
||||
"app.kubernetes.io/part-of": "scobleclaw",
|
||||
"kubeclaw.scoble.me/agent-id": agent.Spec.AgentID,
|
||||
},
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{{
|
||||
Name: "agent",
|
||||
Image: image,
|
||||
Env: r.buildEnv(agent),
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("100m"),
|
||||
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("500m"),
|
||||
corev1.ResourceMemory: resource.MustParse("256Mi"),
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
// Apply resource overrides
|
||||
if agent.Spec.Resources != nil {
|
||||
if cpu, ok := agent.Spec.Resources.Requests["cpu"]; ok {
|
||||
if q, err := resource.ParseQuantity(cpu); err == nil {
|
||||
pod.Spec.Containers[0].Resources.Requests[corev1.ResourceCPU] = q
|
||||
}
|
||||
}
|
||||
if mem, ok := agent.Spec.Resources.Requests["memory"]; ok {
|
||||
if q, err := resource.ParseQuantity(mem); err == nil {
|
||||
pod.Spec.Containers[0].Resources.Requests[corev1.ResourceMemory] = q
|
||||
}
|
||||
}
|
||||
if cpu, ok := agent.Spec.Resources.Limits["cpu"]; ok {
|
||||
if q, err := resource.ParseQuantity(cpu); err == nil {
|
||||
pod.Spec.Containers[0].Resources.Limits[corev1.ResourceCPU] = q
|
||||
}
|
||||
}
|
||||
if mem, ok := agent.Spec.Resources.Limits["memory"]; ok {
|
||||
if q, err := resource.ParseQuantity(mem); err == nil {
|
||||
pod.Spec.Containers[0].Resources.Limits[corev1.ResourceMemory] = q
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create or update
|
||||
foundPod := &corev1.Pod{}
|
||||
err := r.Get(ctx, types.NamespacedName{Name: podName, Namespace: req.Namespace}, foundPod)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
logger.Info("creating Pod", "name", podName)
|
||||
if err := r.Create(ctx, pod); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
} else if err == nil {
|
||||
foundPod.Spec = pod.Spec
|
||||
foundPod.Labels = pod.Labels
|
||||
if err := r.Update(ctx, foundPod); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
agent.Status.Phase = "Running"
|
||||
agent.Status.ReadyReplicas = 1
|
||||
if err := r.Status().Update(ctx, agent); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *AgentReconciler) buildEnv(agent *kubeclawv1alpha1.Agent) []corev1.EnvVar {
|
||||
env := []corev1.EnvVar{{Name: "KUBECLAW_AGENT_ID", Value: agent.Spec.AgentID}}
|
||||
if agent.Spec.Model != "" {
|
||||
env = append(env, corev1.EnvVar{Name: "KUBECLAW_MODEL", Value: agent.Spec.Model})
|
||||
}
|
||||
if agent.Spec.Thinking != "" {
|
||||
env = append(env, corev1.EnvVar{Name: "KUBECLAW_THINKING", Value: agent.Spec.Thinking})
|
||||
}
|
||||
if agent.Spec.DefaultTemplate != "" {
|
||||
env = append(env, corev1.EnvVar{Name: "KUBECLAW_DEFAULT_TEMPLATE", Value: agent.Spec.DefaultTemplate})
|
||||
}
|
||||
if len(agent.Spec.AllowedTemplates) > 0 {
|
||||
env = append(env, corev1.EnvVar{Name: "KUBECLAW_ALLOWED_TEMPLATES", Value: joinSlice(agent.Spec.AllowedTemplates, ",")})
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
func (r *AgentReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&kubeclawv1alpha1.Agent{}).
|
||||
Owns(&corev1.Pod{}).
|
||||
Complete(r)
|
||||
}
|
||||
52
internal/controller/compliancepolicy_controller.go
Normal file
52
internal/controller/compliancepolicy_controller.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
Copyright 2025 Scott Scoble. Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
kubeclawv1alpha1 "github.com/sscoble/kubeclaw/api/v1alpha1"
|
||||
)
|
||||
|
||||
// CompliancePolicyReconciler reconciles CompliancePolicy objects.
|
||||
type CompliancePolicyReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
func (r *CompliancePolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
cp := &kubeclawv1alpha1.CompliancePolicy{}
|
||||
if err := r.Get(ctx, req.NamespacedName, cp); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
logger.Info("validating compliance policy", "name", cp.Name)
|
||||
|
||||
valid := true
|
||||
if len(cp.Spec.Rules) == 0 {
|
||||
valid = false
|
||||
}
|
||||
|
||||
cp.Status.Ready = valid
|
||||
|
||||
if err := r.Status().Update(ctx, cp); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *CompliancePolicyReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&kubeclawv1alpha1.CompliancePolicy{}).
|
||||
Complete(r)
|
||||
}
|
||||
12
internal/controller/helpers.go
Normal file
12
internal/controller/helpers.go
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package controller
|
||||
|
||||
func joinSlice(items []string, sep string) string {
|
||||
result := ""
|
||||
for i, item := range items {
|
||||
if i > 0 {
|
||||
result += sep
|
||||
}
|
||||
result += item
|
||||
}
|
||||
return result
|
||||
}
|
||||
57
internal/controller/workflowtemplate_controller.go
Normal file
57
internal/controller/workflowtemplate_controller.go
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
Copyright 2025 Scott Scoble. Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
kubeclawv1alpha1 "github.com/sscoble/kubeclaw/api/v1alpha1"
|
||||
)
|
||||
|
||||
// WorkflowTemplateReconciler reconciles WorkflowTemplate objects.
|
||||
type WorkflowTemplateReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
func (r *WorkflowTemplateReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
wt := &kubeclawv1alpha1.WorkflowTemplate{}
|
||||
if err := r.Get(ctx, req.NamespacedName, wt); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
logger.Info("validating workflow template", "name", wt.Name)
|
||||
|
||||
// Validate basic shape
|
||||
valid := true
|
||||
if len(wt.Spec.Steps) == 0 {
|
||||
valid = false
|
||||
}
|
||||
if wt.Spec.AuditLevel == "" {
|
||||
wt.Spec.AuditLevel = "standard"
|
||||
}
|
||||
|
||||
wt.Status.Ready = valid
|
||||
wt.Status.Validated = true
|
||||
|
||||
if err := r.Status().Update(ctx, wt); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *WorkflowTemplateReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&kubeclawv1alpha1.WorkflowTemplate{}).
|
||||
Complete(r)
|
||||
}
|
||||
BIN
manager
Executable file
BIN
manager
Executable file
Binary file not shown.
35
nix/build-image.nix
Normal file
35
nix/build-image.nix
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# build-image.nix — Package the pre-built kubeclaw controller binary as an OCI image
|
||||
# Usage: nix-build build-image.nix && result | k3s ctr images import -
|
||||
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
let
|
||||
manager-bin = pkgs.stdenv.mkDerivation {
|
||||
name = "kubeclaw-manager";
|
||||
src = ./../manager;
|
||||
phases = [ "installPhase" ];
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
cp $src $out/bin/manager
|
||||
chmod +x $out/bin/manager
|
||||
'';
|
||||
};
|
||||
|
||||
in pkgs.dockerTools.streamLayeredImage {
|
||||
name = "kubeclaw/controller";
|
||||
tag = "latest";
|
||||
created = "now";
|
||||
contents = [
|
||||
manager-bin
|
||||
pkgs.cacert
|
||||
pkgs.tzdata
|
||||
];
|
||||
config = {
|
||||
User = "65532:65532";
|
||||
Entrypoint = [ "/bin/manager" ];
|
||||
ExposedPorts = {
|
||||
"8080/tcp" = {};
|
||||
"8081/tcp" = {};
|
||||
};
|
||||
};
|
||||
}
|
||||
1
nix/result
Symbolic link
1
nix/result
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
/nix/store/6xkkvlii8k7jj9i2zj0bkw5xj6dbmz81-stream-controller
|
||||
170
quickstart.sh
Executable file
170
quickstart.sh
Executable file
|
|
@ -0,0 +1,170 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# kubeclaw quickstart: install CRDs + deploy controller on Docker Desktop Kubernetes
|
||||
# Prerequisites: Docker Desktop with Kubernetes enabled, kubectl pointing to docker-desktop
|
||||
|
||||
echo "=== kubeclaw Quickstart ==="
|
||||
echo ""
|
||||
|
||||
# 1. Verify cluster access
|
||||
echo "→ Checking cluster connection..."
|
||||
kubectl cluster-info --request-timeout=5s > /dev/null 2>&1 || {
|
||||
echo "ERROR: Cannot reach Kubernetes cluster."
|
||||
echo "Make sure Docker Desktop has Kubernetes enabled (Settings → Kubernetes → Enable Kubernetes)"
|
||||
exit 1
|
||||
}
|
||||
echo " ✓ Cluster reachable ($(kubectl config current-context))"
|
||||
|
||||
# 2. Install CRDs
|
||||
echo ""
|
||||
echo "→ Installing CRDs..."
|
||||
for crd in config/crd/bases/*.yaml; do
|
||||
kubectl apply -f "$crd"
|
||||
done
|
||||
echo " ✓ CRDs installed"
|
||||
|
||||
# Wait for CRDs to register
|
||||
sleep 2
|
||||
|
||||
# 3. Verify CRDs are ready
|
||||
echo ""
|
||||
echo "→ Verifying CRDs..."
|
||||
kubectl get crd | grep kubeclaw.scoble.me | awk '{print " " $1}'
|
||||
|
||||
# 4. Create namespace
|
||||
echo ""
|
||||
echo "→ Creating scobleclaw namespace..."
|
||||
kubectl create namespace scobleclaw --dry-run=client -o yaml | kubectl apply -f -
|
||||
echo " ✓ Namespace ready"
|
||||
|
||||
# 5. Deploy controller
|
||||
echo ""
|
||||
echo "→ Deploying kubeclaw controller..."
|
||||
kubectl apply -f config/rbac/role.yaml 2>/dev/null || {
|
||||
# Generate minimal RBAC if role.yaml doesn't exist
|
||||
kubectl apply -f - <<'RBAC'
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: kubeclaw-controller
|
||||
namespace: scobleclaw
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kubeclaw-controller
|
||||
rules:
|
||||
- apiGroups: ["kubeclaw.scoble.me"]
|
||||
resources: ["*"]
|
||||
verbs: ["*"]
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "configmaps", "serviceaccounts"]
|
||||
verbs: ["*"]
|
||||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: ["apps"]
|
||||
resources: ["deployments"]
|
||||
verbs: ["*"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: kubeclaw-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: kubeclaw-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kubeclaw-controller
|
||||
namespace: scobleclaw
|
||||
RBAC
|
||||
}
|
||||
|
||||
kubectl apply -f - <<'DEPLOYMENT'
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: kubeclaw-config
|
||||
namespace: scobleclaw
|
||||
data:
|
||||
config.yaml: |
|
||||
logLevel: info
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: kubeclaw-controller
|
||||
namespace: scobleclaw
|
||||
labels:
|
||||
app: kubeclaw
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: kubeclaw
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: kubeclaw
|
||||
spec:
|
||||
serviceAccountName: kubeclaw-controller
|
||||
containers:
|
||||
- name: manager
|
||||
image: ghcr.io/sscoble/kubeclaw:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["/manager"]
|
||||
args:
|
||||
- --health-probe-bind-address=:8081
|
||||
- --metrics-bind-address=:8080
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: metrics
|
||||
- containerPort: 8081
|
||||
name: health
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: health
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /readyz
|
||||
port: health
|
||||
DEPLOYMENT
|
||||
|
||||
echo " ✓ Controller deployed"
|
||||
|
||||
# 6. Wait for controller
|
||||
echo ""
|
||||
echo "→ Waiting for controller to start..."
|
||||
kubectl wait --for=condition=available deployment/kubeclaw-controller -n scobleclaw --timeout=60s 2>/dev/null || {
|
||||
echo " (controller may still be starting — check with 'kubectl get pods -n scobleclaw')"
|
||||
}
|
||||
|
||||
# 7. Apply samples
|
||||
echo ""
|
||||
echo "→ Installing sample resources..."
|
||||
for sample in config/samples/0*.yaml; do
|
||||
echo " Applying $sample..."
|
||||
kubectl apply -f "$sample" -n scobleclaw 2>/dev/null || kubectl apply -f "$sample"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=== Quickstart Complete ==="
|
||||
echo ""
|
||||
echo "Check status:"
|
||||
echo " kubectl get agents -n scobleclaw"
|
||||
echo " kubectl get workflowtemplates"
|
||||
echo " kubectl get compliancepolicies"
|
||||
echo ""
|
||||
echo "View controller logs:"
|
||||
echo " kubectl logs -n scobleclaw deployment/kubeclaw-controller"
|
||||
Loading…
Add table
Add a link
Reference in a new issue