mirror of
https://github.com/cozystack/cozystack.git
synced 2026-03-03 21:48:57 +00:00
Compare commits
75 Commits
linstor-af
...
release-0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
074725f69c | ||
|
|
ea0abcc465 | ||
|
|
cf96f19a70 | ||
|
|
274a3a57a8 | ||
|
|
5d56029be6 | ||
|
|
8e9b398a21 | ||
|
|
93339820f8 | ||
|
|
909997da1d | ||
|
|
98cab21ab8 | ||
|
|
ce6ec2c0bb | ||
|
|
d90882c4af | ||
|
|
cbe36e4ccf | ||
|
|
ee600b0ce6 | ||
|
|
2282afcd6a | ||
|
|
8298763562 | ||
|
|
3028dc9f1a | ||
|
|
3ab4707c6a | ||
|
|
17966bfba1 | ||
|
|
a9de6bf07e | ||
|
|
765cffdc66 | ||
|
|
48ebe418b2 | ||
|
|
25f31022f0 | ||
|
|
5be383610f | ||
|
|
363c8cf066 | ||
|
|
8c8ab1f1a0 | ||
|
|
9405e05185 | ||
|
|
0aee5b4dfa | ||
|
|
8373a8cb3f | ||
|
|
cb0e457dda | ||
|
|
c74b88ce66 | ||
|
|
07982b2646 | ||
|
|
d80ea29dfa | ||
|
|
143da399ff | ||
|
|
ad3bff6692 | ||
|
|
f1852ca841 | ||
|
|
5914515371 | ||
|
|
5be4fc8f40 | ||
|
|
f4ff44a18c | ||
|
|
6b5e2e3e77 | ||
|
|
fc84b80b70 | ||
|
|
85a0003ffb | ||
|
|
7c766fc27d | ||
|
|
46d81c806f | ||
|
|
9300309dfb | ||
|
|
d90a76bc25 | ||
|
|
13bfb72ea5 | ||
|
|
56ed29c7fc | ||
|
|
1ffa1ef454 | ||
|
|
1826858bb0 | ||
|
|
0e262838d2 | ||
|
|
b610630592 | ||
|
|
357d9f95be | ||
|
|
37a2e38798 | ||
|
|
da44a65ba9 | ||
|
|
26da347de8 | ||
|
|
dd2798dbda | ||
|
|
fb5b4da2b2 | ||
|
|
540e1c6e0d | ||
|
|
1dd8e00e17 | ||
|
|
79165ca2f9 | ||
|
|
a8bd179f0d | ||
|
|
1491535b35 | ||
|
|
290c6be04b | ||
|
|
03328dc4e4 | ||
|
|
9311a9e547 | ||
|
|
548b2c0ed3 | ||
|
|
202ff3433e | ||
|
|
891195018f | ||
|
|
d53861837f | ||
|
|
f1a75ab864 | ||
|
|
2110534e63 | ||
|
|
2e22a6579e | ||
|
|
35907dd474 | ||
|
|
af56c105c2 | ||
|
|
30e5b71e3f |
2
Makefile
2
Makefile
@@ -17,7 +17,7 @@ build: build-deps
|
||||
make -C packages/system/cozystack-controller image
|
||||
make -C packages/system/lineage-controller-webhook image
|
||||
make -C packages/system/cilium image
|
||||
make -C packages/system/kubeovn image
|
||||
make -C packages/system/linstor image
|
||||
make -C packages/system/kubeovn-webhook image
|
||||
make -C packages/system/kubeovn-plunger image
|
||||
make -C packages/system/dashboard image
|
||||
|
||||
11
go.mod
11
go.mod
@@ -6,11 +6,15 @@ go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/fluxcd/helm-controller/api v1.1.0
|
||||
github.com/go-logr/logr v1.4.2
|
||||
github.com/go-logr/zapr v1.3.0
|
||||
github.com/google/gofuzz v1.2.0
|
||||
github.com/onsi/ginkgo/v2 v2.19.0
|
||||
github.com/onsi/gomega v1.33.1
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
go.uber.org/zap v1.27.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
k8s.io/api v0.31.2
|
||||
k8s.io/apiextensions-apiserver v0.31.2
|
||||
@@ -44,9 +48,7 @@ require (
|
||||
github.com/fluxcd/pkg/apis/meta v1.6.1 // 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/stdr v1.2.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
|
||||
@@ -74,7 +76,6 @@ require (
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.19.1 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
@@ -94,7 +95,6 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
@@ -119,3 +119,6 @@ require (
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
// See: issues.k8s.io/135537
|
||||
replace k8s.io/apimachinery => github.com/cozystack/apimachinery v0.0.0-20251201201312-18e522a87614
|
||||
|
||||
4
go.sum
4
go.sum
@@ -18,6 +18,8 @@ github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr
|
||||
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cozystack/apimachinery v0.0.0-20251201201312-18e522a87614 h1:jH9elECUvhiIs3IMv3oS5k1JgCLVsSK6oU4dmq5gyW8=
|
||||
github.com/cozystack/apimachinery v0.0.0-20251201201312-18e522a87614/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
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=
|
||||
@@ -291,8 +293,6 @@ k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
|
||||
k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
|
||||
k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0=
|
||||
k8s.io/apiextensions-apiserver v0.31.2/go.mod h1:i+Geh+nGCJEGiCGR3MlBDkS7koHIIKWVfWeRFiOsUcM=
|
||||
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
|
||||
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4=
|
||||
k8s.io/apiserver v0.31.2/go.mod h1:o3nKZR7lPlJqkU5I3Ove+Zx3JuoFjQobGX1Gctw6XuE=
|
||||
k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
|
||||
|
||||
@@ -21,14 +21,33 @@
|
||||
}
|
||||
|
||||
@test "Test kinds" {
|
||||
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/tenants | jq -r '.kind')
|
||||
if [ "$val" != "TenantList" ]; then
|
||||
echo "Expected kind to be TenantList, got $val"
|
||||
exit 1
|
||||
fi
|
||||
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/tenants | jq -r '.items[0].kind')
|
||||
if [ "$val" != "Tenant" ]; then
|
||||
echo "Expected kind to be Tenant, got $val"
|
||||
exit 1
|
||||
fi
|
||||
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/ingresses | jq -r '.kind')
|
||||
if [ "$val" != "IngressList" ]; then
|
||||
echo "Expected kind to be IngressList, got $val"
|
||||
exit 1
|
||||
fi
|
||||
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/ingresses | jq -r '.items[0].kind')
|
||||
if [ "$val" != "Ingress" ]; then
|
||||
echo "Expected kind to be Ingress, got $val"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
@test "Create and delete namespace" {
|
||||
kubectl create ns cozy-test-create-and-delete-namespace --dry-run=client -o yaml | kubectl apply -f -
|
||||
if ! kubectl delete ns cozy-test-create-and-delete-namespace; then
|
||||
echo "Failed to delete namespace"
|
||||
kubectl describe ns cozy-test-create-and-delete-namespace
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -105,8 +105,26 @@ func buildMultilineStringSchema(openAPISchema string) (map[string]any, error) {
|
||||
"properties": map[string]any{},
|
||||
}
|
||||
|
||||
// Check if there's a spec property
|
||||
specProp, ok := props["spec"].(map[string]any)
|
||||
if !ok {
|
||||
return map[string]any{}, nil
|
||||
}
|
||||
|
||||
specProps, ok := specProp["properties"].(map[string]any)
|
||||
if !ok {
|
||||
return map[string]any{}, nil
|
||||
}
|
||||
|
||||
// Create spec.properties structure in schema
|
||||
schemaProps := schema["properties"].(map[string]any)
|
||||
specSchema := map[string]any{
|
||||
"properties": map[string]any{},
|
||||
}
|
||||
schemaProps["spec"] = specSchema
|
||||
|
||||
// Process spec properties recursively
|
||||
processSpecProperties(props, schema["properties"].(map[string]any))
|
||||
processSpecProperties(specProps, specSchema["properties"].(map[string]any))
|
||||
|
||||
return schema, nil
|
||||
}
|
||||
|
||||
@@ -9,41 +9,46 @@ func TestBuildMultilineStringSchema(t *testing.T) {
|
||||
// Test OpenAPI schema with various field types
|
||||
openAPISchema := `{
|
||||
"properties": {
|
||||
"simpleString": {
|
||||
"type": "string",
|
||||
"description": "A simple string field"
|
||||
},
|
||||
"stringWithEnum": {
|
||||
"type": "string",
|
||||
"enum": ["option1", "option2"],
|
||||
"description": "String with enum should be skipped"
|
||||
},
|
||||
"numberField": {
|
||||
"type": "number",
|
||||
"description": "Number field should be skipped"
|
||||
},
|
||||
"nestedObject": {
|
||||
"spec": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nestedString": {
|
||||
"simpleString": {
|
||||
"type": "string",
|
||||
"description": "Nested string should get multilineString"
|
||||
"description": "A simple string field"
|
||||
},
|
||||
"nestedStringWithEnum": {
|
||||
"stringWithEnum": {
|
||||
"type": "string",
|
||||
"enum": ["a", "b"],
|
||||
"description": "Nested string with enum should be skipped"
|
||||
}
|
||||
}
|
||||
},
|
||||
"arrayOfObjects": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"itemString": {
|
||||
"type": "string",
|
||||
"description": "String in array item"
|
||||
"enum": ["option1", "option2"],
|
||||
"description": "String with enum should be skipped"
|
||||
},
|
||||
"numberField": {
|
||||
"type": "number",
|
||||
"description": "Number field should be skipped"
|
||||
},
|
||||
"nestedObject": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nestedString": {
|
||||
"type": "string",
|
||||
"description": "Nested string should get multilineString"
|
||||
},
|
||||
"nestedStringWithEnum": {
|
||||
"type": "string",
|
||||
"enum": ["a", "b"],
|
||||
"description": "Nested string with enum should be skipped"
|
||||
}
|
||||
}
|
||||
},
|
||||
"arrayOfObjects": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"itemString": {
|
||||
"type": "string",
|
||||
"description": "String in array item"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,33 +75,44 @@ func TestBuildMultilineStringSchema(t *testing.T) {
|
||||
t.Fatal("schema.properties is not a map")
|
||||
}
|
||||
|
||||
// Check simpleString
|
||||
simpleString, ok := props["simpleString"].(map[string]any)
|
||||
// Check spec property exists
|
||||
spec, ok := props["spec"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("simpleString not found in properties")
|
||||
t.Fatal("spec not found in properties")
|
||||
}
|
||||
|
||||
specProps, ok := spec["properties"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("spec.properties is not a map")
|
||||
}
|
||||
|
||||
// Check simpleString
|
||||
simpleString, ok := specProps["simpleString"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("simpleString not found in spec.properties")
|
||||
}
|
||||
if simpleString["type"] != "multilineString" {
|
||||
t.Errorf("simpleString should have type multilineString, got %v", simpleString["type"])
|
||||
}
|
||||
|
||||
// Check stringWithEnum should not be present (or should not have multilineString)
|
||||
if stringWithEnum, ok := props["stringWithEnum"].(map[string]any); ok {
|
||||
if stringWithEnum, ok := specProps["stringWithEnum"].(map[string]any); ok {
|
||||
if stringWithEnum["type"] == "multilineString" {
|
||||
t.Error("stringWithEnum should not have multilineString type")
|
||||
}
|
||||
}
|
||||
|
||||
// Check numberField should not be present
|
||||
if numberField, ok := props["numberField"].(map[string]any); ok {
|
||||
if numberField, ok := specProps["numberField"].(map[string]any); ok {
|
||||
if numberField["type"] != nil {
|
||||
t.Error("numberField should not have any type override")
|
||||
}
|
||||
}
|
||||
|
||||
// Check nested object
|
||||
nestedObject, ok := props["nestedObject"].(map[string]any)
|
||||
nestedObject, ok := specProps["nestedObject"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("nestedObject not found in properties")
|
||||
t.Fatal("nestedObject not found in spec.properties")
|
||||
}
|
||||
nestedProps, ok := nestedObject["properties"].(map[string]any)
|
||||
if !ok {
|
||||
@@ -113,9 +129,9 @@ func TestBuildMultilineStringSchema(t *testing.T) {
|
||||
}
|
||||
|
||||
// Check array of objects
|
||||
arrayOfObjects, ok := props["arrayOfObjects"].(map[string]any)
|
||||
arrayOfObjects, ok := specProps["arrayOfObjects"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("arrayOfObjects not found in properties")
|
||||
t.Fatal("arrayOfObjects not found in spec.properties")
|
||||
}
|
||||
items, ok := arrayOfObjects["items"].(map[string]any)
|
||||
if !ok {
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:e0a07082bb6fc6aeaae2315f335386f1705a646c72f9e0af512aebbca5cb2b15
|
||||
ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:31ebc09cfa11d8b438d2bbb32fa61b133aaf4b48b1a1282c9e59b5c127af61c1
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.0.0@sha256:2d39989846c3579dd020b9f6c77e6e314cc81aa344eaac0f6d633e723c17196d
|
||||
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.0.0@sha256:6f2b1d6b0b2bdc66f1cbb30c59393369cbf070cb8f5fec748f176952273483cc
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.0.0@sha256:5335c044313b69ee13b30ca4941687e509005e55f4ae25723861edbf2fbd6dd2
|
||||
ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.0.0@sha256:dee69d15fa8616aa6a1e5a67fc76370e7698a7f58b25e30650eb39c9fb826de8
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/pkg/controller/kubevirteps/kubevirteps_controller.go b/pkg/controller/kubevirteps/kubevirteps_controller.go
|
||||
index 53388eb8e..28644236f 100644
|
||||
index 53388eb8e..873060251 100644
|
||||
--- a/pkg/controller/kubevirteps/kubevirteps_controller.go
|
||||
+++ b/pkg/controller/kubevirteps/kubevirteps_controller.go
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
@@ -10,12 +10,17 @@ index 53388eb8e..28644236f 100644
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
@@ -669,35 +668,50 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
|
||||
@@ -666,38 +665,62 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
|
||||
// for extracting the nodes it does not matter what type of address we are dealing with
|
||||
// all nodes with an endpoint for a corresponding slice will be selected.
|
||||
nodeSet := sets.Set[string]{}
|
||||
+ hasEndpointsWithoutNodeName := false
|
||||
for _, slice := range tenantSlices {
|
||||
for _, endpoint := range slice.Endpoints {
|
||||
// find all unique nodes that correspond to an endpoint in a tenant slice
|
||||
+ if endpoint.NodeName == nil {
|
||||
+ klog.Warningf("Skipping endpoint without NodeName in slice %s/%s", slice.Namespace, slice.Name)
|
||||
+ hasEndpointsWithoutNodeName = true
|
||||
+ continue
|
||||
+ }
|
||||
nodeSet.Insert(*endpoint.NodeName)
|
||||
@@ -23,6 +28,13 @@ index 53388eb8e..28644236f 100644
|
||||
}
|
||||
|
||||
- klog.Infof("Desired nodes for service %s in namespace %s: %v", service.Name, service.Namespace, sets.List(nodeSet))
|
||||
+ // Fallback: if no endpoints with NodeName were found, but there are endpoints without NodeName,
|
||||
+ // distribute traffic to all VMIs (similar to ExternalTrafficPolicy=Cluster behavior)
|
||||
+ if nodeSet.Len() == 0 && hasEndpointsWithoutNodeName {
|
||||
+ klog.Infof("No endpoints with NodeName found for service %s/%s, falling back to all VMIs", service.Namespace, service.Name)
|
||||
+ return c.getAllVMIEndpoints()
|
||||
+ }
|
||||
+
|
||||
+ klog.Infof("Desired nodes for service %s/%s: %v", service.Namespace, service.Name, sets.List(nodeSet))
|
||||
|
||||
for _, node := range sets.List(nodeSet) {
|
||||
@@ -68,7 +80,7 @@ index 53388eb8e..28644236f 100644
|
||||
desiredEndpoints = append(desiredEndpoints, &discovery.Endpoint{
|
||||
Addresses: []string{i.IP},
|
||||
Conditions: discovery.EndpointConditions{
|
||||
@@ -705,9 +719,9 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
|
||||
@@ -705,9 +728,9 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
|
||||
Serving: &serving,
|
||||
Terminating: &terminating,
|
||||
},
|
||||
@@ -80,6 +92,71 @@ index 53388eb8e..28644236f 100644
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -716,6 +739,64 @@ func (c *Controller) getDesiredEndpoints(service *v1.Service, tenantSlices []*di
|
||||
return desiredEndpoints
|
||||
}
|
||||
|
||||
+// getAllVMIEndpoints returns endpoints for all VMIs in the infra namespace.
|
||||
+// This is used as a fallback when tenant endpoints don't have NodeName specified,
|
||||
+// similar to ExternalTrafficPolicy=Cluster behavior where traffic is distributed to all nodes.
|
||||
+func (c *Controller) getAllVMIEndpoints() []*discovery.Endpoint {
|
||||
+ var endpoints []*discovery.Endpoint
|
||||
+
|
||||
+ // List all VMIs in the infra namespace
|
||||
+ vmiList, err := c.infraDynamic.
|
||||
+ Resource(kubevirtv1.VirtualMachineInstanceGroupVersionKind.GroupVersion().WithResource("virtualmachineinstances")).
|
||||
+ Namespace(c.infraNamespace).
|
||||
+ List(context.TODO(), metav1.ListOptions{})
|
||||
+ if err != nil {
|
||||
+ klog.Errorf("Failed to list VMIs in namespace %q: %v", c.infraNamespace, err)
|
||||
+ return endpoints
|
||||
+ }
|
||||
+
|
||||
+ for _, obj := range vmiList.Items {
|
||||
+ vmi := &kubevirtv1.VirtualMachineInstance{}
|
||||
+ err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, vmi)
|
||||
+ if err != nil {
|
||||
+ klog.Errorf("Failed to convert Unstructured to VirtualMachineInstance: %v", err)
|
||||
+ continue
|
||||
+ }
|
||||
+
|
||||
+ if vmi.Status.NodeName == "" {
|
||||
+ klog.Warningf("Skipping VMI %s/%s: NodeName is empty", vmi.Namespace, vmi.Name)
|
||||
+ continue
|
||||
+ }
|
||||
+ nodeNamePtr := &vmi.Status.NodeName
|
||||
+
|
||||
+ ready := vmi.Status.Phase == kubevirtv1.Running
|
||||
+ serving := vmi.Status.Phase == kubevirtv1.Running
|
||||
+ terminating := vmi.Status.Phase == kubevirtv1.Failed || vmi.Status.Phase == kubevirtv1.Succeeded
|
||||
+
|
||||
+ for _, i := range vmi.Status.Interfaces {
|
||||
+ if i.Name == "default" {
|
||||
+ if i.IP == "" {
|
||||
+ klog.Warningf("VMI %s/%s interface %q has no IP, skipping", vmi.Namespace, vmi.Name, i.Name)
|
||||
+ continue
|
||||
+ }
|
||||
+ endpoints = append(endpoints, &discovery.Endpoint{
|
||||
+ Addresses: []string{i.IP},
|
||||
+ Conditions: discovery.EndpointConditions{
|
||||
+ Ready: &ready,
|
||||
+ Serving: &serving,
|
||||
+ Terminating: &terminating,
|
||||
+ },
|
||||
+ NodeName: nodeNamePtr,
|
||||
+ })
|
||||
+ break
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ klog.Infof("Fallback: created %d endpoints from all VMIs in namespace %s", len(endpoints), c.infraNamespace)
|
||||
+ return endpoints
|
||||
+}
|
||||
+
|
||||
func (c *Controller) ensureEndpointSliceLabels(slice *discovery.EndpointSlice, svc *v1.Service) (map[string]string, bool) {
|
||||
labels := make(map[string]string)
|
||||
labelsChanged := false
|
||||
diff --git a/pkg/controller/kubevirteps/kubevirteps_controller_test.go b/pkg/controller/kubevirteps/kubevirteps_controller_test.go
|
||||
index 1c97035b4..d205d0bed 100644
|
||||
--- a/pkg/controller/kubevirteps/kubevirteps_controller_test.go
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:d5c836ba33cf5dbed7e6f866784f668f80ffe69179e7c75847b680111984eefb
|
||||
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:b42c6af641ee0eadb7e0a42e368021b4759f443cb7b71b7e745a64f0fc8b752e
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:a09724a7f95283f9130b3da2a89d81c4c6051c6edf0392a81b6fc90f404b76b6
|
||||
ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:d25e567bc8b17b596e050f5ff410e36112c7966e33f4b372c752e7350bacc894
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:1c0beb1b23a109b0e13727b4c73d2c74830e11cede92858ab20101b66f45a858
|
||||
ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:aca403030ff5d831415d72367866fdf291fab73ee2cfddbe4c93c2915a316ab1
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "tenant.name" . }}-cleanup
|
||||
namespace: {{ include "tenant.name" . }}
|
||||
namespace: cozy-system
|
||||
annotations:
|
||||
helm.sh/hook: pre-delete
|
||||
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
|
||||
@@ -39,13 +39,13 @@ roleRef:
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "tenant.name" . }}-cleanup
|
||||
namespace: {{ include "tenant.name" . }}
|
||||
namespace: cozy-system
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ include "tenant.name" . }}-cleanup
|
||||
namespace: {{ include "tenant.name" . }}
|
||||
namespace: cozy-system
|
||||
annotations:
|
||||
helm.sh/hook: pre-delete
|
||||
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
|
||||
|
||||
@@ -67,6 +67,19 @@ spec:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if ne (include "tenant.name" .) "tenant-root" }}
|
||||
- toEndpoints:
|
||||
{{- if hasPrefix "tenant-" .Release.Namespace }}
|
||||
{{- $parts := splitList "-" .Release.Namespace }}
|
||||
{{- range $i, $v := $parts }}
|
||||
{{- if ne $i 0 }}
|
||||
- matchLabels:
|
||||
cozystack.io/service: ingress
|
||||
"k8s:io.kubernetes.pod.namespace": {{ join "-" (slice $parts 0 (add $i 1)) }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumClusterwideNetworkPolicy
|
||||
|
||||
@@ -69,3 +69,36 @@ Generate a stable UUID for cloud-init re-initialization upon upgrade.
|
||||
{{- end }}
|
||||
{{- $uuid }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Node Affinity for Windows VMs
|
||||
*/}}
|
||||
{{- define "virtual-machine.nodeAffinity" -}}
|
||||
{{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" -}}
|
||||
{{- if $configMap -}}
|
||||
{{- $dedicatedNodesForWindowsVMs := get $configMap.data "dedicatedNodesForWindowsVMs" -}}
|
||||
{{- if eq $dedicatedNodesForWindowsVMs "true" -}}
|
||||
{{- $isWindows := hasPrefix "windows" (toString .Values.instanceProfile) -}}
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
{{- if $isWindows }}
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: scheduling.cozystack.io/vm-windows
|
||||
operator: In
|
||||
values:
|
||||
- "true"
|
||||
{{- else }}
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: scheduling.cozystack.io/vm-windows
|
||||
operator: NotIn
|
||||
values:
|
||||
- "true"
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
{{- if .Values.external }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
@@ -7,17 +6,24 @@ metadata:
|
||||
labels:
|
||||
apps.cozystack.io/user-service: "true"
|
||||
{{- include "virtual-machine.labels" . | nindent 4 }}
|
||||
{{- if .Values.external }}
|
||||
annotations:
|
||||
networking.cozystack.io/wholeIP: "true"
|
||||
{{- end }}
|
||||
spec:
|
||||
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
|
||||
{{- if .Values.external }}
|
||||
externalTrafficPolicy: Local
|
||||
{{- if ((include "cozy-lib.network.disableLoadBalancerNodePorts" $) | fromYaml) }}
|
||||
allocateLoadBalancerNodePorts: false
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
clusterIP: None
|
||||
{{- end }}
|
||||
selector:
|
||||
{{- include "virtual-machine.selectorLabels" . | nindent 4 }}
|
||||
ports:
|
||||
{{- if .Values.external }}
|
||||
{{- if and (eq .Values.externalMethod "WholeIP") (not .Values.externalPorts) }}
|
||||
- port: 65535
|
||||
{{- else }}
|
||||
@@ -27,4 +33,6 @@ spec:
|
||||
targetPort: {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
- port: 65535
|
||||
{{- end }}
|
||||
|
||||
@@ -27,7 +27,11 @@
|
||||
{{- if and $existingPVC $desiredStorage -}}
|
||||
{{- $currentStorage := $existingPVC.spec.resources.requests.storage | toString -}}
|
||||
{{- if not (eq $currentStorage $desiredStorage) -}}
|
||||
{{- $needResizePVC = true -}}
|
||||
{{- $oldSize := (include "cozy-lib.resources.toFloat" $currentStorage) | float64 -}}
|
||||
{{- $newSize := (include "cozy-lib.resources.toFloat" $desiredStorage) | float64 -}}
|
||||
{{- if gt $newSize $oldSize -}}
|
||||
{{- $needResizePVC = true -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
|
||||
@@ -124,6 +124,8 @@ spec:
|
||||
|
||||
terminationGracePeriodSeconds: 30
|
||||
|
||||
{{- include "virtual-machine.nodeAffinity" . | nindent 6 }}
|
||||
|
||||
volumes:
|
||||
- name: systemdisk
|
||||
dataVolume:
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
{{- $existingPVC := lookup "v1" "PersistentVolumeClaim" .Release.Namespace .Release.Name }}
|
||||
{{- if and $existingPVC (ne ($existingPVC.spec.resources.requests.storage | toString) .Values.storage) -}}
|
||||
{{- $shouldResize := false -}}
|
||||
{{- if and $existingPVC .Values.storage -}}
|
||||
{{- $currentStorage := $existingPVC.spec.resources.requests.storage | toString -}}
|
||||
{{- if ne $currentStorage .Values.storage -}}
|
||||
{{- $oldSize := (include "cozy-lib.resources.toFloat" $currentStorage) | float64 -}}
|
||||
{{- $newSize := (include "cozy-lib.resources.toFloat" .Values.storage) | float64 -}}
|
||||
{{- if gt $newSize $oldSize -}}
|
||||
{{- $shouldResize = true -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if $shouldResize -}}
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
@@ -23,6 +35,7 @@ spec:
|
||||
command: ["sh", "-xec"]
|
||||
args:
|
||||
- |
|
||||
echo "Resizing PVC to {{ .Values.storage }}..."
|
||||
kubectl patch pvc {{ .Release.Name }} -p '{"spec":{"resources":{"requests":{"storage":"{{ .Values.storage }}"}}}}'
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -69,3 +69,36 @@ Generate a stable UUID for cloud-init re-initialization upon upgrade.
|
||||
{{- end }}
|
||||
{{- $uuid }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Node Affinity for Windows VMs
|
||||
*/}}
|
||||
{{- define "virtual-machine.nodeAffinity" -}}
|
||||
{{- $configMap := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" -}}
|
||||
{{- if $configMap -}}
|
||||
{{- $dedicatedNodesForWindowsVMs := get $configMap.data "dedicatedNodesForWindowsVMs" -}}
|
||||
{{- if eq $dedicatedNodesForWindowsVMs "true" -}}
|
||||
{{- $isWindows := hasPrefix "windows" (toString .Values.instanceProfile) -}}
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
{{- if $isWindows }}
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: scheduling.cozystack.io/vm-windows
|
||||
operator: In
|
||||
values:
|
||||
- "true"
|
||||
{{- else }}
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: scheduling.cozystack.io/vm-windows
|
||||
operator: NotIn
|
||||
values:
|
||||
- "true"
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
{{- if .Values.external }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
@@ -7,17 +6,24 @@ metadata:
|
||||
labels:
|
||||
apps.cozystack.io/user-service: "true"
|
||||
{{- include "virtual-machine.labels" . | nindent 4 }}
|
||||
{{- if .Values.external }}
|
||||
annotations:
|
||||
networking.cozystack.io/wholeIP: "true"
|
||||
{{- end }}
|
||||
spec:
|
||||
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
|
||||
{{- if .Values.external }}
|
||||
externalTrafficPolicy: Local
|
||||
{{- if ((include "cozy-lib.network.disableLoadBalancerNodePorts" $) | fromYaml) }}
|
||||
allocateLoadBalancerNodePorts: false
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
clusterIP: None
|
||||
{{- end }}
|
||||
selector:
|
||||
{{- include "virtual-machine.selectorLabels" . | nindent 4 }}
|
||||
ports:
|
||||
{{- if .Values.external }}
|
||||
{{- if and (eq .Values.externalMethod "WholeIP") (not .Values.externalPorts) }}
|
||||
- port: 65535
|
||||
{{- else }}
|
||||
@@ -27,4 +33,6 @@ spec:
|
||||
targetPort: {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
- port: 65535
|
||||
{{- end }}
|
||||
|
||||
@@ -95,6 +95,9 @@ spec:
|
||||
noCloud: {}
|
||||
{{- end }}
|
||||
terminationGracePeriodSeconds: 30
|
||||
|
||||
{{- include "virtual-machine.nodeAffinity" . | nindent 6 }}
|
||||
|
||||
volumes:
|
||||
{{- range .Values.disks }}
|
||||
- name: disk-{{ .name }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.24-alpine as k8s-await-election-builder
|
||||
FROM golang:1.24-alpine AS k8s-await-election-builder
|
||||
|
||||
ARG K8S_AWAIT_ELECTION_GITREPO=https://github.com/LINBIT/k8s-await-election
|
||||
ARG K8S_AWAIT_ELECTION_VERSION=0.4.1
|
||||
@@ -13,7 +13,7 @@ RUN git clone ${K8S_AWAIT_ELECTION_GITREPO} /usr/local/go/k8s-await-election/ \
|
||||
&& make \
|
||||
&& mv ./out/k8s-await-election-${TARGETARCH} /k8s-await-election
|
||||
|
||||
FROM golang:1.24-alpine as builder
|
||||
FROM golang:1.24-alpine AS builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
cozystack:
|
||||
image: ghcr.io/cozystack/cozystack/installer:v0.38.2@sha256:9ff92b655de6f9bea3cba4cd42dcffabd9aace6966dcfb1cc02dda2420ea4a15
|
||||
image: ghcr.io/cozystack/cozystack/installer:v0.39.5@sha256:0d1b48c61278b0c6214c50ca06f354cfbe6181cd282e9d6ef35c968456fcc951
|
||||
|
||||
@@ -87,25 +87,25 @@ releases:
|
||||
releaseName: cozystack
|
||||
chart: cozy-cozy-proxy
|
||||
namespace: cozy-system
|
||||
dependsOn: [cilium,kubeovn]
|
||||
dependsOn: [cilium,kubeovn,multus]
|
||||
|
||||
- name: cert-manager-crds
|
||||
releaseName: cert-manager-crds
|
||||
chart: cozy-cert-manager-crds
|
||||
namespace: cozy-cert-manager
|
||||
dependsOn: [cilium, kubeovn]
|
||||
dependsOn: []
|
||||
|
||||
- name: cozystack-api
|
||||
releaseName: cozystack-api
|
||||
chart: cozy-cozystack-api
|
||||
namespace: cozy-system
|
||||
dependsOn: [cilium,kubeovn,cozystack-controller]
|
||||
dependsOn: [cilium,kubeovn,multus,cozystack-controller]
|
||||
|
||||
- name: cozystack-controller
|
||||
releaseName: cozystack-controller
|
||||
chart: cozy-cozystack-controller
|
||||
namespace: cozy-system
|
||||
dependsOn: [cilium,kubeovn]
|
||||
dependsOn: [cilium,kubeovn,multus]
|
||||
{{- if eq (index $cozyConfig.data "telemetry-enabled") "false" }}
|
||||
values:
|
||||
cozystackController:
|
||||
@@ -116,44 +116,44 @@ releases:
|
||||
releaseName: lineage-controller-webhook
|
||||
chart: cozy-lineage-controller-webhook
|
||||
namespace: cozy-system
|
||||
dependsOn: [cozystack-controller,cilium,kubeovn,cert-manager]
|
||||
dependsOn: [cozystack-controller,cilium,kubeovn,multus,cert-manager]
|
||||
|
||||
- name: cozystack-resource-definition-crd
|
||||
releaseName: cozystack-resource-definition-crd
|
||||
chart: cozystack-resource-definition-crd
|
||||
namespace: cozy-system
|
||||
dependsOn: [cilium,kubeovn,cozystack-api,cozystack-controller]
|
||||
dependsOn: [cilium,kubeovn,multus,cozystack-api,cozystack-controller]
|
||||
|
||||
- name: cozystack-resource-definitions
|
||||
releaseName: cozystack-resource-definitions
|
||||
chart: cozystack-resource-definitions
|
||||
namespace: cozy-system
|
||||
dependsOn: [cilium,kubeovn,cozystack-api,cozystack-controller,cozystack-resource-definition-crd]
|
||||
dependsOn: [cilium,kubeovn,multus,cozystack-api,cozystack-controller,cozystack-resource-definition-crd]
|
||||
|
||||
- name: cert-manager
|
||||
releaseName: cert-manager
|
||||
chart: cozy-cert-manager
|
||||
namespace: cozy-cert-manager
|
||||
dependsOn: [cert-manager-crds]
|
||||
dependsOn: [cilium,kubeovn,multus,cert-manager-crds]
|
||||
|
||||
- name: cert-manager-issuers
|
||||
releaseName: cert-manager-issuers
|
||||
chart: cozy-cert-manager-issuers
|
||||
namespace: cozy-cert-manager
|
||||
dependsOn: [cilium,kubeovn,cert-manager]
|
||||
dependsOn: [cilium,kubeovn,multus,cert-manager]
|
||||
|
||||
- name: victoria-metrics-operator
|
||||
releaseName: victoria-metrics-operator
|
||||
chart: cozy-victoria-metrics-operator
|
||||
namespace: cozy-victoria-metrics-operator
|
||||
dependsOn: [cilium,kubeovn,cert-manager]
|
||||
dependsOn: [cilium,kubeovn,multus,cert-manager]
|
||||
|
||||
- name: monitoring-agents
|
||||
releaseName: monitoring-agents
|
||||
chart: cozy-monitoring-agents
|
||||
namespace: cozy-monitoring
|
||||
privileged: true
|
||||
dependsOn: [victoria-metrics-operator, vertical-pod-autoscaler-crds]
|
||||
dependsOn: [victoria-metrics-operator,vertical-pod-autoscaler-crds]
|
||||
values:
|
||||
scrapeRules:
|
||||
etcd:
|
||||
@@ -163,14 +163,14 @@ releases:
|
||||
releaseName: kubevirt-operator
|
||||
chart: cozy-kubevirt-operator
|
||||
namespace: cozy-kubevirt
|
||||
dependsOn: [cilium,kubeovn,victoria-metrics-operator]
|
||||
dependsOn: [cilium,kubeovn,multus,victoria-metrics-operator]
|
||||
|
||||
- name: kubevirt
|
||||
releaseName: kubevirt
|
||||
chart: cozy-kubevirt
|
||||
namespace: cozy-kubevirt
|
||||
privileged: true
|
||||
dependsOn: [cilium,kubeovn,kubevirt-operator]
|
||||
dependsOn: [cilium,kubeovn,multus,kubevirt-operator]
|
||||
{{- $cpuAllocationRatio := index $cozyConfig.data "cpu-allocation-ratio" }}
|
||||
{{- if $cpuAllocationRatio }}
|
||||
values:
|
||||
@@ -181,19 +181,19 @@ releases:
|
||||
releaseName: kubevirt-instancetypes
|
||||
chart: cozy-kubevirt-instancetypes
|
||||
namespace: cozy-kubevirt
|
||||
dependsOn: [cilium,kubeovn,kubevirt-operator,kubevirt]
|
||||
dependsOn: [cilium,kubeovn,multus,kubevirt-operator,kubevirt]
|
||||
|
||||
- name: kubevirt-cdi-operator
|
||||
releaseName: kubevirt-cdi-operator
|
||||
chart: cozy-kubevirt-cdi-operator
|
||||
namespace: cozy-kubevirt-cdi
|
||||
dependsOn: [cilium,kubeovn]
|
||||
dependsOn: [cilium,kubeovn,multus]
|
||||
|
||||
- name: kubevirt-cdi
|
||||
releaseName: kubevirt-cdi
|
||||
chart: cozy-kubevirt-cdi
|
||||
namespace: cozy-kubevirt-cdi
|
||||
dependsOn: [cilium,kubeovn,kubevirt-cdi-operator]
|
||||
dependsOn: [cilium,kubeovn,multus,kubevirt-cdi-operator]
|
||||
|
||||
- name: gpu-operator
|
||||
releaseName: gpu-operator
|
||||
@@ -201,7 +201,7 @@ releases:
|
||||
namespace: cozy-gpu-operator
|
||||
privileged: true
|
||||
optional: true
|
||||
dependsOn: [cilium,kubeovn]
|
||||
dependsOn: [cilium,kubeovn,multus]
|
||||
valuesFiles:
|
||||
- values.yaml
|
||||
- values-talos.yaml
|
||||
@@ -211,25 +211,25 @@ releases:
|
||||
chart: cozy-metallb
|
||||
namespace: cozy-metallb
|
||||
privileged: true
|
||||
dependsOn: [cilium,kubeovn]
|
||||
dependsOn: [cilium,kubeovn,multus]
|
||||
|
||||
- name: etcd-operator
|
||||
releaseName: etcd-operator
|
||||
chart: cozy-etcd-operator
|
||||
namespace: cozy-etcd-operator
|
||||
dependsOn: [cilium,kubeovn,cert-manager]
|
||||
dependsOn: [cilium,kubeovn,multus,cert-manager]
|
||||
|
||||
- name: grafana-operator
|
||||
releaseName: grafana-operator
|
||||
chart: cozy-grafana-operator
|
||||
namespace: cozy-grafana-operator
|
||||
dependsOn: [cilium,kubeovn]
|
||||
dependsOn: [cilium,kubeovn,multus]
|
||||
|
||||
- name: mariadb-operator
|
||||
releaseName: mariadb-operator
|
||||
chart: cozy-mariadb-operator
|
||||
namespace: cozy-mariadb-operator
|
||||
dependsOn: [cilium,kubeovn,cert-manager,victoria-metrics-operator]
|
||||
dependsOn: [cilium,kubeovn,multus,cert-manager,victoria-metrics-operator]
|
||||
values:
|
||||
mariadb-operator:
|
||||
clusterName: {{ $clusterDomain }}
|
||||
@@ -238,13 +238,13 @@ releases:
|
||||
releaseName: postgres-operator
|
||||
chart: cozy-postgres-operator
|
||||
namespace: cozy-postgres-operator
|
||||
dependsOn: [cilium,kubeovn,cert-manager]
|
||||
dependsOn: [cilium,kubeovn,multus,cert-manager]
|
||||
|
||||
- name: kafka-operator
|
||||
releaseName: kafka-operator
|
||||
chart: cozy-kafka-operator
|
||||
namespace: cozy-kafka-operator
|
||||
dependsOn: [cilium,kubeovn,victoria-metrics-operator]
|
||||
dependsOn: [cilium,kubeovn,multus,victoria-metrics-operator]
|
||||
values:
|
||||
strimzi-kafka-operator:
|
||||
kubernetesServiceDnsDomain: {{ $clusterDomain }}
|
||||
@@ -253,65 +253,65 @@ releases:
|
||||
releaseName: clickhouse-operator
|
||||
chart: cozy-clickhouse-operator
|
||||
namespace: cozy-clickhouse-operator
|
||||
dependsOn: [cilium,kubeovn,victoria-metrics-operator]
|
||||
dependsOn: [cilium,kubeovn,multus,victoria-metrics-operator]
|
||||
|
||||
- name: foundationdb-operator
|
||||
releaseName: foundationdb-operator
|
||||
chart: cozy-foundationdb-operator
|
||||
namespace: cozy-foundationdb-operator
|
||||
dependsOn: [cilium,kubeovn,cert-manager]
|
||||
dependsOn: [cilium,kubeovn,multus,cert-manager]
|
||||
|
||||
- name: rabbitmq-operator
|
||||
releaseName: rabbitmq-operator
|
||||
chart: cozy-rabbitmq-operator
|
||||
namespace: cozy-rabbitmq-operator
|
||||
dependsOn: [cilium,kubeovn]
|
||||
dependsOn: [cilium,kubeovn,multus]
|
||||
|
||||
- name: redis-operator
|
||||
releaseName: redis-operator
|
||||
chart: cozy-redis-operator
|
||||
namespace: cozy-redis-operator
|
||||
dependsOn: [cilium,kubeovn]
|
||||
dependsOn: [cilium,kubeovn,multus]
|
||||
|
||||
- name: piraeus-operator
|
||||
releaseName: piraeus-operator
|
||||
chart: cozy-piraeus-operator
|
||||
namespace: cozy-linstor
|
||||
dependsOn: [cilium,kubeovn,cert-manager,victoria-metrics-operator]
|
||||
dependsOn: [cilium,kubeovn,multus,cert-manager,victoria-metrics-operator]
|
||||
|
||||
- name: linstor
|
||||
releaseName: linstor
|
||||
chart: cozy-linstor
|
||||
namespace: cozy-linstor
|
||||
privileged: true
|
||||
dependsOn: [piraeus-operator,cilium,kubeovn,cert-manager,snapshot-controller]
|
||||
dependsOn: [piraeus-operator,cilium,kubeovn,multus,cert-manager,snapshot-controller]
|
||||
|
||||
- name: nfs-driver
|
||||
releaseName: nfs-driver
|
||||
chart: cozy-nfs-driver
|
||||
namespace: cozy-nfs-driver
|
||||
privileged: true
|
||||
dependsOn: [cilium,kubeovn]
|
||||
dependsOn: [cilium,kubeovn,multus]
|
||||
optional: true
|
||||
|
||||
- name: snapshot-controller
|
||||
releaseName: snapshot-controller
|
||||
chart: cozy-snapshot-controller
|
||||
namespace: cozy-snapshot-controller
|
||||
dependsOn: [cilium,kubeovn,cert-manager-issuers]
|
||||
dependsOn: [cilium,kubeovn,multus,cert-manager-issuers]
|
||||
|
||||
- name: objectstorage-controller
|
||||
releaseName: objectstorage-controller
|
||||
chart: cozy-objectstorage-controller
|
||||
namespace: cozy-objectstorage-controller
|
||||
dependsOn: [cilium,kubeovn]
|
||||
dependsOn: [cilium,kubeovn,multus]
|
||||
|
||||
- name: telepresence
|
||||
releaseName: traffic-manager
|
||||
chart: cozy-telepresence
|
||||
namespace: cozy-telepresence
|
||||
optional: true
|
||||
dependsOn: [cilium,kubeovn]
|
||||
dependsOn: [cilium,kubeovn,multus]
|
||||
|
||||
- name: dashboard
|
||||
releaseName: dashboard
|
||||
@@ -324,6 +324,7 @@ releases:
|
||||
dependsOn:
|
||||
- cilium
|
||||
- kubeovn
|
||||
- multus
|
||||
{{- if eq $oidcEnabled "true" }}
|
||||
- keycloak-configure
|
||||
{{- end }}
|
||||
@@ -332,56 +333,56 @@ releases:
|
||||
releaseName: kamaji
|
||||
chart: cozy-kamaji
|
||||
namespace: cozy-kamaji
|
||||
dependsOn: [cilium,kubeovn,cert-manager]
|
||||
dependsOn: [cilium,kubeovn,multus,cert-manager]
|
||||
|
||||
- name: capi-operator
|
||||
releaseName: capi-operator
|
||||
chart: cozy-capi-operator
|
||||
namespace: cozy-cluster-api
|
||||
privileged: true
|
||||
dependsOn: [cilium,kubeovn,cert-manager]
|
||||
dependsOn: [cilium,kubeovn,multus,cert-manager]
|
||||
|
||||
- name: capi-providers-bootstrap
|
||||
releaseName: capi-providers-bootstrap
|
||||
chart: cozy-capi-providers-bootstrap
|
||||
namespace: cozy-cluster-api
|
||||
privileged: true
|
||||
dependsOn: [cilium,kubeovn,capi-operator]
|
||||
dependsOn: [cilium,kubeovn,multus,capi-operator]
|
||||
|
||||
- name: capi-providers-core
|
||||
releaseName: capi-providers-core
|
||||
chart: cozy-capi-providers-core
|
||||
namespace: cozy-cluster-api
|
||||
privileged: true
|
||||
dependsOn: [cilium,kubeovn,capi-operator]
|
||||
dependsOn: [cilium,kubeovn,multus,capi-operator]
|
||||
|
||||
- name: capi-providers-cpprovider
|
||||
releaseName: capi-providers-cpprovider
|
||||
chart: cozy-capi-providers-cpprovider
|
||||
namespace: cozy-cluster-api
|
||||
privileged: true
|
||||
dependsOn: [cilium,kubeovn,capi-operator]
|
||||
dependsOn: [cilium,kubeovn,multus,capi-operator]
|
||||
|
||||
- name: capi-providers-infraprovider
|
||||
releaseName: capi-providers-infraprovider
|
||||
chart: cozy-capi-providers-infraprovider
|
||||
namespace: cozy-cluster-api
|
||||
privileged: true
|
||||
dependsOn: [cilium,kubeovn,capi-operator]
|
||||
dependsOn: [cilium,kubeovn,multus,capi-operator]
|
||||
|
||||
- name: external-dns
|
||||
releaseName: external-dns
|
||||
chart: cozy-external-dns
|
||||
namespace: cozy-external-dns
|
||||
optional: true
|
||||
dependsOn: [cilium,kubeovn]
|
||||
dependsOn: [cilium,kubeovn,multus]
|
||||
|
||||
- name: external-secrets-operator
|
||||
releaseName: external-secrets-operator
|
||||
chart: cozy-external-secrets-operator
|
||||
namespace: cozy-external-secrets-operator
|
||||
optional: true
|
||||
dependsOn: [cilium,kubeovn]
|
||||
dependsOn: [cilium,kubeovn,multus]
|
||||
|
||||
- name: bootbox
|
||||
releaseName: bootbox
|
||||
@@ -389,7 +390,7 @@ releases:
|
||||
namespace: cozy-bootbox
|
||||
privileged: true
|
||||
optional: true
|
||||
dependsOn: [cilium,kubeovn]
|
||||
dependsOn: [cilium,kubeovn,multus]
|
||||
|
||||
{{- if $oidcEnabled }}
|
||||
- name: keycloak
|
||||
@@ -438,7 +439,7 @@ releases:
|
||||
chart: cozy-vertical-pod-autoscaler-crds
|
||||
namespace: cozy-vertical-pod-autoscaler
|
||||
privileged: true
|
||||
dependsOn: [cilium, kubeovn]
|
||||
dependsOn: []
|
||||
|
||||
- name: reloader
|
||||
releaseName: reloader
|
||||
|
||||
0
packages/core/testing/Chart.yaml
Executable file → Normal file
0
packages/core/testing/Chart.yaml
Executable file → Normal file
0
packages/core/testing/Makefile
Executable file → Normal file
0
packages/core/testing/Makefile
Executable file → Normal file
10
packages/core/testing/images/e2e-sandbox/Dockerfile
Executable file → Normal file
10
packages/core/testing/images/e2e-sandbox/Dockerfile
Executable file → Normal file
@@ -9,7 +9,7 @@ ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
|
||||
RUN apt update -q
|
||||
RUN apt install -yq --no-install-recommends psmisc genisoimage ca-certificates qemu-kvm qemu-utils iproute2 iptables wget xz-utils netcat curl jq make git
|
||||
RUN apt install -yq --no-install-recommends psmisc genisoimage ca-certificates qemu-kvm qemu-utils iproute2 iptables wget xz-utils netcat curl jq make git bash-completion
|
||||
RUN curl -sSL "https://github.com/siderolabs/talos/releases/download/v${TALOSCTL_VERSION}/talosctl-${TARGETOS}-${TARGETARCH}" -o /usr/local/bin/talosctl \
|
||||
&& chmod +x /usr/local/bin/talosctl
|
||||
RUN curl -sSL "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/${TARGETOS}/${TARGETARCH}/kubectl" -o /usr/local/bin/kubectl \
|
||||
@@ -21,5 +21,13 @@ RUN curl -sSL "https://fluxcd.io/install.sh" | bash
|
||||
RUN curl -sSL "https://github.com/cozystack/cozypkg/raw/refs/heads/main/hack/install.sh" | sh -s -- -v "${COZYPKG_VERSION}"
|
||||
RUN curl https://dl.min.io/client/mc/release/${TARGETOS}-${TARGETARCH}/mc --create-dirs -o /usr/local/bin/mc \
|
||||
&& chmod +x /usr/local/bin/mc
|
||||
RUN <<'EOF'
|
||||
cat <<'EOT' >> /etc/bash.bashrc
|
||||
. /etc/bash_completion
|
||||
. <(kubectl completion bash)
|
||||
alias k=kubectl
|
||||
complete -F __start_kubectl k
|
||||
EOT
|
||||
EOF
|
||||
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||
|
||||
2
packages/core/testing/values.yaml
Executable file → Normal file
2
packages/core/testing/values.yaml
Executable file → Normal file
@@ -1,2 +1,2 @@
|
||||
e2e:
|
||||
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.38.2@sha256:84be9e42bc2c04b0765c8b89e0a9728c49ebf4676a92522b007af96ae9aec68d
|
||||
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.39.5@sha256:3395a0e3c02e85878a44d3177cae3c37b087edeb4fab6547cb8227862b1de773
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/matchbox:v0.38.2@sha256:9cd7f46fcae119a3f8e35b428b018d0cb6da7b0cdd2ce764cc9fbf6dcd903f27
|
||||
ghcr.io/cozystack/cozystack/matchbox:v0.39.5@sha256:7a0c29d75a0b5416e2409d4703131b2f3f09cdc39e55ee916803cabf0606f724
|
||||
|
||||
@@ -12,6 +12,13 @@ spec:
|
||||
containers:
|
||||
- name: etcd-defrag
|
||||
image: ghcr.io/ahrtr/etcd-defrag:v0.13.0
|
||||
resources:
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
args:
|
||||
- --endpoints={{ range $i, $e := until (int .Values.replicas) }}{{ if $i }},{{ end }}https://{{ $.Release.Name }}-{{ $i }}.{{ $.Release.Name }}-headless.{{ $.Release.Namespace }}.svc:2379{{ end }}
|
||||
- --cacert=/etc/etcd/pki/client/cert/ca.crt
|
||||
|
||||
@@ -55,25 +55,26 @@
|
||||
|
||||
### Alerta configuration
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| ----------------------------------------- | ----------------------------------------------------------------- | ---------- | ------- |
|
||||
| `alerta` | Configuration for the Alerta service. | `object` | `{}` |
|
||||
| `alerta.storage` | Persistent volume size for the database. | `string` | `10Gi` |
|
||||
| `alerta.storageClassName` | StorageClass used for the database. | `string` | `""` |
|
||||
| `alerta.resources` | Resource configuration. | `object` | `{}` |
|
||||
| `alerta.resources.requests` | Resource requests. | `object` | `{}` |
|
||||
| `alerta.resources.requests.cpu` | CPU request. | `quantity` | `100m` |
|
||||
| `alerta.resources.requests.memory` | Memory request. | `quantity` | `256Mi` |
|
||||
| `alerta.resources.limits` | Resource limits. | `object` | `{}` |
|
||||
| `alerta.resources.limits.cpu` | CPU limit. | `quantity` | `1` |
|
||||
| `alerta.resources.limits.memory` | Memory limit. | `quantity` | `1Gi` |
|
||||
| `alerta.alerts` | Alert routing configuration. | `object` | `{}` |
|
||||
| `alerta.alerts.telegram` | Configuration for Telegram alerts. | `object` | `{}` |
|
||||
| `alerta.alerts.telegram.token` | Telegram bot token. | `string` | `""` |
|
||||
| `alerta.alerts.telegram.chatID` | Telegram chat ID(s), separated by commas. | `string` | `""` |
|
||||
| `alerta.alerts.telegram.disabledSeverity` | List of severities without alerts (e.g. "informational,warning"). | `string` | `""` |
|
||||
| `alerta.alerts.slack` | Configuration for Slack alerts. | `object` | `{}` |
|
||||
| `alerta.alerts.slack.url` | Configuration uri for Slack alerts. | `string` | `""` |
|
||||
| Name | Description | Type | Value |
|
||||
| ----------------------------------------- | --------------------------------------------------------------------- | ---------- | ------- |
|
||||
| `alerta` | Configuration for the Alerta service. | `object` | `{}` |
|
||||
| `alerta.storage` | Persistent volume size for the database. | `string` | `10Gi` |
|
||||
| `alerta.storageClassName` | StorageClass used for the database. | `string` | `""` |
|
||||
| `alerta.resources` | Resource configuration. | `object` | `{}` |
|
||||
| `alerta.resources.requests` | Resource requests. | `object` | `{}` |
|
||||
| `alerta.resources.requests.cpu` | CPU request. | `quantity` | `100m` |
|
||||
| `alerta.resources.requests.memory` | Memory request. | `quantity` | `256Mi` |
|
||||
| `alerta.resources.limits` | Resource limits. | `object` | `{}` |
|
||||
| `alerta.resources.limits.cpu` | CPU limit. | `quantity` | `1` |
|
||||
| `alerta.resources.limits.memory` | Memory limit. | `quantity` | `1Gi` |
|
||||
| `alerta.alerts` | Alert routing configuration. | `object` | `{}` |
|
||||
| `alerta.alerts.telegram` | Configuration for Telegram alerts. | `object` | `{}` |
|
||||
| `alerta.alerts.telegram.token` | Telegram bot token. | `string` | `""` |
|
||||
| `alerta.alerts.telegram.chatID` | Telegram chat ID(s), separated by commas. | `string` | `""` |
|
||||
| `alerta.alerts.telegram.disabledSeverity` | List of severities without alerts (e.g. ["informational","warning"]). | `[]string` | `[]` |
|
||||
| `alerta.alerts.slack` | Configuration for Slack alerts. | `object` | `{}` |
|
||||
| `alerta.alerts.slack.url` | Configuration uri for Slack alerts. | `string` | `""` |
|
||||
| `alerta.alerts.slack.disabledSeverity` | List of severities without alerts (e.g. ["informational","warning"]). | `[]string` | `[]` |
|
||||
|
||||
|
||||
### Grafana configuration
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/grafana:0.0.0@sha256:c63978e1ed0304e8518b31ddee56c4e8115541b997d8efbe1c0a74da57140399
|
||||
ghcr.io/cozystack/cozystack/grafana:0.0.0@sha256:8ce0cd90c8f614cdabf5a41f8aa50b7dfbd02b31b9a0bd7897927e7f89968e07
|
||||
|
||||
@@ -129,13 +129,19 @@ spec:
|
||||
value: "{{ .Values.alerta.alerts.telegram.token }}"
|
||||
- name: TELEGRAM_WEBHOOK_URL
|
||||
value: "https://{{ printf "alerta.%s" (.Values.host | default $host) }}/api/webhooks/telegram?api-key={{ $apiKey }}"
|
||||
{{- if .Values.alerta.alerts.telegram.disabledSeverity }}
|
||||
- name: TELEGRAM_DISABLE_NOTIFICATION_SEVERITY
|
||||
value: "{{ .Values.alerta.alerts.telegram.disabledSeverity }}"
|
||||
value: {{ .Values.alerta.alerts.telegram.disabledSeverity | toJson | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{- if .Values.alerta.alerts.slack.url }}
|
||||
- name: "SLACK_WEBHOOK_URL"
|
||||
value: "{{ .Values.alerta.alerts.slack.url }}"
|
||||
{{- if .Values.alerta.alerts.slack.disabledSeverity }}
|
||||
- name: SLACK_SEVERITY_FILTER
|
||||
value: {{ .Values.alerta.alerts.slack.disabledSeverity | toJson | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
ports:
|
||||
|
||||
@@ -20,6 +20,14 @@
|
||||
"url"
|
||||
],
|
||||
"properties": {
|
||||
"disabledSeverity": {
|
||||
"description": "List of severities without alerts (e.g. [\"informational\",\"warning\"]).",
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"description": "Configuration uri for Slack alerts.",
|
||||
"type": "string",
|
||||
@@ -42,9 +50,12 @@
|
||||
"default": ""
|
||||
},
|
||||
"disabledSeverity": {
|
||||
"description": "List of severities without alerts (e.g. \"informational,warning\").",
|
||||
"type": "string",
|
||||
"default": ""
|
||||
"description": "List of severities without alerts (e.g. [\"informational\",\"warning\"]).",
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"token": {
|
||||
"description": "Telegram bot token.",
|
||||
|
||||
@@ -99,10 +99,11 @@ logsStorages:
|
||||
## @typedef {struct} TelegramAlerts - Telegram alert configuration.
|
||||
## @field {string} token - Telegram bot token.
|
||||
## @field {string} chatID - Telegram chat ID(s), separated by commas.
|
||||
## @field {string} [disabledSeverity] - List of severities without alerts (e.g. "informational,warning").
|
||||
## @field {[]string} [disabledSeverity] - List of severities without alerts (e.g. ["informational","warning"]).
|
||||
|
||||
## @typedef {struct} SlackAlerts - Slack alert configuration.
|
||||
## @field {string} url - Configuration uri for Slack alerts.
|
||||
## @field {[]string} [disabledSeverity] - List of severities without alerts (e.g. ["informational","warning"]).
|
||||
|
||||
## @typedef {struct} Alerts - Alert routing configuration.
|
||||
## @field {TelegramAlerts} [telegram] - Configuration for Telegram alerts.
|
||||
@@ -129,9 +130,10 @@ alerta:
|
||||
telegram:
|
||||
token: ""
|
||||
chatID: ""
|
||||
disabledSeverity: ""
|
||||
disabledSeverity: []
|
||||
slack:
|
||||
url: ""
|
||||
disabledSeverity: []
|
||||
##
|
||||
## @section Grafana configuration
|
||||
##
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.38.2@sha256:ff3281fe53a97d2cd5cd94bd4c4d8ff08189508729869bb39b3f60c80da5f919
|
||||
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.39.5@sha256:4bb47c8adb34543403a16d1ff61b307939850e8cf5fc365388469e30dfb9681b
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
OUT=../../_out/repos/system
|
||||
CHARTS := $(shell find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}')
|
||||
CHARTS := $(shell grep -F 'version: 0.0.0' */Chart.yaml | cut -f1 -d/)
|
||||
VERSIONED_CHARTS := $(shell grep '^version:' */Chart.yaml | grep -Fv '0.0.0' | cut -f1 -d/)
|
||||
|
||||
include ../../scripts/common-envs.mk
|
||||
|
||||
repo:
|
||||
rm -rf "$(OUT)"
|
||||
helm package -d "$(OUT)" $(CHARTS) --version $(COZYSTACK_VERSION)
|
||||
helm package -d "$(OUT)" $(VERSIONED_CHARTS)
|
||||
cd "$(OUT)" && helm repo index .
|
||||
|
||||
fix-charts:
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:3825c9b4b6238f88f1b0de73bd18866a7e5f83f178d28fe2830f3bf24efb187d
|
||||
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:ecb140d026ed72660306953a7eec140d7ac81e79544d5bbf1aba5f62aa5f8b69
|
||||
|
||||
@@ -10,6 +10,7 @@ cilium:
|
||||
enabled: true
|
||||
loadBalancer:
|
||||
algorithm: maglev
|
||||
serviceTopology: true
|
||||
ipam:
|
||||
mode: "kubernetes"
|
||||
image:
|
||||
@@ -18,3 +19,6 @@ cilium:
|
||||
digest: "sha256:81262986a41487bfa3d0465091d3a386def5bd1ab476350bd4af2fdee5846fe6"
|
||||
envoy:
|
||||
enabled: false
|
||||
rollOutCiliumPods: true
|
||||
operator:
|
||||
rollOutPods: true
|
||||
|
||||
@@ -3,3 +3,6 @@ coredns:
|
||||
repository: registry.k8s.io/coredns/coredns
|
||||
tag: v1.12.4
|
||||
replicaCount: 2
|
||||
k8sAppLabelOverride: kube-dns
|
||||
service:
|
||||
name: kube-dns
|
||||
|
||||
@@ -21,6 +21,8 @@ spec:
|
||||
labels:
|
||||
app: cozystack-api
|
||||
spec:
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
serviceAccountName: cozystack-api
|
||||
{{- if .Values.cozystackAPI.localK8sAPIEndpoint.enabled }}
|
||||
nodeSelector:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
cozystackAPI:
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-api:v0.38.2@sha256:d17f1c59658731e5a2063c3db348adbc03b5cd31720052016b68449164cf2f14
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-api:v0.39.5@sha256:51574c6bb61ae31e63193f84daf18c14ceb71580786e262191c4aa0ac44b1519
|
||||
localK8sAPIEndpoint:
|
||||
enabled: true
|
||||
replicas: 2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
cozystackController:
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.38.2@sha256:468b2eccbc0aa00bd3d72d56624a46e6ba178fa279cdd19248af74d32ea7d319
|
||||
image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.39.5@sha256:dd6bb910842151ec60cea74f43b03ac35f461a03f6fe4370b12c7aaf48fee7b8
|
||||
debug: false
|
||||
disableTelemetry: false
|
||||
cozystackVersion: "v0.38.2"
|
||||
cozystackVersion: "v0.39.5"
|
||||
cozystackAPIKind: "DaemonSet"
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
{{- $brandingConfig:= lookup "v1" "ConfigMap" "cozy-system" "cozystack-branding" }}
|
||||
|
||||
{{- $tenantText := "v0.38.2" }}
|
||||
{{- $tenantText := "v0.39.5" }}
|
||||
{{- $footerText := "Cozystack" }}
|
||||
{{- $titleText := "Cozystack Dashboard" }}
|
||||
{{- $logoText := "" }}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
openapiUI:
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui:v0.38.2@sha256:5aafb6c864c5523418d021a9fe5b514990d36972b6f1de9c34a1cd41f9d8bf7e
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui:v0.39.5@sha256:6c48044fd9bfaea65478c39fc9f3e7322d06aef46022211847621eeb6d46c51c
|
||||
openapiUIK8sBff:
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v0.38.2@sha256:7ffd8ae7b9da73fec7ae61a71c9c821a718d89a1b1df0197e09fda57678e1220
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v0.39.5@sha256:fda379dce49c2cd8cb8d7d2a1d8ec6f7bedb3419c058c4355ecdece1c1e937f4
|
||||
tokenProxy:
|
||||
image: ghcr.io/cozystack/cozystack/token-proxy:v0.38.2@sha256:fad27112617bb17816702571e1f39d0ac3fe5283468d25eb12f79906cdab566b
|
||||
image: ghcr.io/cozystack/cozystack/token-proxy:v0.39.5@sha256:4fc8a11f8a1a81aa0774ae2b1ed2e05d36d0b3ef1e37979cc4994e65114d93ae
|
||||
|
||||
@@ -54,9 +54,45 @@ ingress-nginx:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 90Mi
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 10
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchExpressions:
|
||||
- key: app.kubernetes.io/name
|
||||
operator: In
|
||||
values:
|
||||
- '{{ include "ingress-nginx.name" . }}'
|
||||
- key: app.kubernetes.io/instance
|
||||
operator: In
|
||||
values:
|
||||
- '{{ .Release.Name }}'
|
||||
- key: app.kubernetes.io/component
|
||||
operator: In
|
||||
values:
|
||||
- controller
|
||||
topologyKey: kubernetes.io/hostname
|
||||
- weight: 100
|
||||
podAffinityTerm:
|
||||
labelSelector:
|
||||
matchExpressions:
|
||||
- key: app.kubernetes.io/name
|
||||
operator: In
|
||||
values:
|
||||
- '{{ include "ingress-nginx.name" . }}'
|
||||
- key: app.kubernetes.io/instance
|
||||
operator: In
|
||||
values:
|
||||
- '{{ .Release.Name }}'
|
||||
- key: app.kubernetes.io/component
|
||||
operator: In
|
||||
values:
|
||||
- controller
|
||||
topologyKey: topology.kubernetes.io/zone
|
||||
|
||||
defaultBackend:
|
||||
##
|
||||
enabled: true
|
||||
resources:
|
||||
limits:
|
||||
|
||||
@@ -3,7 +3,7 @@ kamaji:
|
||||
deploy: false
|
||||
image:
|
||||
pullPolicy: IfNotPresent
|
||||
tag: v0.38.2@sha256:13741b8f6dfede3ea0fd16d8bbebae810bc19254a81d7e5a139535efa17eabff
|
||||
tag: v0.39.5@sha256:0bcfb2d376224b18a0627d7b18ba6202bb8a553f71796023e12740d9513740c7
|
||||
repository: ghcr.io/cozystack/cozystack/kamaji
|
||||
resources:
|
||||
limits:
|
||||
@@ -13,4 +13,4 @@ kamaji:
|
||||
cpu: 100m
|
||||
memory: 100Mi
|
||||
extraArgs:
|
||||
- --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v0.38.2@sha256:13741b8f6dfede3ea0fd16d8bbebae810bc19254a81d7e5a139535efa17eabff
|
||||
- --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v0.39.5@sha256:0bcfb2d376224b18a0627d7b18ba6202bb8a553f71796023e12740d9513740c7
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
portSecurity: true
|
||||
routes: ""
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v0.38.2@sha256:76c8af24cbec0261718c13c0150aa81c238a956626d4fd7baa8970b47fb3a6f0
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v0.39.5@sha256:52b7764b06ca3d6e5a2e74ba3d6d68ecdcf6b71a227e50b1344a8f11bf693a23
|
||||
ovnCentralName: ovn-central
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
portSecurity: true
|
||||
routes: ""
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.38.2@sha256:8e67b2971f8c079a8b0636be1d091a9545d6cb653d745ff222a5966f56f903bd
|
||||
image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.39.5@sha256:e6334c29d3aaf0dea766c88e3e05b53ad623d1bb497b3c836e6f76adade45b29
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
apiVersion: v2
|
||||
name: cozy-kubeovn
|
||||
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process
|
||||
version: 0.38.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
KUBEOVN_TAG=$(shell awk '$$1 == "version:" {print $$2}' charts/kube-ovn/Chart.yaml)
|
||||
KUBEOVN_TAG=v0.40.0
|
||||
|
||||
export NAME=kubeovn
|
||||
export NAMESPACE=cozy-$(NAME)
|
||||
@@ -7,28 +7,7 @@ include ../../../scripts/common-envs.mk
|
||||
include ../../../scripts/package.mk
|
||||
|
||||
update:
|
||||
rm -rf charts && mkdir -p charts/kube-ovn
|
||||
tag=$$(git ls-remote --tags --sort="v:refname" https://github.com/kubeovn/kube-ovn | awk -F'[/^]' '{print $$3}' | grep '^v1\.14\.' | tail -n1 ) && \
|
||||
curl -sSL https://github.com/kubeovn/kube-ovn/archive/refs/tags/$${tag}.tar.gz | \
|
||||
tar xzvf - --strip 1 kube-ovn-$${tag#*v}/charts/kube-ovn
|
||||
patch --no-backup-if-mismatch -p4 < patches/cozyconfig.diff
|
||||
patch --no-backup-if-mismatch -p4 < patches/mtu.diff
|
||||
version=$$(awk '$$1 == "version:" {print $$2}' charts/kube-ovn/Chart.yaml) && \
|
||||
sed -i "s/ARG VERSION=.*/ARG VERSION=$${version}/" images/kubeovn/Dockerfile && \
|
||||
sed -i "s/ARG TAG=.*/ARG TAG=$${version}/" images/kubeovn/Dockerfile
|
||||
|
||||
image:
|
||||
docker buildx build images/kubeovn \
|
||||
--tag $(REGISTRY)/kubeovn:$(call settag,$(KUBEOVN_TAG)) \
|
||||
--tag $(REGISTRY)/kubeovn:$(call settag,$(KUBEOVN_TAG)-$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/kubeovn:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/kubeovn.json \
|
||||
$(BUILDX_ARGS)
|
||||
REGISTRY="$(REGISTRY)" \
|
||||
yq -i '.global.registry.address = strenv(REGISTRY)' values.yaml
|
||||
REPOSITORY="kubeovn" \
|
||||
yq -i '.global.images.kubeovn.repository = strenv(REPOSITORY)' values.yaml
|
||||
TAG="$(call settag,$(KUBEOVN_TAG))@$$(yq e '."containerimage.digest"' images/kubeovn.json -o json -r)" \
|
||||
yq -i '.global.images.kubeovn.tag = strenv(TAG)' values.yaml
|
||||
rm -f images/kubeovn.json
|
||||
rm -rf charts values.yaml Chart.yaml
|
||||
tag=$(KUBEOVN_TAG) && \
|
||||
curl -sSL https://github.com/cozystack/kubeovn/archive/refs/tags/$${tag}.tar.gz | \
|
||||
tar xzvf - --strip 2 kubeovn-$${tag#*v}/chart
|
||||
|
||||
@@ -15,12 +15,12 @@ type: application
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: v1.14.11
|
||||
version: v1.14.25
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "1.14.11"
|
||||
appVersion: "1.14.25"
|
||||
|
||||
kubeVersion: ">= 1.29.0-0"
|
||||
|
||||
@@ -2,6 +2,18 @@
|
||||
|
||||
Currently supported version: 1.9
|
||||
|
||||
## Installing the Chart
|
||||
|
||||
### From OCI Registry
|
||||
|
||||
The Helm chart is available from GitHub Container Registry:
|
||||
|
||||
```bash
|
||||
helm install kube-ovn oci://ghcr.io/kubeovn/charts/kube-ovn --version v1.15.0
|
||||
```
|
||||
|
||||
### From Source
|
||||
|
||||
Installation :
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
{{/*
|
||||
Get IP-addresses of master nodes
|
||||
Get IP-addresses of master nodes. If no nodes are returned, we assume this is
|
||||
a dry-run/template call and return nothing.
|
||||
*/}}
|
||||
{{- define "kubeovn.nodeIPs" -}}
|
||||
{{- $nodes := lookup "v1" "Node" "" "" -}}
|
||||
{{- if $nodes -}}
|
||||
{{- $ips := list -}}
|
||||
{{- range $node := $nodes.items -}}
|
||||
{{- $label := splitList "=" $.Values.MASTER_NODES_LABEL }}
|
||||
@@ -25,6 +27,7 @@ Get IP-addresses of master nodes
|
||||
{{- end -}}
|
||||
{{ join "," $ips }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Number of master nodes
|
||||
|
||||
@@ -39,7 +39,11 @@ spec:
|
||||
topologyKey: kubernetes.io/hostname
|
||||
priorityClassName: system-cluster-critical
|
||||
serviceAccountName: ovn-ovs
|
||||
automountServiceAccountToken: true
|
||||
hostNetwork: true
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
- name: hostpath-init
|
||||
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}
|
||||
|
||||
@@ -46,7 +46,11 @@ spec:
|
||||
topologyKey: kubernetes.io/hostname
|
||||
priorityClassName: system-cluster-critical
|
||||
serviceAccountName: ovn
|
||||
automountServiceAccountToken: true
|
||||
hostNetwork: true
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
- name: hostpath-init
|
||||
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}
|
||||
|
||||
@@ -40,7 +40,11 @@ spec:
|
||||
topologyKey: kubernetes.io/hostname
|
||||
priorityClassName: system-cluster-critical
|
||||
serviceAccountName: ovn
|
||||
automountServiceAccountToken: true
|
||||
hostNetwork: true
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
- name: hostpath-init
|
||||
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}
|
||||
|
||||
@@ -1200,6 +1200,52 @@ spec:
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
tolerations:
|
||||
description: optional tolerations applied to the workload pods
|
||||
items:
|
||||
description: |-
|
||||
The pod this Toleration is attached to tolerates any taint that matches
|
||||
the triple <key,value,effect> using the matching operator <operator>.
|
||||
properties:
|
||||
effect:
|
||||
description: |-
|
||||
Effect indicates the taint effect to match. Empty means match all taint effects.
|
||||
When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.
|
||||
type: string
|
||||
enum:
|
||||
- NoSchedule
|
||||
- PreferNoSchedule
|
||||
- NoExecute
|
||||
key:
|
||||
description: |-
|
||||
Key is the taint key that the toleration applies to. Empty means match all taint keys.
|
||||
If the key is empty, operator must be Exists; this combination means to match all values and all keys.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
Operator represents a key's relationship to the value.
|
||||
Valid operators are Exists and Equal. Defaults to Equal.
|
||||
Exists is equivalent to wildcard for value, so that a pod can
|
||||
tolerate all taints of a particular category.
|
||||
type: string
|
||||
enum:
|
||||
- Exists
|
||||
- Equal
|
||||
tolerationSeconds:
|
||||
description: |-
|
||||
TolerationSeconds represents the period of time the toleration (which must be
|
||||
of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default,
|
||||
it is not set, which means tolerate the taint forever (do not evict). Zero and
|
||||
negative values will be treated as 0 (evict immediately) by the system.
|
||||
format: int64
|
||||
type: integer
|
||||
value:
|
||||
description: |-
|
||||
Value is the taint value the toleration matches to.
|
||||
If the operator is Exists, the value should be empty, otherwise just a regular string.
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
@@ -2871,6 +2917,8 @@ spec:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
autoCreateVlanSubinterfaces:
|
||||
type: boolean
|
||||
required:
|
||||
- defaultInterface
|
||||
status:
|
||||
|
||||
@@ -37,7 +37,11 @@ spec:
|
||||
topologyKey: kubernetes.io/hostname
|
||||
priorityClassName: system-cluster-critical
|
||||
serviceAccountName: kube-ovn-app
|
||||
automountServiceAccountToken: true
|
||||
hostNetwork: true
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
- name: hostpath-init
|
||||
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}
|
||||
|
||||
@@ -27,8 +27,12 @@ spec:
|
||||
- operator: Exists
|
||||
priorityClassName: system-node-critical
|
||||
serviceAccountName: ovn-ovs
|
||||
automountServiceAccountToken: true
|
||||
hostNetwork: true
|
||||
hostPID: true
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: openvswitch
|
||||
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.DPDK_IMAGE_TAG }}
|
||||
|
||||
@@ -3,6 +3,7 @@ kind: ServiceAccount
|
||||
metadata:
|
||||
name: ovn
|
||||
namespace: {{ .Values.namespace }}
|
||||
automountServiceAccountToken: false
|
||||
{{- if .Values.global.registry.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- range $index, $secret := .Values.global.registry.imagePullSecrets }}
|
||||
@@ -18,6 +19,7 @@ kind: ServiceAccount
|
||||
metadata:
|
||||
name: ovn-ovs
|
||||
namespace: {{ .Values.namespace }}
|
||||
automountServiceAccountToken: false
|
||||
{{- if .Values.global.registry.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- range $index, $secret := .Values.global.registry.imagePullSecrets }}
|
||||
@@ -33,6 +35,7 @@ kind: ServiceAccount
|
||||
metadata:
|
||||
name: kube-ovn-cni
|
||||
namespace: {{ .Values.namespace }}
|
||||
automountServiceAccountToken: false
|
||||
{{- if .Values.global.registry.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- range $index, $secret := .Values.global.registry.imagePullSecrets }}
|
||||
@@ -48,6 +51,7 @@ kind: ServiceAccount
|
||||
metadata:
|
||||
name: kube-ovn-app
|
||||
namespace: {{ .Values.namespace }}
|
||||
automountServiceAccountToken: false
|
||||
{{- if .Values.global.registry.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- range $index, $secret := .Values.global.registry.imagePullSecrets }}
|
||||
|
||||
@@ -26,8 +26,12 @@ spec:
|
||||
operator: Exists
|
||||
priorityClassName: system-node-critical
|
||||
serviceAccountName: kube-ovn-cni
|
||||
automountServiceAccountToken: true
|
||||
hostNetwork: true
|
||||
hostPID: true
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
- name: hostpath-init
|
||||
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}
|
||||
@@ -35,7 +39,9 @@ spec:
|
||||
command:
|
||||
- sh
|
||||
- -xec
|
||||
- iptables -V
|
||||
- |
|
||||
chmod +t /usr/local/sbin
|
||||
iptables -V
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: true
|
||||
capabilities:
|
||||
@@ -60,16 +66,21 @@ spec:
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
command:
|
||||
- /kube-ovn/install-cni.sh
|
||||
- --cni-conf-dir={{ .Values.cni_conf.CNI_CONF_DIR }}
|
||||
- --cni-conf-dir={{ .Values.cni_conf.MOUNT_CNI_CONF_DIR }}
|
||||
- --cni-conf-file={{ .Values.cni_conf.CNI_CONF_FILE }}
|
||||
- --cni-conf-name={{- .Values.cni_conf.CNI_CONFIG_PRIORITY -}}-kube-ovn.conflist
|
||||
env:
|
||||
- name: POD_IPS
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: status.podIPs
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- mountPath: /opt/cni/bin
|
||||
name: cni-bin
|
||||
- mountPath: /etc/cni/net.d
|
||||
- mountPath: {{ .Values.cni_conf.MOUNT_CNI_CONF_DIR }}
|
||||
name: cni-conf
|
||||
{{- if .Values.cni_conf.MOUNT_LOCAL_BIN_DIR }}
|
||||
- mountPath: /usr/local/bin
|
||||
|
||||
@@ -34,8 +34,12 @@ spec:
|
||||
operator: Exists
|
||||
priorityClassName: system-node-critical
|
||||
serviceAccountName: ovn-ovs
|
||||
automountServiceAccountToken: true
|
||||
hostNetwork: true
|
||||
hostPID: true
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
- name: hostpath-init
|
||||
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}
|
||||
@@ -44,6 +48,7 @@ spec:
|
||||
- sh
|
||||
- -xec
|
||||
- |
|
||||
chmod +t /usr/local/sbin
|
||||
chown -R nobody: /var/run/ovn /var/log/ovn /etc/openvswitch /var/run/openvswitch /var/log/openvswitch
|
||||
iptables -V
|
||||
{{- if not .Values.DISABLE_MODULES_MANAGEMENT }}
|
||||
|
||||
@@ -28,7 +28,11 @@ spec:
|
||||
- key: CriticalAddonsOnly
|
||||
operator: Exists
|
||||
serviceAccountName: kube-ovn-app
|
||||
hostPID: true
|
||||
automountServiceAccountToken: true
|
||||
hostPID: false
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
- name: hostpath-init
|
||||
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}
|
||||
|
||||
@@ -9,6 +9,7 @@ metadata:
|
||||
"helm.sh/hook": post-delete
|
||||
"helm.sh/hook-weight": "1"
|
||||
"helm.sh/hook-delete-policy": hook-succeeded
|
||||
automountServiceAccountToken: false
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
@@ -102,8 +103,11 @@ spec:
|
||||
hostNetwork: true
|
||||
nodeSelector:
|
||||
kubernetes.io/os: "linux"
|
||||
serviceAccount: kube-ovn-post-delete-hook
|
||||
serviceAccountName: kube-ovn-post-delete-hook
|
||||
automountServiceAccountToken: true
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: remove-subnet-finalizer
|
||||
image: "{{ .Values.global.registry.address}}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}"
|
||||
|
||||
@@ -11,6 +11,7 @@ metadata:
|
||||
"helm.sh/hook": post-upgrade
|
||||
"helm.sh/hook-weight": "1"
|
||||
"helm.sh/hook-delete-policy": hook-succeeded
|
||||
automountServiceAccountToken: false
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
@@ -133,8 +134,11 @@ spec:
|
||||
hostNetwork: true
|
||||
nodeSelector:
|
||||
kubernetes.io/os: "linux"
|
||||
serviceAccount: ovs-ovn-upgrade
|
||||
serviceAccountName: ovs-ovn-upgrade
|
||||
automountServiceAccountToken: true
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: ovs-ovn-upgrade
|
||||
image: "{{ .Values.global.registry.address}}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}"
|
||||
|
||||
@@ -9,7 +9,7 @@ global:
|
||||
kubeovn:
|
||||
repository: kube-ovn
|
||||
vpcRepository: vpc-nat-gateway
|
||||
tag: v1.14.11
|
||||
tag: v1.14.25
|
||||
support_arm: true
|
||||
thirdparty: true
|
||||
|
||||
@@ -111,6 +111,7 @@ debug:
|
||||
cni_conf:
|
||||
CNI_CONFIG_PRIORITY: "01"
|
||||
CNI_CONF_DIR: "/etc/cni/net.d"
|
||||
MOUNT_CNI_CONF_DIR: "/etc/cni/net.d"
|
||||
CNI_BIN_DIR: "/opt/cni/bin"
|
||||
CNI_CONF_FILE: "/kube-ovn/01-kube-ovn.conflist"
|
||||
LOCAL_BIN_DIR: "/usr/local/bin"
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
# syntax = docker/dockerfile:experimental
|
||||
ARG VERSION=v1.14.11
|
||||
ARG BASE_TAG=$VERSION
|
||||
|
||||
FROM golang:1.25-bookworm as builder
|
||||
|
||||
ARG TAG=v1.14.11
|
||||
RUN git clone --branch ${TAG} --depth 1 https://github.com/kubeovn/kube-ovn /source
|
||||
|
||||
WORKDIR /source
|
||||
|
||||
COPY patches /patches
|
||||
RUN git apply /patches/*.diff
|
||||
RUN make build-go
|
||||
|
||||
WORKDIR /source/dist/images
|
||||
|
||||
# imported from https://github.com/kubeovn/kube-ovn/blob/master/dist/images/Dockerfile
|
||||
FROM kubeovn/kube-ovn-base:$BASE_TAG AS setcap
|
||||
|
||||
COPY --from=builder /source/dist/images/*.sh /kube-ovn/
|
||||
COPY --from=builder /source/dist/images/kubectl-ko /kube-ovn/kubectl-ko
|
||||
COPY --from=builder /source/dist/images/01-kube-ovn.conflist /kube-ovn/01-kube-ovn.conflist
|
||||
|
||||
COPY --from=builder /source/dist/images/kube-ovn /kube-ovn/kube-ovn
|
||||
COPY --from=builder /source/dist/images/kube-ovn-cmd /kube-ovn/kube-ovn-cmd
|
||||
COPY --from=builder /source/dist/images/kube-ovn-daemon /kube-ovn/kube-ovn-daemon
|
||||
COPY --from=builder /source/dist/images/kube-ovn-controller /kube-ovn/kube-ovn-controller
|
||||
RUN ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-monitor && \
|
||||
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-speaker && \
|
||||
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-webhook && \
|
||||
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-leader-checker && \
|
||||
ln -s /kube-ovn/kube-ovn-cmd /kube-ovn/kube-ovn-ic-controller && \
|
||||
ln -s /kube-ovn/kube-ovn-controller /kube-ovn/kube-ovn-pinger && \
|
||||
setcap CAP_NET_BIND_SERVICE+eip /kube-ovn/kube-ovn-cmd && \
|
||||
setcap CAP_NET_RAW,CAP_NET_BIND_SERVICE+eip /kube-ovn/kube-ovn-controller && \
|
||||
setcap CAP_NET_ADMIN,CAP_NET_RAW,CAP_NET_BIND_SERVICE,CAP_SYS_ADMIN+eip /kube-ovn/kube-ovn-daemon
|
||||
|
||||
FROM kubeovn/kube-ovn-base:$BASE_TAG
|
||||
|
||||
COPY --chmod=0644 --from=builder /source/dist/images/logrotate/* /etc/logrotate.d/
|
||||
COPY --from=builder /source/dist/images/grace_stop_ovn_controller /usr/share/ovn/scripts/grace_stop_ovn_controller
|
||||
|
||||
COPY --from=setcap /kube-ovn /kube-ovn
|
||||
RUN /kube-ovn/iptables-wrapper-installer.sh --no-sanity-check
|
||||
|
||||
WORKDIR /kube-ovn
|
||||
@@ -1,109 +0,0 @@
|
||||
diff --git a/pkg/ovn_leader_checker/ovn.go b/pkg/ovn_leader_checker/ovn.go
|
||||
index 0f86a371d..8ddf7bca6 100755
|
||||
--- a/pkg/ovn_leader_checker/ovn.go
|
||||
+++ b/pkg/ovn_leader_checker/ovn.go
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
+ "sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -271,19 +272,56 @@ func checkNorthdSvcExist(cfg *Configuration, namespace, svcName string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
-func checkNorthdEpAvailable(ip string) bool {
|
||||
- address := net.JoinHostPort(ip, OvnNorthdPort)
|
||||
- conn, err := net.DialTimeout("tcp", address, 3*time.Second)
|
||||
- if err != nil {
|
||||
- klog.Errorf("failed to connect to northd leader %s, err: %v", ip, err)
|
||||
- failCount++
|
||||
- if failCount >= MaxFailCount {
|
||||
- return false
|
||||
- }
|
||||
- } else {
|
||||
+func checkNorthdEpAvailable(ips ...string) bool {
|
||||
+ var wg sync.WaitGroup
|
||||
+ success := make(chan struct{}, 1)
|
||||
+ failure := make(chan struct{})
|
||||
+ ctx, cancel := context.WithCancel(context.Background())
|
||||
+ defer cancel()
|
||||
+ d := net.Dialer{Timeout: 3 * time.Second}
|
||||
+ for _, ip := range ips {
|
||||
+ address := net.JoinHostPort(ip, OvnNorthdPort)
|
||||
+ wg.Add(1)
|
||||
+ go func() {
|
||||
+ defer wg.Done()
|
||||
+ conn, err := d.DialContext(ctx, "tcp", address)
|
||||
+ if err != nil {
|
||||
+ klog.Errorf("failed to connect to northd leader %s, err: %v", ip, err)
|
||||
+ return
|
||||
+ } else {
|
||||
+ defer conn.Close()
|
||||
+ select {
|
||||
+ case success <- struct{}{}:
|
||||
+ klog.V(5).Infof("succeed to connect to northd leader %s", ip)
|
||||
+ cancel()
|
||||
+ default:
|
||||
+ // someone else already succeeded
|
||||
+ }
|
||||
+ }
|
||||
+ }()
|
||||
+ }
|
||||
+ go func() {
|
||||
+ wg.Wait()
|
||||
+ close(failure)
|
||||
+ }()
|
||||
+ select {
|
||||
+ case <-success:
|
||||
failCount = 0
|
||||
- klog.V(5).Infof("succeed to connect to northd leader %s", ip)
|
||||
- _ = conn.Close()
|
||||
+ return true
|
||||
+ case <-failure:
|
||||
+ // if the last groroutine is the one to succeed,
|
||||
+ // there's a small chance that failure is selected,
|
||||
+ // this is a guard for this
|
||||
+ select {
|
||||
+ case <-success:
|
||||
+ failCount = 0
|
||||
+ return true
|
||||
+ default:
|
||||
+ failCount++
|
||||
+ if failCount >= MaxFailCount {
|
||||
+ return false
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -295,6 +333,8 @@ func checkNorthdEpAlive(cfg *Configuration, namespace, service string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
+ addresses := []string{}
|
||||
+
|
||||
for _, eps := range epsList.Items {
|
||||
for _, ep := range eps.Endpoints {
|
||||
if (ep.Conditions.Ready != nil && !*ep.Conditions.Ready) || len(ep.Addresses) == 0 {
|
||||
@@ -303,12 +343,15 @@ func checkNorthdEpAlive(cfg *Configuration, namespace, service string) bool {
|
||||
|
||||
// Found an address, check its availability. We only need one.
|
||||
klog.V(5).Infof("found address %s in endpoint slice %s/%s for service %s, checking availability", ep.Addresses[0], eps.Namespace, eps.Name, service)
|
||||
- return checkNorthdEpAvailable(ep.Addresses[0])
|
||||
+ addresses = append(addresses, ep.Addresses[0]) // Addresses are fungible by k8s API design
|
||||
}
|
||||
}
|
||||
|
||||
- klog.V(5).Infof("no address found in any endpoint slices for service %s/%s", namespace, service)
|
||||
- return false
|
||||
+ if len(addresses) == 0 {
|
||||
+ klog.V(5).Infof("no address found in any endpoint slices for service %s/%s", namespace, service)
|
||||
+ return false
|
||||
+ }
|
||||
+ return checkNorthdEpAvailable(addresses...)
|
||||
}
|
||||
|
||||
func compactOvnDatabase(db string) {
|
||||
@@ -1,103 +0,0 @@
|
||||
|
||||
diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml
|
||||
index d9a9a67..b2e12dd 100644
|
||||
--- a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml
|
||||
+++ b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml
|
||||
@@ -51,18 +51,15 @@ spec:
|
||||
- bash
|
||||
- /kube-ovn/start-cniserver.sh
|
||||
args:
|
||||
+ {{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }}
|
||||
- --enable-mirror={{- .Values.debug.ENABLE_MIRROR }}
|
||||
- --mirror-iface={{- .Values.debug.MIRROR_IFACE }}
|
||||
- --node-switch={{ .Values.networking.NODE_SUBNET }}
|
||||
- --encap-checksum=true
|
||||
- - --service-cluster-ip-range=
|
||||
- {{- if eq .Values.networking.NET_STACK "dual_stack" -}}
|
||||
- {{ .Values.dual_stack.SVC_CIDR }}
|
||||
- {{- else if eq .Values.networking.NET_STACK "ipv4" -}}
|
||||
- {{ .Values.ipv4.SVC_CIDR }}
|
||||
- {{- else if eq .Values.networking.NET_STACK "ipv6" -}}
|
||||
- {{ .Values.ipv6.SVC_CIDR }}
|
||||
- {{- end }}
|
||||
+ - --service-cluster-ip-range={{ index $cozyConfig.data "ipv4-svc-cidr" }}
|
||||
+ {{- if .Values.global.logVerbosity }}
|
||||
+ - --v={{ .Values.global.logVerbosity }}
|
||||
+ {{- end }}
|
||||
{{- if eq .Values.networking.NETWORK_TYPE "vlan" }}
|
||||
- --iface=
|
||||
{{- else}}
|
||||
diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml
|
||||
index 0e69494..756eb7c 100644
|
||||
--- a/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml
|
||||
+++ b/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml
|
||||
@@ -52,46 +52,22 @@ spec:
|
||||
image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
args:
|
||||
+ {{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }}
|
||||
- /kube-ovn/start-controller.sh
|
||||
- --default-ls={{ .Values.networking.DEFAULT_SUBNET }}
|
||||
- - --default-cidr=
|
||||
- {{- if eq .Values.networking.NET_STACK "dual_stack" -}}
|
||||
- {{ .Values.dual_stack.POD_CIDR }}
|
||||
- {{- else if eq .Values.networking.NET_STACK "ipv4" -}}
|
||||
- {{ .Values.ipv4.POD_CIDR }}
|
||||
- {{- else if eq .Values.networking.NET_STACK "ipv6" -}}
|
||||
- {{ .Values.ipv6.POD_CIDR }}
|
||||
- {{- end }}
|
||||
- - --default-gateway=
|
||||
- {{- if eq .Values.networking.NET_STACK "dual_stack" -}}
|
||||
- {{ .Values.dual_stack.POD_GATEWAY }}
|
||||
- {{- else if eq .Values.networking.NET_STACK "ipv4" -}}
|
||||
- {{ .Values.ipv4.POD_GATEWAY }}
|
||||
- {{- else if eq .Values.networking.NET_STACK "ipv6" -}}
|
||||
- {{ .Values.ipv6.POD_GATEWAY }}
|
||||
- {{- end }}
|
||||
+ - --default-cidr={{ index $cozyConfig.data "ipv4-pod-cidr" }}
|
||||
+ - --default-gateway={{ index $cozyConfig.data "ipv4-pod-gateway" }}
|
||||
- --default-gateway-check={{- .Values.func.CHECK_GATEWAY }}
|
||||
- --default-logical-gateway={{- .Values.func.LOGICAL_GATEWAY }}
|
||||
- --default-u2o-interconnection={{- .Values.func.U2O_INTERCONNECTION }}
|
||||
- --default-exclude-ips={{- .Values.networking.EXCLUDE_IPS }}
|
||||
- --cluster-router={{ .Values.networking.DEFAULT_VPC }}
|
||||
- --node-switch={{ .Values.networking.NODE_SUBNET }}
|
||||
- - --node-switch-cidr=
|
||||
- {{- if eq .Values.networking.NET_STACK "dual_stack" -}}
|
||||
- {{ .Values.dual_stack.JOIN_CIDR }}
|
||||
- {{- else if eq .Values.networking.NET_STACK "ipv4" -}}
|
||||
- {{ .Values.ipv4.JOIN_CIDR }}
|
||||
- {{- else if eq .Values.networking.NET_STACK "ipv6" -}}
|
||||
- {{ .Values.ipv6.JOIN_CIDR }}
|
||||
- {{- end }}
|
||||
- - --service-cluster-ip-range=
|
||||
- {{- if eq .Values.networking.NET_STACK "dual_stack" -}}
|
||||
- {{ .Values.dual_stack.SVC_CIDR }}
|
||||
- {{- else if eq .Values.networking.NET_STACK "ipv4" -}}
|
||||
- {{ .Values.ipv4.SVC_CIDR }}
|
||||
- {{- else if eq .Values.networking.NET_STACK "ipv6" -}}
|
||||
- {{ .Values.ipv6.SVC_CIDR }}
|
||||
- {{- end }}
|
||||
+ - --node-switch-cidr={{ index $cozyConfig.data "ipv4-join-cidr" }}
|
||||
+ - --service-cluster-ip-range={{ index $cozyConfig.data "ipv4-svc-cidr" }}
|
||||
+ {{- if .Values.global.logVerbosity }}
|
||||
+ - --v={{ .Values.global.logVerbosity }}
|
||||
+ {{- end }}
|
||||
- --network-type={{- .Values.networking.NETWORK_TYPE }}
|
||||
- --default-provider-name={{ .Values.networking.vlan.PROVIDER_NAME }}
|
||||
- --default-interface-name={{- .Values.networking.vlan.VLAN_INTERFACE_NAME }}
|
||||
diff --git a/packages/system/kubeovn/charts/kube-ovn/values.yaml b/packages/system/kubeovn/charts/kube-ovn/values.yaml
|
||||
index bfffc4d..b880749 100644
|
||||
--- a/packages/system/kubeovn/charts/kube-ovn/values.yaml
|
||||
+++ b/packages/system/kubeovn/charts/kube-ovn/values.yaml
|
||||
@@ -70,10 +70,6 @@ func:
|
||||
ENABLE_TPROXY: false
|
||||
|
||||
ipv4:
|
||||
- POD_CIDR: "10.16.0.0/16"
|
||||
- POD_GATEWAY: "10.16.0.1"
|
||||
- SVC_CIDR: "10.96.0.0/12"
|
||||
- JOIN_CIDR: "100.64.0.0/16"
|
||||
PINGER_EXTERNAL_ADDRESS: "1.1.1.1"
|
||||
PINGER_EXTERNAL_DOMAIN: "alauda.cn."
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml
|
||||
index 63f4258..dafe1fd 100644
|
||||
--- a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml
|
||||
+++ b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml
|
||||
@@ -112,6 +112,9 @@ spec:
|
||||
- --secure-serving={{- .Values.func.SECURE_SERVING }}
|
||||
- --enable-ovn-ipsec={{- .Values.func.ENABLE_OVN_IPSEC }}
|
||||
- --set-vxlan-tx-off={{- .Values.func.SET_VXLAN_TX_OFF }}
|
||||
+ {{- with .Values.mtu }}
|
||||
+ - --mtu={{ . }}
|
||||
+ {{- end }}
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
privileged: false
|
||||
@@ -65,4 +65,4 @@ global:
|
||||
images:
|
||||
kubeovn:
|
||||
repository: kubeovn
|
||||
tag: v1.14.11@sha256:8e6cf216687b4a80c35fa7c60bb4d511dd6aaaaf19d1ec53321dfef98d343f51
|
||||
tag: v1.14.25@sha256:d0b29daaf36e81cac0f9fb15d0ea6b1b49f1abba81a14c73b88a2e60ffcc5978
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
storageClass: replicated
|
||||
csiDriver:
|
||||
image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:d5c836ba33cf5dbed7e6f866784f668f80ffe69179e7c75847b680111984eefb
|
||||
image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:b42c6af641ee0eadb7e0a42e368021b4759f443cb7b71b7e745a64f0fc8b752e
|
||||
|
||||
@@ -27,7 +27,7 @@ spec:
|
||||
expr: |
|
||||
max_over_time(
|
||||
kubevirt_vmi_info{
|
||||
phase!="Running",
|
||||
phase!="running",
|
||||
exported_namespace=~".+",
|
||||
name=~".+"
|
||||
}[10m]
|
||||
|
||||
@@ -16,12 +16,7 @@ spec:
|
||||
nodeSelector:
|
||||
node-role.kubernetes.io/control-plane: ""
|
||||
tolerations:
|
||||
- key: "node-role.kubernetes.io/control-plane"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
- key: "node-role.kubernetes.io/master"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
- operator: Exists
|
||||
serviceAccountName: lineage-controller-webhook
|
||||
containers:
|
||||
- name: lineage-controller-webhook
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
lineageControllerWebhook:
|
||||
image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v0.38.2@sha256:a5c750a0f46e8e25329b3ee2110d5dfb077c73e473195f1ed768d28d6f43902c
|
||||
image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v0.39.5@sha256:dc5c7c990e468b291390a54f5fb5970097a1ff65ba4be6e95317e3ee7bf4430d
|
||||
debug: false
|
||||
localK8sAPIEndpoint:
|
||||
enabled: true
|
||||
|
||||
@@ -1,4 +1,23 @@
|
||||
export NAME=linstor
|
||||
export NAMESPACE=cozy-$(NAME)
|
||||
|
||||
include ../../../scripts/common-envs.mk
|
||||
include ../../../scripts/package.mk
|
||||
|
||||
LINSTOR_VERSION ?= 1.32.3
|
||||
|
||||
image:
|
||||
docker buildx build images/piraeus-server \
|
||||
--build-arg LINSTOR_VERSION=$(LINSTOR_VERSION) \
|
||||
--build-arg K8S_AWAIT_ELECTION_VERSION=v0.4.2 \
|
||||
--tag $(REGISTRY)/piraeus-server:$(call settag,$(LINSTOR_VERSION)) \
|
||||
--tag $(REGISTRY)/piraeus-server:$(call settag,$(LINSTOR_VERSION)-$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/piraeus-server:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/piraeus-server.json \
|
||||
$(BUILDX_ARGS)
|
||||
REPOSITORY="$(REGISTRY)/piraeus-server" \
|
||||
yq -i '.piraeusServer.image.repository = strenv(REPOSITORY)' values.yaml
|
||||
TAG="$(call settag,$(LINSTOR_VERSION))@$$(yq e '."containerimage.digest"' images/piraeus-server.json -o json -r)" \
|
||||
yq -i '.piraeusServer.image.tag = strenv(TAG)' values.yaml
|
||||
rm -f images/piraeus-server.json
|
||||
|
||||
@@ -10,10 +10,125 @@ trap terminate SIGINT SIGQUIT SIGTERM
|
||||
|
||||
echo "Starting Linstor per-satellite plunger"
|
||||
|
||||
INTERVAL_SEC="${INTERVAL_SEC:-30}"
|
||||
STALL_ITERS="${STALL_ITERS:-4}"
|
||||
STATE_FILE="${STATE_FILE:-/run/drbd-sync-watch.state}"
|
||||
|
||||
log() { printf '%s %s\n' "$(date -Is)" "$*" >&2; }
|
||||
|
||||
drbd_status_json() {
|
||||
drbdsetup status --json 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Detect DRBD resources where resync is stuck:
|
||||
# - at least one local device is Inconsistent
|
||||
# - there is an active SyncTarget peer
|
||||
# - there are other peers suspended with resync-suspended:dependency
|
||||
# Output format: "<resource> <sync-peer> <percent-in-sync>"
|
||||
drbd_stall_candidates() {
|
||||
jq -r '
|
||||
.[]?
|
||||
| . as $r
|
||||
| select(any($r.devices[]?; ."disk-state" == "Inconsistent"))
|
||||
| (
|
||||
[ $r.connections[]?
|
||||
| . as $c
|
||||
| $c.peer_devices[]?
|
||||
| select(."replication-state" == "SyncTarget")
|
||||
| { peer: $c.name, pct: (."percent-in-sync" // empty) }
|
||||
] | .[0]?
|
||||
) as $sync
|
||||
| select($sync != null and ($sync.pct|tostring) != "")
|
||||
| select(any($r.connections[]?.peer_devices[]?; ."resync-suspended" == "dependency"))
|
||||
| "\($r.name) \($sync.peer) \($sync.pct)"
|
||||
'
|
||||
}
|
||||
|
||||
drbd_stall_load_state() {
|
||||
[ -f "$STATE_FILE" ] && cat "$STATE_FILE" || true
|
||||
}
|
||||
|
||||
drbd_stall_save_state() {
|
||||
local tmp="${STATE_FILE}.tmp"
|
||||
cat >"$tmp"
|
||||
mv "$tmp" "$STATE_FILE"
|
||||
}
|
||||
|
||||
# Break stalled resync by disconnecting the current SyncTarget peer.
|
||||
# After reconnect, DRBD will typically pick another eligible peer and continue syncing.
|
||||
drbd_stall_act() {
|
||||
local res="$1"
|
||||
local peer="$2"
|
||||
local pct="$3"
|
||||
log "STALL detected: res=$res sync_peer=$peer percent_in_sync=$pct -> disconnect/connect"
|
||||
drbdadm disconnect "${res}:${peer}" && drbdadm connect "$res" || log "WARN: action failed for ${res}:${peer}"
|
||||
}
|
||||
|
||||
# Track percent-in-sync progress across iterations.
|
||||
# If progress does not change for STALL_ITERS loops, trigger reconnect.
|
||||
drbd_fix_stalled_sync() {
|
||||
local now prev json out
|
||||
now="$(date +%s)"
|
||||
prev="$(drbd_stall_load_state)"
|
||||
|
||||
json="$(drbd_status_json)"
|
||||
[ -n "$json" ] || return 0
|
||||
|
||||
out="$(printf '%s' "$json" | drbd_stall_candidates)"
|
||||
|
||||
local new_state=""
|
||||
local acts=""
|
||||
|
||||
while IFS= read -r line; do
|
||||
[ -n "$line" ] || continue
|
||||
set -- $line
|
||||
local res="$1" peer="$2" pct="$3"
|
||||
local key="${res} ${peer}"
|
||||
|
||||
local prev_line
|
||||
prev_line="$(printf '%s\n' "$prev" | awk -v k="$key" '$1" "$2==k {print; exit}')"
|
||||
|
||||
local cnt last_act prev_pct prev_cnt prev_act
|
||||
if [ -n "$prev_line" ]; then
|
||||
set -- $prev_line
|
||||
prev_pct="$3"
|
||||
prev_cnt="$4"
|
||||
prev_act="$5"
|
||||
if [ "$pct" = "$prev_pct" ]; then
|
||||
cnt=$((prev_cnt + 1))
|
||||
else
|
||||
cnt=1
|
||||
fi
|
||||
last_act="$prev_act"
|
||||
else
|
||||
cnt=1
|
||||
last_act=0
|
||||
fi
|
||||
|
||||
if [ "$cnt" -ge "$STALL_ITERS" ]; then
|
||||
acts="${acts}${res} ${peer} ${pct}"$'\n'
|
||||
cnt=0
|
||||
last_act="$now"
|
||||
fi
|
||||
|
||||
new_state="${new_state}${res} ${peer} ${pct} ${cnt} ${last_act}"$'\n'
|
||||
done <<< "$out"
|
||||
|
||||
if [ -n "$acts" ]; then
|
||||
while IFS= read -r a; do
|
||||
[ -n "$a" ] || continue
|
||||
set -- $a
|
||||
drbd_stall_act "$1" "$2" "$3"
|
||||
done <<< "$acts"
|
||||
fi
|
||||
|
||||
printf '%s' "$new_state" | drbd_stall_save_state
|
||||
}
|
||||
|
||||
while true; do
|
||||
|
||||
# timeout at the start of the loop to give a chance for the fresh linstor-satellite instance to cleanup itself
|
||||
sleep 30 &
|
||||
sleep "$INTERVAL_SEC" &
|
||||
pid=$!
|
||||
wait $pid
|
||||
|
||||
@@ -21,7 +136,7 @@ while true; do
|
||||
# the `/` path could not be a backing file for a loop device, so it's a good indicator of a stuck loop device
|
||||
# TODO describe the issue in more detail
|
||||
# Using the direct /usr/sbin/losetup as the linstor-satellite image has own wrapper in /usr/local
|
||||
stale_loopbacks=$(/usr/sbin/losetup --json | jq -r '.[][] | select(."back-file" == "/" or ."back-file" == "/ (deleted)").name' )
|
||||
stale_loopbacks=$(/usr/sbin/losetup --json | jq -r '.[][] | select(."back-file" == "/" or ."back-file" == "/ (deleted)").name')
|
||||
for stale_device in $stale_loopbacks; do (
|
||||
echo "Detaching stuck loop device ${stale_device}"
|
||||
set -x
|
||||
@@ -39,4 +154,7 @@ while true; do
|
||||
drbdadm up "${secondary}" || echo "Command failed"
|
||||
); done
|
||||
|
||||
# Detect and fix stalled DRBD resync by switching SyncTarget peer
|
||||
drbd_fix_stalled_sync || true
|
||||
|
||||
done
|
||||
|
||||
172
packages/system/linstor/images/piraeus-server/Dockerfile
Normal file
172
packages/system/linstor/images/piraeus-server/Dockerfile
Normal file
@@ -0,0 +1,172 @@
|
||||
ARG DISTRO=bookworm
|
||||
ARG LINSTOR_VERSION
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Build linstor-server from source
|
||||
FROM debian:bookworm AS builder
|
||||
|
||||
ARG LINSTOR_VERSION
|
||||
ARG VERSION=${LINSTOR_VERSION}
|
||||
ARG DISTRO
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update \
|
||||
&& apt-get -y upgrade \
|
||||
&& apt-get -y install build-essential git default-jdk-headless python3-all debhelper wget unzip && \
|
||||
wget https://services.gradle.org/distributions/gradle-8.9-bin.zip -O /tmp/gradle.zip && \
|
||||
unzip -d /opt /tmp/gradle.zip && \
|
||||
rm /tmp/gradle.zip && \
|
||||
ln -s /opt/gradle-8.9/bin/gradle /usr/local/bin/gradle
|
||||
|
||||
RUN git clone https://github.com/LINBIT/linstor-server.git /linstor-server
|
||||
WORKDIR /linstor-server
|
||||
RUN git checkout v${VERSION}
|
||||
|
||||
# Apply patches
|
||||
COPY patches /patches
|
||||
RUN git apply /patches/*.diff && \
|
||||
git config user.email "build@cozystack.io" && \
|
||||
git config user.name "Cozystack Builder" && \
|
||||
git add -A && \
|
||||
git commit -m "Apply patches"
|
||||
|
||||
# Initialize git submodules
|
||||
RUN git submodule update --init --recursive || make check-submods
|
||||
|
||||
# Pre-download ALL dependencies before make tarball
|
||||
# This ensures all transitive dependencies are cached, including optional ones like AWS SDK
|
||||
RUN ./gradlew getProtoc
|
||||
RUN ./gradlew generateJava
|
||||
RUN ./gradlew --no-daemon --gradle-user-home .gradlehome downloadDependencies
|
||||
|
||||
# Manually create tarball without removing caches
|
||||
# make tarball removes .gradlehome/caches/[0-9]* which deletes dependencies
|
||||
# So we'll do the steps manually but keep the caches
|
||||
RUN make check-submods versioninfo gen-java FORCE=1 VERSION=${VERSION}
|
||||
RUN make server/jar.deps controller/jar.deps satellite/jar.deps jclcrypto/jar.deps FORCE=1 VERSION=${VERSION}
|
||||
RUN make .filelist FORCE=1 VERSION=${VERSION} PRESERVE_DEBIAN=1
|
||||
# Don't remove caches - we need them for offline build
|
||||
RUN rm -Rf .gradlehome/wrapper .gradlehome/native .gradlehome/.tmp || true
|
||||
RUN mkdir -p ./libs
|
||||
RUN make tgz VERSION=${VERSION}
|
||||
|
||||
# Extract tarball and build DEB packages from it
|
||||
RUN mv linstor-server-${VERSION}.tar.gz /linstor-server_${VERSION}.orig.tar.gz \
|
||||
&& tar -C / -xvf /linstor-server_${VERSION}.orig.tar.gz
|
||||
|
||||
WORKDIR /linstor-server-${VERSION}
|
||||
# Verify .gradlehome is present in extracted tarball
|
||||
RUN test -d .gradlehome && echo ".gradlehome found in tarball" || (echo ".gradlehome not found in tarball!" && exit 1)
|
||||
|
||||
# Build DEB packages from tarball
|
||||
# Override GRADLE_FLAGS to remove --offline flag, allowing Gradle to download missing dependencies
|
||||
RUN sed -i 's/GRADLE_FLAGS = --offline/GRADLE_FLAGS =/' debian/rules || true
|
||||
RUN LD_LIBRARY_PATH='' dpkg-buildpackage -rfakeroot -b -uc
|
||||
|
||||
# Copy built .deb packages to a location accessible from final image
|
||||
# dpkg-buildpackage creates packages in parent directory
|
||||
RUN mkdir -p /packages-output && \
|
||||
find .. -maxdepth 1 -name "linstor-*.deb" -exec cp {} /packages-output/ \; && \
|
||||
test -n "$(ls -A /packages-output)" || (echo "ERROR: No linstor .deb packages found after build." && exit 1)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Final image
|
||||
FROM debian:${DISTRO}
|
||||
|
||||
LABEL maintainer="Roland Kammerer <roland.kammerer@linbit.com>"
|
||||
|
||||
ARG LINSTOR_VERSION
|
||||
ARG DISTRO
|
||||
|
||||
# Copy built .deb packages from builder stage
|
||||
# dpkg-buildpackage creates packages in parent directory, we copied them to /packages-output
|
||||
COPY --from=builder /packages-output/ /packages/
|
||||
|
||||
RUN { echo 'APT::Install-Recommends "false";' ; echo 'APT::Install-Suggests "false";' ; } > /etc/apt/apt.conf.d/99_piraeus
|
||||
|
||||
RUN --mount=type=cache,target=/var/cache,sharing=private \
|
||||
--mount=type=cache,target=/var/lib/apt/lists,sharing=private \
|
||||
--mount=type=tmpfs,target=/var/log \
|
||||
# Install wget first for downloading keyring
|
||||
apt-get update && apt-get install -y wget ca-certificates && \
|
||||
# Enable contrib repos for zfsutils \
|
||||
. /etc/os-release && \
|
||||
sed -i -r 's/^Components: (.*)$/Components: \1 contrib/' /etc/apt/sources.list.d/debian.sources && \
|
||||
echo "deb http://deb.debian.org/debian $VERSION_CODENAME-backports contrib" > /etc/apt/sources.list.d/backports.list && \
|
||||
wget https://packages.linbit.com/public/linbit-keyring.deb -O /var/cache/linbit-keyring.deb && \
|
||||
dpkg -i /var/cache/linbit-keyring.deb && \
|
||||
echo "deb http://packages.linbit.com/public $VERSION_CODENAME misc" > /etc/apt/sources.list.d/linbit.list && \
|
||||
apt-get update && \
|
||||
# Install useful utilities and general dependencies
|
||||
apt-get install -y udev drbd-utils jq net-tools iputils-ping iproute2 dnsutils netcat-traditional sysstat curl util-linux && \
|
||||
# Install dependencies for optional features \
|
||||
apt-get install -y \
|
||||
# cryptsetup: luks layer
|
||||
cryptsetup \
|
||||
# e2fsprogs: LINSTOR can create file systems \
|
||||
e2fsprogs \
|
||||
# lsscsi: exos layer \
|
||||
lsscsi \
|
||||
# lvm2: manage lvm storage pools \
|
||||
lvm2 \
|
||||
# multipath-tools: exos layer \
|
||||
multipath-tools \
|
||||
# nvme-cli: nvme layer
|
||||
nvme-cli \
|
||||
# procps: used by LINSTOR to find orphaned send/receive processes \
|
||||
procps \
|
||||
# socat: used with thin-send-recv to send snapshots to another LINSTOR cluster
|
||||
socat \
|
||||
# thin-send-recv: used to send/receive snapshots of LVM thin volumes \
|
||||
thin-send-recv \
|
||||
# xfsprogs: LINSTOR can create file systems; xfs deps \
|
||||
xfsprogs \
|
||||
# zstd: used with thin-send-recv to send snapshots to another LINSTOR cluster \
|
||||
zstd \
|
||||
# zfsutils-linux: for zfs storage pools \
|
||||
zfsutils-linux/$VERSION_CODENAME-backports \
|
||||
&& \
|
||||
# remove udev, no need for it in the container \
|
||||
apt-get remove -y udev && \
|
||||
# Install linstor packages from built .deb files and linstor-client from repository
|
||||
apt-get install -y default-jre-headless python3-all python3-natsort linstor-client \
|
||||
&& ls packages/*.deb >/dev/null && (dpkg -i packages/*.deb || apt-get install -f -y) \
|
||||
&& rm -rf /packages \
|
||||
&& sed -i 's/"-Djdk.tls.acknowledgeCloseNotify=true"//g' /usr/share/linstor-server/bin/Controller \
|
||||
&& apt-get clean
|
||||
|
||||
# Log directory need to be group writable. OpenShift assigns random UID and GID, without extra RBAC changes we can only influence the GID.
|
||||
RUN mkdir /var/log/linstor-controller && \
|
||||
chown 0:1000 /var/log/linstor-controller && \
|
||||
chmod -R 0775 /var/log/linstor-controller && \
|
||||
# Ensure we log to files in containers, otherwise SOS reports won't show any logs at all
|
||||
sed -i 's#<!-- <appender-ref ref="FILE" /> -->#<appender-ref ref="FILE" />#' /usr/share/linstor-server/lib/conf/logback.xml
|
||||
|
||||
|
||||
RUN lvmconfig --type current --mergedconfig --config 'activation { udev_sync = 0 udev_rules = 0 monitoring = 0 } devices { global_filter = [ "r|^/dev/drbd|" ] obtain_device_list_from_udev = 0}' > /etc/lvm/lvm.conf.new && mv /etc/lvm/lvm.conf.new /etc/lvm/lvm.conf
|
||||
RUN echo 'global { usage-count no; }' > /etc/drbd.d/global_common.conf
|
||||
|
||||
# controller
|
||||
EXPOSE 3376/tcp 3377/tcp 3370/tcp 3371/tcp
|
||||
|
||||
# satellite
|
||||
EXPOSE 3366/tcp 3367/tcp
|
||||
|
||||
RUN wget https://raw.githubusercontent.com/piraeusdatastore/piraeus/refs/heads/master/dockerfiles/piraeus-server/entry.sh -O /usr/bin/piraeus-entry.sh \
|
||||
&& chmod +x /usr/bin/piraeus-entry.sh
|
||||
|
||||
ARG K8S_AWAIT_ELECTION_VERSION=v0.4.2
|
||||
# TARGETARCH is a docker special variable: https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
|
||||
ARG TARGETARCH
|
||||
|
||||
RUN wget https://github.com/LINBIT/k8s-await-election/releases/download/${K8S_AWAIT_ELECTION_VERSION}/k8s-await-election-${K8S_AWAIT_ELECTION_VERSION}-linux-${TARGETARCH}.tar.gz -O - | tar -xvz -C /usr/bin/
|
||||
|
||||
ARG LOSETUP_CONTAINER_VERSION=v1.0.1
|
||||
RUN wget "https://github.com/LINBIT/losetup-container/releases/download/${LOSETUP_CONTAINER_VERSION}/losetup-container-$(uname -m)-unknown-linux-gnu.tar.gz" -O - | tar -xvz -C /usr/local/sbin && \
|
||||
printf '#!/bin/sh\nLOSETUP_CONTAINER_ORIGINAL_LOSETUP=%s exec /usr/local/sbin/losetup-container "$@"\n' $(command -v losetup) > /usr/local/sbin/losetup && \
|
||||
chmod +x /usr/local/sbin/losetup
|
||||
|
||||
RUN wget "https://dl.k8s.io/$(wget -O - https://dl.k8s.io/release/stable.txt)/bin/linux/${TARGETARCH}/kubectl" -O /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl
|
||||
|
||||
CMD ["startSatellite"]
|
||||
ENTRYPOINT ["/usr/bin/k8s-await-election", "/usr/bin/piraeus-entry.sh"]
|
||||
@@ -0,0 +1,14 @@
|
||||
# LINSTOR Server Patches
|
||||
|
||||
Custom patches for piraeus-server (linstor-server) v1.32.3.
|
||||
|
||||
- **adjust-on-resfile-change.diff** — Use actual device path in res file during toggle-disk; fix LUKS data offset
|
||||
- Upstream: [#473](https://github.com/LINBIT/linstor-server/pull/473), [#472](https://github.com/LINBIT/linstor-server/pull/472)
|
||||
- **allow-toggle-disk-retry.diff** — Allow retry and cancellation of failed toggle-disk operations
|
||||
- Upstream: [#475](https://github.com/LINBIT/linstor-server/pull/475)
|
||||
- **force-metadata-check-on-disk-add.diff** — Create metadata during toggle-disk from diskless to diskful
|
||||
- Upstream: [#474](https://github.com/LINBIT/linstor-server/pull/474)
|
||||
- **fix-duplicate-tcp-ports.diff** — Prevent duplicate TCP ports after toggle-disk operations
|
||||
- Upstream: [#476](https://github.com/LINBIT/linstor-server/pull/476)
|
||||
- **skip-adjust-when-device-inaccessible.diff** — Fix resources stuck in StandAlone after reboot, Unknown state race condition, and encrypted resource deletion
|
||||
- Upstream: [#477](https://github.com/LINBIT/linstor-server/pull/477)
|
||||
@@ -0,0 +1,48 @@
|
||||
diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/ConfFileBuilder.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/ConfFileBuilder.java
|
||||
index 36c52ccf8..c0bb7b967 100644
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/ConfFileBuilder.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/utils/ConfFileBuilder.java
|
||||
@@ -894,12 +894,16 @@ public class ConfFileBuilder
|
||||
if (((Volume) vlmData.getVolume()).getFlags().isUnset(localAccCtx, Volume.Flags.DELETE))
|
||||
{
|
||||
final String disk;
|
||||
+ // Check if we're in toggle-disk operation (adding disk to diskless resource)
|
||||
+ boolean isDiskAdding = vlmData.getVolume().getAbsResource().getStateFlags().isSomeSet(
|
||||
+ localAccCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING);
|
||||
if ((!isPeerRsc && vlmData.getDataDevice() == null) ||
|
||||
(isPeerRsc &&
|
||||
// FIXME: vlmData.getRscLayerObject().getFlags should be used here
|
||||
vlmData.getVolume().getAbsResource().disklessForDrbdPeers(accCtx)
|
||||
) ||
|
||||
- (!isPeerRsc &&
|
||||
+ // For toggle-disk: if dataDevice is set and we're adding disk, use the actual device
|
||||
+ (!isPeerRsc && !isDiskAdding &&
|
||||
// FIXME: vlmData.getRscLayerObject().getFlags should be used here
|
||||
vlmData.getVolume().getAbsResource().isDrbdDiskless(accCtx)
|
||||
)
|
||||
diff --git a/satellite/src/main/java/com/linbit/linstor/layer/luks/CryptSetupCommands.java b/satellite/src/main/java/com/linbit/linstor/layer/luks/CryptSetupCommands.java
|
||||
index 54dd5c19f..018de58cf 100644
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/layer/luks/CryptSetupCommands.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/layer/luks/CryptSetupCommands.java
|
||||
@@ -34,6 +34,9 @@ public class CryptSetupCommands implements Luks
|
||||
private static final Version V2_1_0 = new Version(2, 1, 0);
|
||||
private static final Version V2_0_0 = new Version(2, 0, 0);
|
||||
private static final String PBDKF_MAX_MEMORY_KIB = "262144"; // 256 MiB
|
||||
+ // Fixed LUKS2 data offset in 512-byte sectors (16 MiB = 32768 sectors)
|
||||
+ // This ensures consistent LUKS header size across all nodes regardless of system defaults
|
||||
+ private static final String LUKS2_DATA_OFFSET_SECTORS = "32768";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final ErrorReporter errorReporter;
|
||||
@@ -78,6 +81,11 @@ public class CryptSetupCommands implements Luks
|
||||
command.add(CRYPTSETUP);
|
||||
command.add("-q");
|
||||
command.add("luksFormat");
|
||||
+ // Always specify explicit offset to ensure consistent LUKS header size across all nodes
|
||||
+ // Without this, different systems may create LUKS with different header sizes (16MiB vs 32MiB)
|
||||
+ // which causes "Low.dev. smaller than requested DRBD-dev. size" errors during toggle-disk
|
||||
+ command.add("--offset");
|
||||
+ command.add(LUKS2_DATA_OFFSET_SECTORS);
|
||||
if (version.greaterOrEqual(V2_0_0))
|
||||
{
|
||||
command.add("--pbkdf-memory");
|
||||
@@ -0,0 +1,229 @@
|
||||
diff --git a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java
|
||||
index d93a18014..cc8ce4f04 100644
|
||||
--- a/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java
|
||||
+++ b/controller/src/main/java/com/linbit/linstor/core/apicallhandler/controller/CtrlRscToggleDiskApiCallHandler.java
|
||||
@@ -57,7 +57,9 @@ import com.linbit.linstor.stateflags.StateFlags;
|
||||
import com.linbit.linstor.storage.StorageException;
|
||||
import com.linbit.linstor.storage.data.adapter.drbd.DrbdRscData;
|
||||
import com.linbit.linstor.storage.interfaces.categories.resource.AbsRscLayerObject;
|
||||
+import com.linbit.linstor.storage.interfaces.categories.resource.VlmProviderObject;
|
||||
import com.linbit.linstor.storage.kinds.DeviceLayerKind;
|
||||
+import com.linbit.linstor.storage.kinds.DeviceProviderKind;
|
||||
import com.linbit.linstor.storage.utils.LayerUtils;
|
||||
import com.linbit.linstor.tasks.AutoDiskfulTask;
|
||||
import com.linbit.linstor.utils.layer.LayerRscUtils;
|
||||
@@ -387,21 +389,84 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
|
||||
Resource rsc = ctrlApiDataLoader.loadRsc(nodeName, rscName, true);
|
||||
|
||||
+ // Allow retry of the same operation if the previous attempt failed
|
||||
+ // (the requested flag remains set for retry on reconnection, but we should also allow manual retry)
|
||||
+ // Also allow cancellation of a failed operation by requesting the opposite operation
|
||||
if (hasDiskAddRequested(rsc))
|
||||
{
|
||||
- throw new ApiRcException(ApiCallRcImpl.simpleEntry(
|
||||
- ApiConsts.FAIL_RSC_BUSY,
|
||||
- "Addition of disk to resource already requested",
|
||||
- true
|
||||
- ));
|
||||
+ if (removeDisk)
|
||||
+ {
|
||||
+ // User wants to cancel the failed add-disk operation and go back to diskless
|
||||
+ // Use the existing disk removal flow to properly cleanup storage on satellite
|
||||
+ errorReporter.logInfo(
|
||||
+ "Toggle Disk cancel on %s/%s - cancelling failed DISK_ADD_REQUESTED, reverting to diskless",
|
||||
+ nodeNameStr, rscNameStr);
|
||||
+ unmarkDiskAddRequested(rsc);
|
||||
+ // Also clear DISK_ADDING if it was set
|
||||
+ unmarkDiskAdding(rsc);
|
||||
+
|
||||
+ // Set storage pool to diskless pool (overwrite the diskful pool that was set)
|
||||
+ Props rscProps = ctrlPropsHelper.getProps(rsc);
|
||||
+ rscProps.map().put(ApiConsts.KEY_STOR_POOL_NAME, LinStor.DISKLESS_STOR_POOL_NAME);
|
||||
+
|
||||
+ // Set DISK_REMOVE_REQUESTED to use the existing disk removal flow
|
||||
+ // This will:
|
||||
+ // 1. updateAndAdjustDisk sets DISK_REMOVING flag
|
||||
+ // 2. Satellite sees DISK_REMOVING and deletes LUKS/storage devices
|
||||
+ // 3. finishOperation rebuilds layer stack as diskless
|
||||
+ // We keep the existing layer data so satellite can properly cleanup
|
||||
+ markDiskRemoveRequested(rsc);
|
||||
+
|
||||
+ ctrlTransactionHelper.commit();
|
||||
+
|
||||
+ // Use existing disk removal flow - this will properly cleanup storage on satellite
|
||||
+ return Flux
|
||||
+ .<ApiCallRc>just(ApiCallRcImpl.singleApiCallRc(
|
||||
+ ApiConsts.MODIFIED,
|
||||
+ "Cancelling disk addition, reverting to diskless"
|
||||
+ ))
|
||||
+ .concatWith(updateAndAdjustDisk(nodeName, rscName, true, toggleIntoTiebreakerRef, context))
|
||||
+ .concatWith(ctrlRscDfnApiCallHandler.get().updateProps(rsc.getResourceDefinition()));
|
||||
+ }
|
||||
+ // If adding disk and DISK_ADD_REQUESTED is already set, treat as retry
|
||||
+ // Simply retry the operation with existing layer data - satellite will handle it idempotently
|
||||
+ // NOTE: We don't remove/recreate layer data here because removeLayerData() only deletes
|
||||
+ // from controller DB without calling drbdadm down on satellite, leaving orphaned DRBD devices
|
||||
+ errorReporter.logInfo(
|
||||
+ "Toggle Disk retry on %s/%s - DISK_ADD_REQUESTED already set, retrying operation",
|
||||
+ nodeNameStr, rscNameStr);
|
||||
+ ctrlTransactionHelper.commit();
|
||||
+ return Flux
|
||||
+ .<ApiCallRc>just(new ApiCallRcImpl())
|
||||
+ .concatWith(updateAndAdjustDisk(nodeName, rscName, false, toggleIntoTiebreakerRef, context))
|
||||
+ .concatWith(ctrlRscDfnApiCallHandler.get().updateProps(rsc.getResourceDefinition()));
|
||||
}
|
||||
if (hasDiskRemoveRequested(rsc))
|
||||
{
|
||||
- throw new ApiRcException(ApiCallRcImpl.simpleEntry(
|
||||
- ApiConsts.FAIL_RSC_BUSY,
|
||||
- "Removal of disk from resource already requested",
|
||||
- true
|
||||
- ));
|
||||
+ if (!removeDisk)
|
||||
+ {
|
||||
+ // User wants to cancel the failed remove-disk operation
|
||||
+ errorReporter.logInfo(
|
||||
+ "Toggle Disk cancel on %s/%s - cancelling failed DISK_REMOVE_REQUESTED",
|
||||
+ nodeNameStr, rscNameStr);
|
||||
+ unmarkDiskRemoveRequested(rsc);
|
||||
+ ctrlTransactionHelper.commit();
|
||||
+ return Flux.<ApiCallRc>just(
|
||||
+ ApiCallRcImpl.singleApiCallRc(
|
||||
+ ApiConsts.MODIFIED,
|
||||
+ "Cancelled disk removal request"
|
||||
+ )
|
||||
+ );
|
||||
+ }
|
||||
+ // If removing disk and DISK_REMOVE_REQUESTED is already set, treat as retry
|
||||
+ errorReporter.logInfo(
|
||||
+ "Toggle Disk retry on %s/%s - DISK_REMOVE_REQUESTED already set, continuing operation",
|
||||
+ nodeNameStr, rscNameStr);
|
||||
+ ctrlTransactionHelper.commit();
|
||||
+ return Flux
|
||||
+ .<ApiCallRc>just(new ApiCallRcImpl())
|
||||
+ .concatWith(updateAndAdjustDisk(nodeName, rscName, true, toggleIntoTiebreakerRef, context))
|
||||
+ .concatWith(ctrlRscDfnApiCallHandler.get().updateProps(rsc.getResourceDefinition()));
|
||||
}
|
||||
|
||||
if (!removeDisk && !ctrlVlmCrtApiHelper.isDiskless(rsc))
|
||||
@@ -412,17 +477,43 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
true
|
||||
));
|
||||
}
|
||||
+ ResourceDefinition rscDfn = rsc.getResourceDefinition();
|
||||
+ AccessContext peerCtx = peerAccCtx.get();
|
||||
+
|
||||
if (removeDisk && ctrlVlmCrtApiHelper.isDiskless(rsc))
|
||||
{
|
||||
+ // Resource is marked as diskless - check if it has orphaned storage layers that need cleanup
|
||||
+ AbsRscLayerObject<Resource> layerData = getLayerData(peerCtx, rsc);
|
||||
+ if (layerData != null && (LayerUtils.hasLayer(layerData, DeviceLayerKind.LUKS) ||
|
||||
+ hasNonDisklessStorageLayer(layerData)))
|
||||
+ {
|
||||
+ // Resource is marked as diskless but has orphaned storage layers - need cleanup
|
||||
+ // Use the existing disk removal flow to properly cleanup storage on satellite
|
||||
+ errorReporter.logInfo(
|
||||
+ "Toggle Disk cleanup on %s/%s - resource is diskless but has orphaned storage layers, cleaning up",
|
||||
+ nodeNameStr, rscNameStr);
|
||||
+
|
||||
+ // Set DISK_REMOVE_REQUESTED to use the existing disk removal flow
|
||||
+ // This will trigger proper satellite cleanup via DISK_REMOVING flag
|
||||
+ markDiskRemoveRequested(rsc);
|
||||
+
|
||||
+ ctrlTransactionHelper.commit();
|
||||
+
|
||||
+ // Use existing disk removal flow - this will properly cleanup storage on satellite
|
||||
+ return Flux
|
||||
+ .<ApiCallRc>just(ApiCallRcImpl.singleApiCallRc(
|
||||
+ ApiConsts.MODIFIED,
|
||||
+ "Cleaning up orphaned storage layers"
|
||||
+ ))
|
||||
+ .concatWith(updateAndAdjustDisk(nodeName, rscName, true, toggleIntoTiebreakerRef, context))
|
||||
+ .concatWith(ctrlRscDfnApiCallHandler.get().updateProps(rsc.getResourceDefinition()));
|
||||
+ }
|
||||
throw new ApiRcException(ApiCallRcImpl.simpleEntry(
|
||||
ApiConsts.WARN_RSC_ALREADY_DISKLESS,
|
||||
"Resource already diskless",
|
||||
true
|
||||
));
|
||||
}
|
||||
-
|
||||
- ResourceDefinition rscDfn = rsc.getResourceDefinition();
|
||||
- AccessContext peerCtx = peerAccCtx.get();
|
||||
if (removeDisk)
|
||||
{
|
||||
// Prevent removal of the last disk
|
||||
@@ -1446,6 +1537,30 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
}
|
||||
}
|
||||
|
||||
+ private void unmarkDiskAddRequested(Resource rsc)
|
||||
+ {
|
||||
+ try
|
||||
+ {
|
||||
+ rsc.getStateFlags().disableFlags(apiCtx, Resource.Flags.DISK_ADD_REQUESTED);
|
||||
+ }
|
||||
+ catch (AccessDeniedException | DatabaseException exc)
|
||||
+ {
|
||||
+ throw new ImplementationError(exc);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void unmarkDiskRemoveRequested(Resource rsc)
|
||||
+ {
|
||||
+ try
|
||||
+ {
|
||||
+ rsc.getStateFlags().disableFlags(apiCtx, Resource.Flags.DISK_REMOVE_REQUESTED);
|
||||
+ }
|
||||
+ catch (AccessDeniedException | DatabaseException exc)
|
||||
+ {
|
||||
+ throw new ImplementationError(exc);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
private void markDiskAdded(Resource rscData)
|
||||
{
|
||||
try
|
||||
@@ -1511,6 +1626,41 @@ public class CtrlRscToggleDiskApiCallHandler implements CtrlSatelliteConnectionL
|
||||
return layerData;
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * Check if the layer stack has a non-diskless STORAGE layer.
|
||||
+ * This is used to detect orphaned storage layers that need cleanup.
|
||||
+ */
|
||||
+ private boolean hasNonDisklessStorageLayer(AbsRscLayerObject<Resource> layerDataRef)
|
||||
+ {
|
||||
+ boolean hasNonDiskless = false;
|
||||
+ if (layerDataRef != null)
|
||||
+ {
|
||||
+ if (layerDataRef.getLayerKind() == DeviceLayerKind.STORAGE)
|
||||
+ {
|
||||
+ for (VlmProviderObject<Resource> vlmData : layerDataRef.getVlmLayerObjects().values())
|
||||
+ {
|
||||
+ if (vlmData.getProviderKind() != DeviceProviderKind.DISKLESS)
|
||||
+ {
|
||||
+ hasNonDiskless = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if (!hasNonDiskless)
|
||||
+ {
|
||||
+ for (AbsRscLayerObject<Resource> child : layerDataRef.getChildren())
|
||||
+ {
|
||||
+ if (hasNonDisklessStorageLayer(child))
|
||||
+ {
|
||||
+ hasNonDiskless = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return hasNonDiskless;
|
||||
+ }
|
||||
+
|
||||
private LockGuard createLockGuard()
|
||||
{
|
||||
return lockGuardFactory.buildDeferred(LockType.WRITE, LockObj.NODES_MAP, LockObj.RSC_DFN_MAP);
|
||||
@@ -0,0 +1,87 @@
|
||||
From 1250abe99d64a0501795e37d3b6af62410002239 Mon Sep 17 00:00:00 2001
|
||||
From: Andrei Kvapil <kvapss@gmail.com>
|
||||
Date: Mon, 12 Jan 2026 13:44:46 +0100
|
||||
Subject: [PATCH] fix(drbd): prevent duplicate TCP ports after toggle-disk
|
||||
operations
|
||||
|
||||
Remove redundant ensureStackDataExists() call with empty payload from
|
||||
resetStoragePools() method that was causing TCP port conflicts after
|
||||
toggle-disk operations.
|
||||
|
||||
Root Cause:
|
||||
-----------
|
||||
The resetStoragePools() method, introduced in 2019 (commit 95cc17d0b8),
|
||||
calls ensureStackDataExists() with an empty LayerPayload. This worked
|
||||
correctly when TCP ports were stored at RscDfn level.
|
||||
|
||||
After the TCP port migration to per-node level (commit f754943463, May
|
||||
2025), this empty payload results in DrbdRscData being created without
|
||||
TCP ports assigned. The controller then sends a Pojo with an empty port
|
||||
Set to satellites.
|
||||
|
||||
On satellites, when DrbdRscData is initialized with an empty port list,
|
||||
initPorts() uses preferredNewPortsRef from peer resources. Since
|
||||
SatelliteDynamicNumberPool.tryAllocate() always returns true (no-op),
|
||||
any port from preferredNewPortsRef is accepted without conflict checking,
|
||||
leading to duplicate TCP port assignments.
|
||||
|
||||
Impact:
|
||||
-------
|
||||
This regression affects toggle-disk operations, particularly:
|
||||
- Snapshot creation/restore operations
|
||||
- Manual toggle-disk operations
|
||||
- Any operation calling resetStoragePools()
|
||||
|
||||
Symptoms include:
|
||||
- DRBD resources failing to adjust with "port is also used" errors
|
||||
- Resources stuck in StandAlone or Connecting states
|
||||
- Multiple resources on the same node using identical TCP ports
|
||||
|
||||
Solution:
|
||||
---------
|
||||
Remove the ensureStackDataExists() call from resetStoragePools() as it
|
||||
is redundant. The calling code (e.g., CtrlRscToggleDiskApiCallHandler
|
||||
line 1071) already invokes ensureStackDataExists() with the correct
|
||||
payload immediately after resetStoragePools().
|
||||
|
||||
This fix ensures:
|
||||
1. resetStoragePools() only resets storage pool assignments
|
||||
2. Layer data creation with proper TCP ports happens via the caller's
|
||||
ensureStackDataExists() with correct payload
|
||||
3. No DrbdRscData objects are created without TCP port assignments
|
||||
|
||||
Related Issues:
|
||||
---------------
|
||||
Fixes #454 - Duplicate TCP ports after backup/restore operations
|
||||
Related to user reports of resources stuck in StandAlone after node
|
||||
reboots when toggle-disk or backup operations were in progress.
|
||||
|
||||
Testing:
|
||||
--------
|
||||
Verified that:
|
||||
- Toggle-disk operations no longer create resources without TCP ports
|
||||
- Backup/restore operations complete without TCP port conflicts
|
||||
- Resources maintain unique TCP ports across toggle-disk cycles
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>
|
||||
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
|
||||
---
|
||||
.../linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java | 2 --
|
||||
1 file changed, 2 deletions(-)
|
||||
|
||||
diff --git a/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java b/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java
|
||||
index 3538b380c..4f589145e 100644
|
||||
--- a/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java
|
||||
+++ b/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java
|
||||
@@ -276,8 +276,6 @@ public class CtrlRscLayerDataFactory
|
||||
|
||||
rscDataToProcess.addAll(rscData.getChildren());
|
||||
}
|
||||
-
|
||||
- ensureStackDataExists(rscRef, null, new LayerPayload());
|
||||
}
|
||||
catch (AccessDeniedException exc)
|
||||
{
|
||||
--
|
||||
2.39.5 (Apple Git-154)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
index a302ee835..01967a31f 100644
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
@@ -371,10 +371,13 @@ public class DrbdLayer implements DeviceLayer
|
||||
boolean isDiskless = drbdRscData.getAbsResource().isDrbdDiskless(workerCtx);
|
||||
StateFlags<Flags> rscFlags = drbdRscData.getAbsResource().getStateFlags();
|
||||
boolean isDiskRemoving = rscFlags.isSet(workerCtx, Resource.Flags.DISK_REMOVING);
|
||||
+ // Check if we're in toggle-disk operation (adding disk to diskless resource)
|
||||
+ boolean isDiskAdding = rscFlags.isSomeSet(workerCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING);
|
||||
|
||||
boolean contProcess = isDiskless;
|
||||
|
||||
- boolean processChildren = !isDiskless || isDiskRemoving;
|
||||
+ // Process children when: has disk, removing disk, OR adding disk (toggle-disk)
|
||||
+ boolean processChildren = !isDiskless || isDiskRemoving || isDiskAdding;
|
||||
// do not process children when ONLY DRBD_DELETE flag is set (DELETE flag is still unset)
|
||||
processChildren &= (!rscFlags.isSet(workerCtx, Resource.Flags.DRBD_DELETE) ||
|
||||
rscFlags.isSet(workerCtx, Resource.Flags.DELETE));
|
||||
@@ -570,7 +573,11 @@ public class DrbdLayer implements DeviceLayer
|
||||
{
|
||||
// hasMetaData needs to be run after child-resource processed
|
||||
List<DrbdVlmData<Resource>> createMetaData = new ArrayList<>();
|
||||
- if (!drbdRscData.getAbsResource().isDrbdDiskless(workerCtx) && !skipDisk)
|
||||
+ // Check if we're in toggle-disk operation (adding disk to diskless resource)
|
||||
+ boolean isDiskAddingForMd = drbdRscData.getAbsResource().getStateFlags()
|
||||
+ .isSomeSet(workerCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING);
|
||||
+ // Create metadata when: has disk OR adding disk (toggle-disk), and skipDisk is disabled
|
||||
+ if ((!drbdRscData.getAbsResource().isDrbdDiskless(workerCtx) || isDiskAddingForMd) && !skipDisk)
|
||||
{
|
||||
// do not try to create meta data while the resource is diskless or skipDisk is enabled
|
||||
for (DrbdVlmData<Resource> drbdVlmData : checkMetaData)
|
||||
@@ -988,8 +995,10 @@ public class DrbdLayer implements DeviceLayer
|
||||
{
|
||||
List<DrbdVlmData<Resource>> checkMetaData = new ArrayList<>();
|
||||
Resource rsc = drbdRscData.getAbsResource();
|
||||
+ // Include DISK_ADD_REQUESTED/DISK_ADDING for toggle-disk scenario where we need to check/create metadata
|
||||
if (!rsc.isDrbdDiskless(workerCtx) ||
|
||||
- rsc.getStateFlags().isSet(workerCtx, Resource.Flags.DISK_REMOVING)
|
||||
+ rsc.getStateFlags().isSet(workerCtx, Resource.Flags.DISK_REMOVING) ||
|
||||
+ rsc.getStateFlags().isSomeSet(workerCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING)
|
||||
)
|
||||
{
|
||||
// using a dedicated list to prevent concurrentModificationException
|
||||
@@ -1177,9 +1186,16 @@ public class DrbdLayer implements DeviceLayer
|
||||
|
||||
boolean hasMetaData;
|
||||
|
||||
+ // Check if we need to verify/create metadata
|
||||
+ // Force metadata check when:
|
||||
+ // 1. checkMetaData is enabled
|
||||
+ // 2. volume doesn't have disk yet (diskless -> diskful transition)
|
||||
+ // 3. DISK_ADD_REQUESTED/DISK_ADDING flag is set (retry scenario where storage exists but no metadata)
|
||||
+ boolean isDiskAddingState = drbdVlmData.getRscLayerObject().getAbsResource().getStateFlags()
|
||||
+ .isSomeSet(workerCtx, Resource.Flags.DISK_ADD_REQUESTED, Resource.Flags.DISK_ADDING);
|
||||
if (drbdVlmData.checkMetaData() ||
|
||||
- // when adding a disk, DRBD believes that it is diskless but we still need to create metadata
|
||||
- !drbdVlmData.hasDisk())
|
||||
+ !drbdVlmData.hasDisk() ||
|
||||
+ isDiskAddingState)
|
||||
{
|
||||
if (mdUtils.hasMetaData())
|
||||
{
|
||||
@@ -0,0 +1,260 @@
|
||||
From 6a556821b9a0996d34389a27b941694ce810a44c Mon Sep 17 00:00:00 2001
|
||||
From: Andrei Kvapil <kvapss@gmail.com>
|
||||
Date: Mon, 12 Jan 2026 14:26:57 +0100
|
||||
Subject: [PATCH 1/3] Fix: Skip DRBD adjust/res file regeneration when child
|
||||
layer device is inaccessible
|
||||
|
||||
When deleting encrypted (LUKS) resources, the LUKS layer may close its device
|
||||
before the DRBD layer attempts to adjust the resource or regenerate the res
|
||||
file. This causes 'Failed to adjust DRBD resource' errors.
|
||||
|
||||
This fix adds checks before regenerateResFile() and drbdUtils.adjust()
|
||||
to verify that child layer devices are accessible. If a child device doesn't
|
||||
exist or is not accessible (e.g., LUKS device is closed during resource
|
||||
deletion), these operations are skipped and the adjustRequired flag is cleared,
|
||||
allowing resource deletion to proceed successfully.
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>
|
||||
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
|
||||
---
|
||||
.../linbit/linstor/layer/drbd/DrbdLayer.java | 72 ++++++++++++++++---
|
||||
1 file changed, 61 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
index 01967a31f..871d830d1 100644
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
@@ -592,7 +592,29 @@ public class DrbdLayer implements DeviceLayer
|
||||
// The .res file might not have been generated in the prepare method since it was
|
||||
// missing information from the child-layers. Now that we have processed them, we
|
||||
// need to make sure the .res file exists in all circumstances.
|
||||
- regenerateResFile(drbdRscData);
|
||||
+ // However, if the underlying devices are not accessible (e.g., LUKS device is closed
|
||||
+ // during resource deletion), we skip regenerating the res file to avoid errors
|
||||
+ boolean canRegenerateResFile = true;
|
||||
+ if (!skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx))
|
||||
+ {
|
||||
+ AbsRscLayerObject<Resource> dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA);
|
||||
+ if (dataChild != null)
|
||||
+ {
|
||||
+ for (DrbdVlmData<Resource> drbdVlmData : drbdRscData.getVlmLayerObjects().values())
|
||||
+ {
|
||||
+ VlmProviderObject<Resource> childVlm = dataChild.getVlmProviderObject(drbdVlmData.getVlmNr());
|
||||
+ if (childVlm == null || !childVlm.exists() || childVlm.getDevicePath() == null)
|
||||
+ {
|
||||
+ canRegenerateResFile = false;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if (canRegenerateResFile)
|
||||
+ {
|
||||
+ regenerateResFile(drbdRscData);
|
||||
+ }
|
||||
|
||||
// createMetaData needs rendered resFile
|
||||
for (DrbdVlmData<Resource> drbdVlmData : createMetaData)
|
||||
@@ -766,19 +788,47 @@ public class DrbdLayer implements DeviceLayer
|
||||
|
||||
if (drbdRscData.isAdjustRequired())
|
||||
{
|
||||
- try
|
||||
+ // Check if underlying devices are accessible before adjusting
|
||||
+ // This is important for encrypted resources (LUKS) where the device
|
||||
+ // might be closed during deletion
|
||||
+ boolean canAdjust = true;
|
||||
+ if (!skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx))
|
||||
{
|
||||
- drbdUtils.adjust(
|
||||
- drbdRscData,
|
||||
- false,
|
||||
- skipDisk,
|
||||
- false
|
||||
- );
|
||||
+ AbsRscLayerObject<Resource> dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA);
|
||||
+ if (dataChild != null)
|
||||
+ {
|
||||
+ for (DrbdVlmData<Resource> drbdVlmData : drbdRscData.getVlmLayerObjects().values())
|
||||
+ {
|
||||
+ VlmProviderObject<Resource> childVlm = dataChild.getVlmProviderObject(drbdVlmData.getVlmNr());
|
||||
+ if (childVlm == null || !childVlm.exists() || childVlm.getDevicePath() == null)
|
||||
+ {
|
||||
+ canAdjust = false;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
- catch (ExtCmdFailedException extCmdExc)
|
||||
+
|
||||
+ if (canAdjust)
|
||||
+ {
|
||||
+ try
|
||||
+ {
|
||||
+ drbdUtils.adjust(
|
||||
+ drbdRscData,
|
||||
+ false,
|
||||
+ skipDisk,
|
||||
+ false
|
||||
+ );
|
||||
+ }
|
||||
+ catch (ExtCmdFailedException extCmdExc)
|
||||
+ {
|
||||
+ restoreBackupResFile(drbdRscData);
|
||||
+ throw extCmdExc;
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
{
|
||||
- restoreBackupResFile(drbdRscData);
|
||||
- throw extCmdExc;
|
||||
+ drbdRscData.setAdjustRequired(false);
|
||||
}
|
||||
}
|
||||
|
||||
--
|
||||
2.39.5 (Apple Git-154)
|
||||
|
||||
|
||||
From afe51ea674c4a350c27d1f2cacfecf6fe42b8a7a Mon Sep 17 00:00:00 2001
|
||||
From: Andrei Kvapil <kvapss@gmail.com>
|
||||
Date: Mon, 12 Jan 2026 14:27:52 +0100
|
||||
Subject: [PATCH 2/3] fix(satellite): skip lsblk when device path doesn't
|
||||
physically exist
|
||||
|
||||
Add physical device path existence check before calling lsblk in updateDiscGran().
|
||||
This prevents race condition when drbdadm adjust temporarily brings devices down/up
|
||||
and the kernel hasn't created the device node yet.
|
||||
|
||||
Issue: After satellite restart with patched code, some DRBD resources ended up in
|
||||
Unknown state because:
|
||||
1. drbdadm adjust successfully completes (brings devices up)
|
||||
2. updateDiscGran() immediately tries to check discard granularity
|
||||
3. /dev/drbd* device node doesn't exist yet (kernel hasn't created it)
|
||||
4. lsblk fails with exit code 32 "not a block device"
|
||||
5. StorageException interrupts DeviceManager cycle
|
||||
6. DRBD device remains in incomplete state
|
||||
|
||||
Solution: Check Files.exists(devicePath) before calling lsblk. If device doesn't
|
||||
exist yet, skip the check - it will be retried in the next DeviceManager cycle
|
||||
when the device node is available.
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>
|
||||
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
|
||||
---
|
||||
.../linbit/linstor/core/devmgr/DeviceHandlerImpl.java | 9 +++++++--
|
||||
1 file changed, 7 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java
|
||||
index 49138a8fd..1c13cfc9d 100644
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java
|
||||
@@ -68,6 +68,8 @@ import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
+import java.nio.file.Files;
|
||||
+import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -1645,8 +1647,11 @@ public class DeviceHandlerImpl implements DeviceHandler
|
||||
|
||||
private void updateDiscGran(VlmProviderObject<Resource> vlmData) throws DatabaseException, StorageException
|
||||
{
|
||||
- String devicePath = vlmData.getDevicePath();
|
||||
- if (devicePath != null && vlmData.exists())
|
||||
+ @Nullable String devicePath = vlmData.getDevicePath();
|
||||
+ // Check if device path physically exists before calling lsblk
|
||||
+ // This is important for DRBD devices which might be temporarily unavailable during adjust
|
||||
+ // (drbdadm adjust brings devices down/up, and kernel might not have created the device node yet)
|
||||
+ if (devicePath != null && vlmData.exists() && Files.exists(Paths.get(devicePath)))
|
||||
{
|
||||
if (vlmData.getDiscGran() == VlmProviderObject.UNINITIALIZED_SIZE)
|
||||
{
|
||||
--
|
||||
2.39.5 (Apple Git-154)
|
||||
|
||||
|
||||
From de1f22e7c008c5479f85a3b1ebdf8461944210f4 Mon Sep 17 00:00:00 2001
|
||||
From: Andrei Kvapil <kvapss@gmail.com>
|
||||
Date: Mon, 12 Jan 2026 14:28:23 +0100
|
||||
Subject: [PATCH 3/3] fix(drbd): only check child devices when disk access is
|
||||
actually needed
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The previous implementation blocked `drbdadm adjust` whenever child
|
||||
device paths were unavailable, even for operations that don't require
|
||||
disk access (like network reconnect from StandAlone to Connected).
|
||||
|
||||
After node reboot, DRBD resources often remain in StandAlone state
|
||||
because:
|
||||
1. updateResourceToCurrentDrbdState() correctly detects StandAlone
|
||||
and sets adjustRequired=true
|
||||
2. However, canAdjust check fails because child volumes may have
|
||||
devicePath=null (due to INACTIVE flag, cloning state, or
|
||||
initialization race)
|
||||
3. adjust is skipped → adjustRequired=false → resources stay StandAlone
|
||||
|
||||
Root cause: The canAdjust check was added to protect LUKS deletion
|
||||
scenarios but was applied to ALL cases, including network reconnect
|
||||
which doesn't need disk access.
|
||||
|
||||
Fix: Check child device accessibility only when disk access is actually
|
||||
required (volume creation, resize, metadata operations). Network
|
||||
reconnect operations (StandAlone → Connected) now proceed without
|
||||
checking child devices.
|
||||
|
||||
This ensures automatic DRBD reconnection after reboot while preserving
|
||||
protection for LUKS deletion scenarios.
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>
|
||||
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
|
||||
---
|
||||
.../linbit/linstor/layer/drbd/DrbdLayer.java | 27 ++++++++++++++++++-
|
||||
1 file changed, 26 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
index 871d830d1..78b8195a4 100644
|
||||
--- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
+++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java
|
||||
@@ -792,7 +792,32 @@ public class DrbdLayer implements DeviceLayer
|
||||
// This is important for encrypted resources (LUKS) where the device
|
||||
// might be closed during deletion
|
||||
boolean canAdjust = true;
|
||||
- if (!skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx))
|
||||
+
|
||||
+ // IMPORTANT: Check child volumes only when disk access is actually needed.
|
||||
+ // For network reconnect (StandAlone -> Connected), disk access is not required.
|
||||
+ boolean needsDiskAccess = false;
|
||||
+
|
||||
+ // Check if there are pending operations that require disk access
|
||||
+ for (DrbdVlmData<Resource> drbdVlmData : drbdRscData.getVlmLayerObjects().values())
|
||||
+ {
|
||||
+ Volume vlm = (Volume) drbdVlmData.getVolume();
|
||||
+ StateFlags<Volume.Flags> vlmFlags = vlm.getFlags();
|
||||
+
|
||||
+ // Disk access is needed if:
|
||||
+ // - creating a new volume
|
||||
+ // - resizing
|
||||
+ // - checking/creating metadata
|
||||
+ if (!drbdVlmData.exists() ||
|
||||
+ drbdVlmData.checkMetaData() ||
|
||||
+ vlmFlags.isSomeSet(workerCtx, Volume.Flags.RESIZE, Volume.Flags.DRBD_RESIZE))
|
||||
+ {
|
||||
+ needsDiskAccess = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Check child volumes only if disk access is actually needed
|
||||
+ if (needsDiskAccess && !skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx))
|
||||
{
|
||||
AbsRscLayerObject<Resource> dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA);
|
||||
if (dataChild != null)
|
||||
--
|
||||
2.39.5 (Apple Git-154)
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
{{- define "cozy.linstor.version" -}}
|
||||
{{- $piraeusConfigMap := lookup "v1" "ConfigMap" "cozy-linstor" "piraeus-operator-image-config"}}
|
||||
{{- if not $piraeusConfigMap }}
|
||||
{{- fail "Piraeus controller is not yet installed, ConfigMap cozy-linstor/piraeus-operator-image-config is missing" }}
|
||||
{{- end }}
|
||||
{{- $piraeusImagesConfig := $piraeusConfigMap | dig "data" "0_piraeus_datastore_images.yaml" nil | required "No image config" | fromYaml }}
|
||||
base: {{ $piraeusImagesConfig.base | required "No image base in piraeus config" }}
|
||||
controller:
|
||||
image: {{ $piraeusImagesConfig | dig "components" "linstor-controller" "image" nil | required "No controller image" }}
|
||||
tag: {{ $piraeusImagesConfig | dig "components" "linstor-controller" "tag" nil | required "No controller tag" }}
|
||||
satellite:
|
||||
image: {{ $piraeusImagesConfig | dig "components" "linstor-satellite" "image" nil | required "No satellite image" }}
|
||||
tag: {{ $piraeusImagesConfig | dig "components" "linstor-satellite" "tag" nil | required "No satellite tag" }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "cozy.linstor.version.controller" -}}
|
||||
{{- $version := (include "cozy.linstor.version" .) | fromYaml }}
|
||||
{{- printf "%s/%s:%s" $version.base $version.controller.image $version.controller.tag }}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "cozy.linstor.version.satellite" -}}
|
||||
{{- $version := (include "cozy.linstor.version" .) | fromYaml }}
|
||||
{{- printf "%s/%s:%s" $version.base $version.satellite.image $version.satellite.tag }}
|
||||
{{- end -}}
|
||||
@@ -27,8 +27,10 @@ spec:
|
||||
podTemplate:
|
||||
spec:
|
||||
containers:
|
||||
- name: linstor-controller
|
||||
image: {{ .Values.piraeusServer.image.repository }}:{{ .Values.piraeusServer.image.tag }}
|
||||
- name: plunger
|
||||
image: {{ include "cozy.linstor.version.controller" . }}
|
||||
image: {{ .Values.piraeusServer.image.repository }}:{{ .Values.piraeusServer.image.tag }}
|
||||
command:
|
||||
- "/scripts/plunger-controller.sh"
|
||||
securityContext:
|
||||
|
||||
@@ -13,6 +13,7 @@ spec:
|
||||
hostNetwork: true
|
||||
containers:
|
||||
- name: linstor-satellite
|
||||
image: {{ .Values.piraeusServer.image.repository }}:{{ .Values.piraeusServer.image.tag }}
|
||||
securityContext:
|
||||
# real-world installations need some debugging from time to time
|
||||
readOnlyRootFilesystem: false
|
||||
|
||||
@@ -11,7 +11,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: plunger
|
||||
image: {{ include "cozy.linstor.version.satellite" . }}
|
||||
image: {{ .Values.piraeusServer.image.repository }}:{{ .Values.piraeusServer.image.tag }}
|
||||
command:
|
||||
- "/scripts/plunger-satellite.sh"
|
||||
securityContext:
|
||||
@@ -48,7 +48,7 @@ spec:
|
||||
name: script-volume
|
||||
readOnly: true
|
||||
- name: drbd-logger
|
||||
image: {{ include "cozy.linstor.version.satellite" . }}
|
||||
image: {{ .Values.piraeusServer.image.repository }}:{{ .Values.piraeusServer.image.tag }}
|
||||
command:
|
||||
- "/scripts/plunger-drbd-logger.sh"
|
||||
securityContext:
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
|
||||
piraeusServer:
|
||||
image:
|
||||
repository: ghcr.io/cozystack/cozystack/piraeus-server
|
||||
tag: 1.32.3@sha256:1138c8dc0a117360ef70e2e2ab97bc2696419b63f46358f7668c7e01a96c419b
|
||||
|
||||
@@ -4,8 +4,8 @@ metallb:
|
||||
controller:
|
||||
image:
|
||||
repository: ghcr.io/cozystack/cozystack/metallb-controller
|
||||
tag: v0.15.2@sha256:0e9080234fc8eedab78ad2831fb38df375c383e901a752d72b353c8d13b9605f
|
||||
tag: v0.15.2@sha256:623ce74b5802bff6e29f29478ccab29ce4162a64148be006c69e16cc3207e289
|
||||
speaker:
|
||||
image:
|
||||
repository: ghcr.io/cozystack/cozystack/metallb-speaker
|
||||
tag: v0.15.2@sha256:e14d4c328c3ab91a6eadfeea90da96388503492d165e7e8582f291b1872e53b2
|
||||
tag: v0.15.2@sha256:f264058afd9228452a260ab9c9dd1859404745627a2a38c2ba4671e27f3b3bb2
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: coredns
|
||||
name: coredns-metrics
|
||||
namespace: kube-system
|
||||
labels:
|
||||
app: coredns
|
||||
@@ -19,7 +19,7 @@ spec:
|
||||
apiVersion: operator.victoriametrics.com/v1beta1
|
||||
kind: VMServiceScrape
|
||||
metadata:
|
||||
name: coredns
|
||||
name: coredns-metrics
|
||||
namespace: cozy-monitoring
|
||||
spec:
|
||||
selector:
|
||||
|
||||
@@ -162,7 +162,6 @@ spec:
|
||||
memory: "100Mi"
|
||||
limits:
|
||||
cpu: "100m"
|
||||
memory: "300Mi"
|
||||
securityContext:
|
||||
privileged: true
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
objectstorage:
|
||||
controller:
|
||||
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.38.2@sha256:7d37495cce46d30d4613ecfacaa7b7f140e7ea8f3dbcc3e8c976e271de6cc71b"
|
||||
image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.39.5@sha256:e8f605a5ec4b801dfaf49aa7bce8d7a1ce28df578e23a0b859571f60f98efe8c"
|
||||
|
||||
@@ -3,8 +3,8 @@ name: piraeus
|
||||
description: |
|
||||
The Piraeus Operator manages software defined storage clusters using LINSTOR in Kubernetes.
|
||||
type: application
|
||||
version: 2.10.1
|
||||
appVersion: "v2.10.1"
|
||||
version: 2.10.2
|
||||
appVersion: "v2.10.2"
|
||||
maintainers:
|
||||
- name: Piraeus Datastore
|
||||
url: https://piraeus.io
|
||||
|
||||
@@ -23,10 +23,10 @@ data:
|
||||
tag: v1.32.3
|
||||
image: piraeus-server
|
||||
linstor-csi:
|
||||
tag: v1.10.2
|
||||
tag: v1.10.3
|
||||
image: piraeus-csi
|
||||
nfs-server:
|
||||
tag: v1.10.2
|
||||
tag: v1.10.3
|
||||
image: piraeus-csi-nfs-server
|
||||
drbd-reactor:
|
||||
tag: v1.10.0
|
||||
@@ -44,7 +44,7 @@ data:
|
||||
tag: v1.3.0
|
||||
image: linstor-affinity-controller
|
||||
drbd-module-loader:
|
||||
tag: v9.2.15
|
||||
tag: v9.2.16
|
||||
# The special "match" attribute is used to select an image based on the node's reported OS.
|
||||
# The operator will first check the k8s node's ".status.nodeInfo.osImage" field, and compare it against the list
|
||||
# here. If one matches, that specific image name will be used instead of the fallback image.
|
||||
@@ -99,7 +99,7 @@ data:
|
||||
tag: v2.17.0
|
||||
image: livenessprobe
|
||||
csi-provisioner:
|
||||
tag: v6.0.0
|
||||
tag: v6.1.0
|
||||
image: csi-provisioner
|
||||
csi-snapshotter:
|
||||
tag: v8.4.0
|
||||
|
||||
@@ -993,6 +993,24 @@ spec:
|
||||
- Retain
|
||||
- Delete
|
||||
type: string
|
||||
evacuationStrategy:
|
||||
description: EvacuationStrategy configures the evacuation of volumes
|
||||
from a Satellite when DeletionPolicy "Evacuate" is used.
|
||||
nullable: true
|
||||
properties:
|
||||
attachedVolumeReattachTimeout:
|
||||
default: 5m
|
||||
description: |-
|
||||
AttachedVolumeReattachTimeout configures how long evacuation waits for attached volumes to reattach on
|
||||
different nodes. Setting this to 0 disable this evacuation step.
|
||||
type: string
|
||||
unattachedVolumeAttachTimeout:
|
||||
default: 5m
|
||||
description: |-
|
||||
UnattachedVolumeAttachTimeout configures how long evacuation waits for unattached volumes to attach on
|
||||
different nodes. Setting this to 0 disable this evacuation step.
|
||||
type: string
|
||||
type: object
|
||||
internalTLS:
|
||||
description: |-
|
||||
InternalTLS configures secure communication for the LINSTOR Satellite.
|
||||
@@ -1683,6 +1701,23 @@ spec:
|
||||
- Retain
|
||||
- Delete
|
||||
type: string
|
||||
evacuationStrategy:
|
||||
description: EvacuationStrategy configures the evacuation of volumes
|
||||
from a Satellite when DeletionPolicy "Evacuate" is used.
|
||||
properties:
|
||||
attachedVolumeReattachTimeout:
|
||||
default: 5m
|
||||
description: |-
|
||||
AttachedVolumeReattachTimeout configures how long evacuation waits for attached volumes to reattach on
|
||||
different nodes. Setting this to 0 disable this evacuation step.
|
||||
type: string
|
||||
unattachedVolumeAttachTimeout:
|
||||
default: 5m
|
||||
description: |-
|
||||
UnattachedVolumeAttachTimeout configures how long evacuation waits for unattached volumes to attach on
|
||||
different nodes. Setting this to 0 disable this evacuation step.
|
||||
type: string
|
||||
type: object
|
||||
internalTLS:
|
||||
description: |-
|
||||
InternalTLS configures secure communication for the LINSTOR Satellite.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user