mirror of
https://github.com/cozystack/cozystack.git
synced 2026-03-04 05:58:53 +00:00
Compare commits
75 Commits
release-0.
...
lineage-co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca61c71084 | ||
|
|
65a734bb65 | ||
|
|
07384c3605 | ||
|
|
87b2316194 | ||
|
|
585569f285 | ||
|
|
dbe1df8d27 | ||
|
|
17eb1e0ba3 | ||
|
|
b55c9f616d | ||
|
|
f025845a94 | ||
|
|
e54fc63af4 | ||
|
|
9352861051 | ||
|
|
b9eec3f261 | ||
|
|
f2cfb4f870 | ||
|
|
2291d0f7f2 | ||
|
|
15c100d262 | ||
|
|
2c9864bc09 | ||
|
|
bb1e8805dc | ||
|
|
08b5217b72 | ||
|
|
08d2d61f1a | ||
|
|
356fea6a37 | ||
|
|
e1b97e3727 | ||
|
|
ea27dc9497 | ||
|
|
f06c5d996d | ||
|
|
87c5540ad3 | ||
|
|
03e18ee02f | ||
|
|
382a9787f4 | ||
|
|
2bca6b932c | ||
|
|
601f6bd3c9 | ||
|
|
1243a960e3 | ||
|
|
4dd062d9cd | ||
|
|
3e03b1bd86 | ||
|
|
8f1975d1da | ||
|
|
e15ff2a4d0 | ||
|
|
272185a2df | ||
|
|
be8495dd06 | ||
|
|
7f477eec96 | ||
|
|
cc4b7ea28c | ||
|
|
8335347dc3 | ||
|
|
49d69a5896 | ||
|
|
89a74f653a | ||
|
|
9f2b98d364 | ||
|
|
7090b8adf1 | ||
|
|
c5b46fc79c | ||
|
|
a291badbd4 | ||
|
|
52d749d46a | ||
|
|
9f89ef36bb | ||
|
|
f59d072ef1 | ||
|
|
c0d5e52e65 | ||
|
|
034f71cc9d | ||
|
|
fdd4f167c6 | ||
|
|
8fbebd4e47 | ||
|
|
389ec27b19 | ||
|
|
29df1fdc1e | ||
|
|
c4e048b315 | ||
|
|
ce5fd9d292 | ||
|
|
8e906be9df | ||
|
|
99bfd4884f | ||
|
|
15b213b38b | ||
|
|
8ca8817000 | ||
|
|
9f8c79f5d1 | ||
|
|
ce21299280 | ||
|
|
403d1f9944 | ||
|
|
138e5fbe15 | ||
|
|
fe869b97fd | ||
|
|
a4aeeca2d3 | ||
|
|
33691c2d3a | ||
|
|
08f1bda1aa | ||
|
|
58f65abefd | ||
|
|
9c1563adb7 | ||
|
|
cbbb50b194 | ||
|
|
6684117a00 | ||
|
|
6b9b700177 | ||
|
|
89c80a8178 | ||
|
|
6b5af37e1a | ||
|
|
c10f6240b1 |
9
.github/workflows/pull-requests.yaml
vendored
9
.github/workflows/pull-requests.yaml
vendored
@@ -1,7 +1,7 @@
|
||||
name: Pull Request
|
||||
|
||||
env:
|
||||
REGISTRY: ${{ secrets.OCIR_REPO }}
|
||||
REGISTRY: ${{ vars.OCIR_REPO }}
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
@@ -32,7 +32,14 @@ jobs:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: Set up Docker config
|
||||
run: |
|
||||
if [ -d ~/.docker ]; then
|
||||
cp -r ~/.docker "${{ runner.temp }}/.docker"
|
||||
fi
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.OCIR_USER}}
|
||||
|
||||
1
Makefile
1
Makefile
@@ -18,6 +18,7 @@ build: build-deps
|
||||
make -C packages/system/cilium image
|
||||
make -C packages/system/kubeovn image
|
||||
make -C packages/system/kubeovn-webhook image
|
||||
make -C packages/system/kubeovn-plunger image
|
||||
make -C packages/system/dashboard image
|
||||
make -C packages/system/metallb image
|
||||
make -C packages/system/kamaji image
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
|
||||
cozystackiov1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
|
||||
"github.com/cozystack/cozystack/internal/controller"
|
||||
"github.com/cozystack/cozystack/internal/controller/lineagelabeler"
|
||||
"github.com/cozystack/cozystack/internal/telemetry"
|
||||
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
@@ -67,6 +68,7 @@ func main() {
|
||||
var telemetryEndpoint string
|
||||
var telemetryInterval string
|
||||
var cozystackVersion string
|
||||
var watchResources string
|
||||
var tlsOpts []func(*tls.Config)
|
||||
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
|
||||
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
|
||||
@@ -86,6 +88,9 @@ func main() {
|
||||
"Interval between telemetry data collection (e.g. 15m, 1h)")
|
||||
flag.StringVar(&cozystackVersion, "cozystack-version", "unknown",
|
||||
"Version of Cozystack")
|
||||
flag.StringVar(&watchResources, "watch-resources",
|
||||
"v1/Pod,v1/Service,v1/Secret,v1/PersistentVolumeClaim",
|
||||
"Comma-separated list of resources to watch in the form 'group/version/Kind'.")
|
||||
opts := zap.Options{
|
||||
Development: false,
|
||||
}
|
||||
@@ -214,6 +219,15 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := (&lineagelabeler.LineageLabelerReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
WatchResourceCSV: watchResources,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "LineageLabeler")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// +kubebuilder:scaffold:builder
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
|
||||
176
cmd/kubeovn-plunger/main.go
Normal file
176
cmd/kubeovn-plunger/main.go
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
Copyright 2025.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
||||
// to ensure that exec-entrypoint and run can make use of them.
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
|
||||
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
|
||||
"github.com/cozystack/cozystack/internal/controller/kubeovnplunger"
|
||||
// +kubebuilder:scaffold:imports
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = runtime.NewScheme()
|
||||
setupLog = ctrl.Log.WithName("setup")
|
||||
)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
func main() {
|
||||
var metricsAddr string
|
||||
var enableLeaderElection bool
|
||||
var probeAddr string
|
||||
var kubeOVNNamespace string
|
||||
var ovnCentralName string
|
||||
var secureMetrics bool
|
||||
var enableHTTP2 bool
|
||||
var disableTelemetry bool
|
||||
var tlsOpts []func(*tls.Config)
|
||||
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
|
||||
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
|
||||
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
|
||||
flag.StringVar(&kubeOVNNamespace, "kube-ovn-namespace", "cozy-kubeovn", "Namespace where kube-OVN is deployed.")
|
||||
flag.StringVar(&ovnCentralName, "ovn-central-name", "ovn-central", "Ovn-central deployment name.")
|
||||
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
|
||||
"Enable leader election for controller manager. "+
|
||||
"Enabling this will ensure there is only one active controller manager.")
|
||||
flag.BoolVar(&secureMetrics, "metrics-secure", true,
|
||||
"If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
|
||||
flag.BoolVar(&enableHTTP2, "enable-http2", false,
|
||||
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
|
||||
flag.BoolVar(&disableTelemetry, "disable-telemetry", false,
|
||||
"Disable telemetry collection")
|
||||
opts := zap.Options{
|
||||
Development: false,
|
||||
}
|
||||
opts.BindFlags(flag.CommandLine)
|
||||
flag.Parse()
|
||||
|
||||
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
|
||||
|
||||
// if the enable-http2 flag is false (the default), http/2 should be disabled
|
||||
// due to its vulnerabilities. More specifically, disabling http/2 will
|
||||
// prevent from being vulnerable to the HTTP/2 Stream Cancellation and
|
||||
// Rapid Reset CVEs. For more information see:
|
||||
// - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
|
||||
// - https://github.com/advisories/GHSA-4374-p667-p6c8
|
||||
disableHTTP2 := func(c *tls.Config) {
|
||||
setupLog.Info("disabling http/2")
|
||||
c.NextProtos = []string{"http/1.1"}
|
||||
}
|
||||
|
||||
if !enableHTTP2 {
|
||||
tlsOpts = append(tlsOpts, disableHTTP2)
|
||||
}
|
||||
|
||||
webhookServer := webhook.NewServer(webhook.Options{
|
||||
TLSOpts: tlsOpts,
|
||||
})
|
||||
|
||||
// Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
|
||||
// More info:
|
||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/server
|
||||
// - https://book.kubebuilder.io/reference/metrics.html
|
||||
metricsServerOptions := metricsserver.Options{
|
||||
BindAddress: metricsAddr,
|
||||
SecureServing: secureMetrics,
|
||||
TLSOpts: tlsOpts,
|
||||
}
|
||||
|
||||
if secureMetrics {
|
||||
// FilterProvider is used to protect the metrics endpoint with authn/authz.
|
||||
// These configurations ensure that only authorized users and service accounts
|
||||
// can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info:
|
||||
// https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/metrics/filters#WithAuthenticationAndAuthorization
|
||||
metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
|
||||
|
||||
// TODO(user): If CertDir, CertName, and KeyName are not specified, controller-runtime will automatically
|
||||
// generate self-signed certificates for the metrics server. While convenient for development and testing,
|
||||
// this setup is not recommended for production.
|
||||
}
|
||||
|
||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||
Scheme: scheme,
|
||||
Metrics: metricsServerOptions,
|
||||
WebhookServer: webhookServer,
|
||||
HealthProbeBindAddress: probeAddr,
|
||||
LeaderElection: enableLeaderElection,
|
||||
LeaderElectionID: "29a0338b.cozystack.io",
|
||||
// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
|
||||
// when the Manager ends. This requires the binary to immediately end when the
|
||||
// Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
|
||||
// speeds up voluntary leader transitions as the new leader don't have to wait
|
||||
// LeaseDuration time first.
|
||||
//
|
||||
// In the default scaffold provided, the program ends immediately after
|
||||
// the manager stops, so would be fine to enable this option. However,
|
||||
// if you are doing or is intended to do any operation such as perform cleanups
|
||||
// after the manager stops then its usage might be unsafe.
|
||||
// LeaderElectionReleaseOnCancel: true,
|
||||
})
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to create manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&kubeovnplunger.KubeOVNPlunger{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Registry: metrics.Registry,
|
||||
}).SetupWithManager(mgr, kubeOVNNamespace, ovnCentralName); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "KubeOVNPlunger")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// +kubebuilder:scaffold:builder
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up health check")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up ready check")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
setupLog.Info("starting manager")
|
||||
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
|
||||
setupLog.Error(err, "problem running manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
Release description.
|
||||
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0..
|
||||
@@ -15,3 +14,7 @@ https://github.com/cozystack/cozystack/releases/tag/v0..
|
||||
## Documentation
|
||||
|
||||
## Development, Testing, and CI/CD
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: **Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.0...v0.35.0
|
||||
|
||||
138
docs/changelogs/v0.35.0.md
Normal file
138
docs/changelogs/v0.35.0.md
Normal file
@@ -0,0 +1,138 @@
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.35.0
|
||||
-->
|
||||
|
||||
## Feature Highlights
|
||||
|
||||
### External Application Sources in Cozystack
|
||||
|
||||
Cozystack now supports adding external application packages to the platform's application catalog.
|
||||
Platform administrators can include custom or third-party applications alongside built-in ones, using the Cozystack API.
|
||||
|
||||
Adding an application requires making an application package, similar to the ones included in Cozystack
|
||||
under [`packages/apps`](https://github.com/cozystack/cozystack/tree/main/packages/apps).
|
||||
Using external packages is enabled by a new CustomResourceDefinition (CRD) called `CozystackResourceDefinition` and
|
||||
a corresponding controller (reconciler) that watches for these resources.
|
||||
|
||||
Add your own managed application using the [documentation](https://cozystack.io/docs/applications/external/)
|
||||
and an example at [github.com/cozystack/external-apps-example](https://github.com/cozystack/external-apps-example).
|
||||
|
||||
<!--
|
||||
* [platform] Enable using external application packages by adding a `CozystackResourceDefinition` reconciler. Read the documentation on [adding external applications to Cozystack](https://cozystack.io/docs/applications/external/) to learn more. (@klinch0 in https://github.com/cozystack/cozystack/pull/1313)
|
||||
* [cozystack-api] Provide an API for administrators to define custom managed applications alongside existing managed apps. (@klinch in https://github.com/cozystack/cozystack/pull/1230)
|
||||
-->
|
||||
|
||||
|
||||
### Cozystack API Improvements
|
||||
|
||||
This release brings significant improvements to the OpenAPI specs for all managed applications in Cozystack,
|
||||
including databases, tenant Kubernetes, virtual machines, monitoring, and others.
|
||||
These changes include more precise type definitions for fields that were previously defined only as generic objects,
|
||||
and many fields now have value constraints.
|
||||
Now many possible misconfigurations are detected immediately upon API request, and not later, with a failed deployment.
|
||||
|
||||
The Cozystack API now also displays default values for the application resources.
|
||||
Most other fields now have sane default values when such values are possible.
|
||||
|
||||
All these changes pave the road for the new Cozystack UI, which is currently under development.
|
||||
|
||||
### Hetzner RobotLB Support
|
||||
|
||||
MetalLB, the default load balancer included in Cozystack, is built for bare metal and self-hosted VMs,
|
||||
but is not supported on most cloud providers.
|
||||
For example, Hetzner provides its own RobotLB service, which Cozystack now supports as an optional component.
|
||||
|
||||
Read the updated guide on [deploying Cozystack on Hetzner.com](https://cozystack.io/docs/install/providers/hetzner/)
|
||||
to learn more and deploy your own Cozystack cluster on Hetzner.
|
||||
|
||||
### S3 Service: Dedicated Clusters and Monitoring
|
||||
|
||||
You can now deploy dedicated Cozystack clusters to run the S3 service, powered by SeaweedFS.
|
||||
Thanks to the support for [integration with remote filer endpoints](https://cozystack.io/docs/operations/stretched/seaweedfs-multidc/),
|
||||
you can connect your primary Cozystack cluster to use S3 storage in a dedicated cluster.
|
||||
|
||||
For security, platform administrators can now configure the SeaweedFS application with
|
||||
a list of IP addresses or CIDR ranges that are allowed to access the filer service.
|
||||
|
||||
SeaweedFS has also been integrated into the monitoring stack and now has its own Grafana dashboard.
|
||||
Together, these enhancements help Cozystack users build a more reliable, scalable, and observable S3 service.
|
||||
|
||||
### ClickHouse Keeper
|
||||
|
||||
The ClickHouse application now includes a ClickHouse Keeper service to improve cluster reliability and availability.
|
||||
This component is deployed by default with every ClickHouse cluster.
|
||||
|
||||
Learn more in the [ClickHouse configuration reference](https://cozystack.io/docs/applications/clickhouse/#clickhouse-keeper-parameters).
|
||||
|
||||
## Major Features and Improvements
|
||||
|
||||
* [platform] Enable using external application packages by adding a `CozystackResourceDefinition` reconciler. Read the documentation on [adding external applications to Cozystack](https://cozystack.io/docs/applications/external/) to learn more. (@klinch0 in https://github.com/cozystack/cozystack/pull/1313)
|
||||
* [cozystack-api, apps] Add default values, clear type definitions, value constraints and other improvements to the OpenAPI specs and READMEs by migrating to [cozyvalue-gen](https://github.com/cozystack/cozyvalues-gen). (@kvaps and @NickVolynkin in https://github.com/cozystack/cozystack/pull/1216, https://github.com/cozystack/cozystack/pull/1314, https://github.com/cozystack/cozystack/pull/1316, https://github.com/cozystack/cozystack/pull/1321, and https://github.com/cozystack/cozystack/pull/1333)
|
||||
* [cozystack-api] Show default values from the OpenAPI spec in the application resources. (@kvaps in https://github.com/cozystack/cozystack/pull/1241)
|
||||
* [cozystack-api] Provide an API for administrators to define custom managed applications alongside existing managed apps. (@klinch in https://github.com/cozystack/cozystack/pull/1230)
|
||||
* [robotlb] Introduce the Hetzner RobotLB balancer. (@IvanHunters and @gwynbleidd2106 in https://github.com/cozystack/cozystack/pull/1233)
|
||||
* [platform, robotlb] Autodetect if node ports should be assigned to load balancer services. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1271)
|
||||
* [seaweedfs] Enable [integration with remote filer endpoints](https://cozystack.io/docs/operations/stretched/seaweedfs-multidc/) by adding new `Client` topology. (@kvaps in https://github.com/cozystack/cozystack/pull/1239)
|
||||
* [seaweedfs] Add support for whitelisting and exporting via nginx-ingress. Update cosi-driver. (@kvaps in https://github.com/cozystack/cozystack/pull/1277)
|
||||
* [monitoring, seaweedfs] Add monitoring and Grafana dashboard for SeaweedFS. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1285)
|
||||
* [clickhouse] Add the ClickHouse Keeper component. (@klinch0 in https://github.com/cozystack/cozystack/pull/1298 and https://github.com/cozystack/cozystack/pull/1320)
|
||||
|
||||
## Security
|
||||
|
||||
* [keycloak] Store administrative passwords in the management cluster's secrets. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1286)
|
||||
* [keycloak] Update Keycloak client redirect URI to use HTTPS instead of HTTP. Enable `cookie-secure`. (@klinch0 in https://github.com/cozystack/cozystack/pull/1287)
|
||||
|
||||
## Fixes
|
||||
|
||||
* [platform] Introduce a fixed 2-second delay at the start of reconciliation for system and tenant Helm operations. (@klinch0 in https://github.com/cozystack/cozystack/pull/1343)
|
||||
* [kubernetes] Add dependency for snapshot CRD and migration to the latest version. (@kvaps in https://github.com/cozystack/cozystack/pull/1275)
|
||||
* [kubernetes] Fix regression in `volumesnapshotclass` installation from https://github.com/cozystack/cozystack/pull/1203. (@kvaps in https://github.com/cozystack/cozystack/pull/1238)
|
||||
* [kubernetes] Resolve problems with pod names exceeding allowed length by shortening the name of volume snapshot CRD from `*-volumesnapshot-crd-for-tenant-k8s` to `*-vsnap-crd`. To apply this change, update each affected tenant Kubernetes cluster after updating Cozystack. (@klinch0 in https://github.com/cozystack/cozystack/pull/1284)
|
||||
* [kubernetes] Disable VPA for VPA in tenant Kubernetes clusters. Tenant clusters have no need for this feature, and it was not designed to work in a tenant cluster, but was enabled by mistake. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1301 and https://github.com/cozystack/cozystack/pull/1318)
|
||||
* [kamaji] Fix broken migration jobs originating from missing environment variables in the in-tree build. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1338)
|
||||
* [etcd] Fix the `topologySpreadConstraints` for etcd. (@klinch0 in https://github.com/cozystack/cozystack/pull/1331)
|
||||
* [tenant] Fix tenant network policy to allow traffic to additional tenant-related services across namespace hierarchies. (@klinch0 in https://github.com/cozystack/cozystack/pull/1232)
|
||||
* [tenant, monitoring] Improve the reliability of tenant monitoring by increasing the timeout and number of retries. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1294)
|
||||
* [kubevirt] Fix building KubeVirt CCM image. (@kvaps in https://github.com/cozystack/cozystack/commit/3c7e256906e1dbb0f957dc3a205fa77a147d419d)
|
||||
* [virtual-machine] Fix a regression with `optional=true` field. (@kvaps in https://github.com/cozystack/cozystack/commit/01053f7c3180d1bd045d7c5fb949984c2bdaf19d)
|
||||
* [virtual-machine] Enable using custom `instanceType` values in `virtual-machine` and `vm-instance` by disabling field validation. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1300, backported in https://github.com/cozystack/cozystack/pull/1303)
|
||||
* [cozystack-api] Show correct `kind` values of `ApplicationList`. (@kvaps in https://github.com/cozystack/cozystack/pull/1290)
|
||||
* [cozystack-api] Add missing roles to allow cozystack-controller to read Kubernetes deployments. (@klinch0 in https://github.com/cozystack/cozystack/pull/1342)
|
||||
* [linstor] Update LINSTOR monitoring configuration to use label `controller_node` instead of `node`. (@kvaps in https://github.com/cozystack/cozystack/pull/1326 and https://github.com/cozystack/cozystack/pull/1335)
|
||||
* [seaweedfs] Fix SeaweedFS volume configuration. Increase the volume size limit from 100MB to 30,000MB. (@kvaps in https://github.com/cozystack/cozystack/pull/1328)
|
||||
* [seaweedfs] Disable proxy buffering and proxy request buffering for ingress. (@kvaps in https://github.com/cozystack/cozystack/pull/1330)
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
* Update flux-operator to 0.28.0. (@kingdonb in https://github.com/cozystack/cozystack/pull/1315 and https://github.com/cozystack/cozystack/pull/1344)
|
||||
|
||||
## Documentation
|
||||
|
||||
* [Reimplement Cozystack Roadmap as a GitHub project](https://github.com/orgs/cozystack/projects/1). (@cozystack team)
|
||||
* [SeaweedFS Multi-DC Configuration](https://cozystack.io/docs/operations/stretched/seaweedfs-multidc/). (@kvaps and @NickVolynkin in https://github.com/cozystack/website/pull/272)
|
||||
* [Troubleshooting Kube-OVN](https://cozystack.io/docs/operations/troubleshooting/#kube-ovn-crash). (@kvaps and @NickVolynkin in https://github.com/cozystack/website/pull/273)
|
||||
* [Removing failed nodes from Cozystack cluster](https://cozystack.io/docs/operations/troubleshooting/#remove-a-failed-node-from-the-cluster). (@kvaps and @NickVolynkin in https://github.com/cozystack/website/pull/273)
|
||||
* [Installing Talos with `kexec`](https://cozystack.io/docs/talos/install/kexec/). (@kvaps and @NickVolynkin in https://github.com/cozystack/website/pull/268)
|
||||
* [Rewrite Cozystack tutorial](https://cozystack.io/docs/getting-started/). (@NickVolynkin in https://github.com/cozystack/website/pull/262 and https://github.com/cozystack/website/pull/268)
|
||||
* [How to install Cozystack in Hetzner](https://cozystack.io/docs/install/providers/hetzner/). (@NickVolynkin and @IvanHunters in https://github.com/cozystack/website/pull/280)
|
||||
* [Adding External Applications to Cozystack Catalog](https://cozystack.io/docs/applications/external/). (@klinch0 and @NickVolynkin in https://github.com/cozystack/website/pull/283)
|
||||
* [Creating and Using Named VM Images (Golden Images)](https://cozystack.io/docs/virtualization/vm-image/) (@NickVolynkin and @kvaps in https://github.com/cozystack/website/pull/276)
|
||||
* [Creating Encrypted Storage on LINSTOR](https://cozystack.io/docs/operations/storage/disk-encryption/). (@kvaps and @NickVolynkin in https://github.com/cozystack/website/pull/282)
|
||||
* [Adding and removing components on Cozystack installation using `bundle-enable` and `bundle-disable`](https://cozystack.io/docs/operations/bundles/#how-to-enable-and-disable-bundle-components) (@NickVolynkin in https://github.com/cozystack/website/pull/281)
|
||||
* Restructure Cozystack documentation. Bring [managed Kubernetes](https://cozystack.io/docs/kubernetes/), [managed applications](https://cozystack.io/docs/applications/), [virtualization](https://cozystack.io/docs/virtualization/), and [networking](https://cozystack.io/docs/networking/) guides to the top level. (@NickVolynkin in https://github.com/cozystack/website/pull/266)
|
||||
|
||||
|
||||
## Development, Testing, and CI/CD
|
||||
|
||||
* [tests] Add tests for S3 buckets. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1283)
|
||||
* [tests, ci] Simplify test discovery logic; run two k8s tests as separate jobs; delete Clickhouse application after a successful test. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1236)
|
||||
* [dx] When running `make` commands with `BUILDER` value specified, `PLATFORM` is optional. (@kvaps in https://github.com/cozystack/cozystack/pull/1288)
|
||||
* [tests] Fix resource specification in virtual machine tests. (@IvanHunters in https://github.com/cozystack/cozystack/pull/1308)
|
||||
* [tests] Increase available space for e2e tests. (@kvaps in https://github.com/cozystack/cozystack/commit/168a24ffdf1202b3bf2e7d2b5ef54b72b7403baf)
|
||||
* [tests, ci] Continue application tests after one of them fails. (@NickVolynkin in https://github.com/cozystack/cozystack/commit/634b77edad6c32c101f3e5daea6a5ffc0c83d904)
|
||||
* [ci] Use a subdomain of aenix.org for Nexus service in CI. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1322)
|
||||
|
||||
---
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.34.0...v0.35.0
|
||||
10
docs/changelogs/v0.35.1.md
Normal file
10
docs/changelogs/v0.35.1.md
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.35.1
|
||||
-->
|
||||
|
||||
## Fixes
|
||||
|
||||
* [cozy-lib] Fix malformed retrieval of `cozyConfig` in the cozy-lib template. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1348)
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.35.0...v0.35.1
|
||||
22
docs/changelogs/v0.35.2.md
Normal file
22
docs/changelogs/v0.35.2.md
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
<!--
|
||||
https://github.com/cozystack/cozystack/releases/tag/v0.35.2
|
||||
-->
|
||||
|
||||
## Features and Improvements
|
||||
|
||||
* [talos] Add LLDPD (`ghcr.io/siderolabs/lldpd`) as a built-in system extension, enabling LLDP-based neighbor discovery out of the box. (@lllamnyp in https://github.com/cozystack/cozystack/pull/1351 and https://github.com/cozystack/cozystack/pull/1360)
|
||||
|
||||
## Fixes
|
||||
|
||||
* [cozystack-api] Sanitize the OpenAPI v2 schema. (@kvaps in https://github.com/cozystack/cozystack/pull/1353)
|
||||
* [seaweedfs] Fix a problem where S3 gateway would be moved to an external pod, resulting in authentication failure. (@kvaps in https://github.com/cozystack/cozystack/pull/1361)
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
* Update LINSTOR to v1.31.3. (@kvaps in https://github.com/cozystack/cozystack/pull/1358)
|
||||
* Update SeaweedFS to v3.96. (@kvaps in https://github.com/cozystack/cozystack/pull/1361)
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/cozystack/cozystack/compare/v0.35.1...v0.35.2
|
||||
3
go.mod
3
go.mod
@@ -59,6 +59,7 @@ require (
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||
github.com/imdario/mergo v0.3.6 // indirect
|
||||
@@ -66,9 +67,11 @@ require (
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/moby/spdystream v0.4.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/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
|
||||
|
||||
6
go.sum
6
go.sum
@@ -2,6 +2,8 @@ github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cq
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -115,6 +117,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8=
|
||||
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -122,6 +126,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
||||
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
|
||||
|
||||
@@ -48,7 +48,7 @@ kubectl get ns --no-headers | awk '$2 != "Active"' |
|
||||
|
||||
echo "Collecting helmreleases..."
|
||||
kubectl get hr -A > $REPORT_DIR/kubernetes/helmreleases.txt 2>&1
|
||||
kubectl get hr -A | awk '$4 != "True"' | \
|
||||
kubectl get hr -A --no-headers | awk '$4 != "True"' | \
|
||||
while read NAMESPACE NAME _; do
|
||||
DIR=$REPORT_DIR/kubernetes/helmreleases/$NAMESPACE/$NAME
|
||||
mkdir -p $DIR
|
||||
@@ -105,7 +105,7 @@ kubectl get svc -A --no-headers | awk '$4 == "<pending>"' |
|
||||
|
||||
echo "Collecting pvcs..."
|
||||
kubectl get pvc -A > $REPORT_DIR/kubernetes/pvcs.txt 2>&1
|
||||
kubectl get pvc -A | awk '$3 != "Bound"' |
|
||||
kubectl get pvc -A --no-headers | awk '$3 != "Bound"' |
|
||||
while read NAMESPACE NAME _; do
|
||||
DIR=$REPORT_DIR/kubernetes/pvc/$NAMESPACE/$NAME
|
||||
mkdir -p $DIR
|
||||
|
||||
@@ -18,8 +18,8 @@ spec:
|
||||
EOF
|
||||
sleep 5
|
||||
kubectl -n tenant-test wait hr vm-disk-$name --timeout=5s --for=condition=ready
|
||||
kubectl -n tenant-test wait dv vm-disk-$name --timeout=150s --for=condition=ready
|
||||
kubectl -n tenant-test wait pvc vm-disk-$name --timeout=100s --for=jsonpath='{.status.phase}'=Bound
|
||||
kubectl -n tenant-test wait dv vm-disk-$name --timeout=250s --for=condition=ready
|
||||
kubectl -n tenant-test wait pvc vm-disk-$name --timeout=200s --for=jsonpath='{.status.phase}'=Bound
|
||||
}
|
||||
|
||||
@test "Create a VM Instance" {
|
||||
|
||||
@@ -123,6 +123,7 @@ EOF
|
||||
|
||||
@test "Configure Tenant and wait for applications" {
|
||||
# Patch root tenant and wait for its releases
|
||||
|
||||
kubectl patch tenants/root -n tenant-root --type merge -p '{"spec":{"host":"example.org","ingress":true,"monitoring":true,"etcd":true,"isolated":true, "seaweedfs": true}}'
|
||||
|
||||
timeout 60 sh -ec 'until kubectl get hr -n tenant-root etcd ingress monitoring seaweedfs tenant-root >/dev/null 2>&1; do sleep 1; done'
|
||||
@@ -139,6 +140,7 @@ EOF
|
||||
kubectl wait hr/seaweedfs-system -n tenant-root --timeout=2m --for=condition=ready
|
||||
fi
|
||||
|
||||
|
||||
# Expose Cozystack services through ingress
|
||||
kubectl patch configmap/cozystack -n cozy-system --type merge -p '{"data":{"expose-services":"api,dashboard,cdi-uploadproxy,vm-exportproxy,keycloak"}}'
|
||||
|
||||
@@ -150,7 +152,7 @@ EOF
|
||||
kubectl wait sts/etcd -n tenant-root --for=jsonpath='{.status.readyReplicas}'=3 --timeout=5m
|
||||
|
||||
# VictoriaMetrics components
|
||||
kubectl wait vmalert/vmalert-shortterm vmalertmanager/alertmanager -n tenant-root --for=jsonpath='{.status.updateStatus}'=operational --timeout=5m
|
||||
kubectl wait vmalert/vmalert-shortterm vmalertmanager/alertmanager -n tenant-root --for=jsonpath='{.status.updateStatus}'=operational --timeout=15m
|
||||
kubectl wait vlogs/generic -n tenant-root --for=jsonpath='{.status.updateStatus}'=operational --timeout=5m
|
||||
kubectl wait vmcluster/shortterm vmcluster/longterm -n tenant-root --for=jsonpath='{.status.clusterStatus}'=operational --timeout=5m
|
||||
|
||||
@@ -187,9 +189,22 @@ spec:
|
||||
ingress: false
|
||||
isolated: true
|
||||
monitoring: false
|
||||
resourceQuotas: {}
|
||||
resourceQuotas:
|
||||
cpu: "60"
|
||||
memory: "128Gi"
|
||||
storage: "100Gi"
|
||||
seaweedfs: false
|
||||
EOF
|
||||
kubectl wait hr/tenant-test -n tenant-root --timeout=1m --for=condition=ready
|
||||
kubectl wait namespace tenant-test --timeout=20s --for=jsonpath='{.status.phase}'=Active
|
||||
# Wait for ResourceQuota to appear and assert values
|
||||
timeout 60 sh -ec 'until [ "$(kubectl get quota -n tenant-test --no-headers 2>/dev/null | wc -l)" -ge 1 ]; do sleep 1; done'
|
||||
kubectl get quota -n tenant-test \
|
||||
-o jsonpath='{range .items[*]}{.spec.hard.requests\.memory}{" "}{.spec.hard.requests\.storage}{"\n"}{end}' \
|
||||
| grep -qx '137438953472 100Gi'
|
||||
|
||||
# Assert LimitRange defaults for containers
|
||||
kubectl get limitrange -n tenant-test \
|
||||
-o jsonpath='{range .items[*].spec.limits[*]}{.default.cpu}{" "}{.default.memory}{" "}{.defaultRequest.cpu}{" "}{.defaultRequest.memory}{"\n"}{end}' \
|
||||
| grep -qx '250m 128Mi 25m 128Mi'
|
||||
}
|
||||
|
||||
@@ -2,14 +2,25 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cozystack/cozystack/internal/shared/crdmem"
|
||||
|
||||
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
@@ -20,85 +31,55 @@ type CozystackResourceDefinitionReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
// Configurable debounce duration
|
||||
Debounce time.Duration
|
||||
|
||||
// Internal state for debouncing
|
||||
mu sync.Mutex
|
||||
lastEvent time.Time // Time of last CRUD event on CozystackResourceDefinition
|
||||
lastHandled time.Time // Last time the Deployment was actually restarted
|
||||
lastEvent time.Time
|
||||
lastHandled time.Time
|
||||
|
||||
mem *crdmem.Memory
|
||||
}
|
||||
|
||||
// Reconcile handles the logic to restart the target Deployment only once,
|
||||
// even if multiple events occur close together
|
||||
func (r *CozystackResourceDefinitionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
log := log.FromContext(ctx)
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
// Only respond to our target deployment
|
||||
if req.Namespace != "cozy-system" || req.Name != "cozystack-api" {
|
||||
crd := &cozyv1alpha1.CozystackResourceDefinition{}
|
||||
err := r.Get(ctx, types.NamespacedName{Name: req.Name}, crd)
|
||||
if err == nil {
|
||||
if r.mem != nil {
|
||||
r.mem.Upsert(crd)
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
r.lastEvent = time.Now()
|
||||
r.mu.Unlock()
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
le := r.lastEvent
|
||||
lh := r.lastHandled
|
||||
debounce := r.Debounce
|
||||
r.mu.Unlock()
|
||||
|
||||
if debounce <= 0 {
|
||||
debounce = 5 * time.Second
|
||||
}
|
||||
|
||||
// No events received yet — nothing to do
|
||||
if le.IsZero() {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// Wait until the debounce duration has passed since the last event
|
||||
if d := time.Since(le); d < debounce {
|
||||
return ctrl.Result{RequeueAfter: debounce - d}, nil
|
||||
}
|
||||
|
||||
// Already handled this event — skip restart
|
||||
if !lh.Before(le) {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// Perform the restart by patching the deployment annotation
|
||||
deploy := &appsv1.Deployment{}
|
||||
if err := r.Get(ctx, types.NamespacedName{Namespace: "cozy-system", Name: "cozystack-api"}, deploy); err != nil {
|
||||
log.Error(err, "Failed to get Deployment cozy-system/cozystack-api")
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
patch := client.MergeFrom(deploy.DeepCopy())
|
||||
if deploy.Spec.Template.Annotations == nil {
|
||||
deploy.Spec.Template.Annotations = make(map[string]string)
|
||||
}
|
||||
deploy.Spec.Template.Annotations["kubectl.kubernetes.io/restartedAt"] = time.Now().Format(time.RFC3339)
|
||||
|
||||
if err := r.Patch(ctx, deploy, patch); err != nil {
|
||||
log.Error(err, "Failed to patch Deployment annotation")
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Mark this event as handled
|
||||
r.mu.Lock()
|
||||
r.lastHandled = le
|
||||
r.mu.Unlock()
|
||||
|
||||
log.Info("Deployment cozy-system/cozystack-api successfully restarted")
|
||||
if apierrors.IsNotFound(err) && r.mem != nil {
|
||||
r.mem.Delete(req.Name)
|
||||
}
|
||||
if req.Namespace == "cozy-system" && req.Name == "cozystack-api" {
|
||||
return r.debouncedRestart(ctx, logger)
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// SetupWithManager configures how the controller listens to events
|
||||
func (r *CozystackResourceDefinitionReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
if r.Debounce == 0 {
|
||||
r.Debounce = 5 * time.Second
|
||||
}
|
||||
|
||||
if r.mem == nil {
|
||||
r.mem = crdmem.Global()
|
||||
}
|
||||
if err := r.mem.EnsurePrimingWithManager(mgr); err != nil {
|
||||
return err
|
||||
}
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
Named("cozystack-restart-controller").
|
||||
Named("cozystackresource-controller").
|
||||
For(&cozyv1alpha1.CozystackResourceDefinition{}, builder.WithPredicates()).
|
||||
Watches(
|
||||
&cozyv1alpha1.CozystackResourceDefinition{},
|
||||
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
|
||||
@@ -115,3 +96,88 @@ func (r *CozystackResourceDefinitionReconciler) SetupWithManager(mgr ctrl.Manage
|
||||
).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
type crdHashView struct {
|
||||
Name string `json:"name"`
|
||||
Spec cozyv1alpha1.CozystackResourceDefinitionSpec `json:"spec"`
|
||||
}
|
||||
|
||||
func (r *CozystackResourceDefinitionReconciler) computeConfigHash() (string, error) {
|
||||
if r.mem == nil {
|
||||
return "", nil
|
||||
}
|
||||
snapshot := r.mem.Snapshot()
|
||||
sort.Slice(snapshot, func(i, j int) bool { return snapshot[i].Name < snapshot[j].Name })
|
||||
views := make([]crdHashView, 0, len(snapshot))
|
||||
for i := range snapshot {
|
||||
views = append(views, crdHashView{
|
||||
Name: snapshot[i].Name,
|
||||
Spec: snapshot[i].Spec,
|
||||
})
|
||||
}
|
||||
b, err := json.Marshal(views)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sum := sha256.Sum256(b)
|
||||
return hex.EncodeToString(sum[:]), nil
|
||||
}
|
||||
|
||||
func (r *CozystackResourceDefinitionReconciler) debouncedRestart(ctx context.Context, logger logr.Logger) (ctrl.Result, error) {
|
||||
r.mu.Lock()
|
||||
le := r.lastEvent
|
||||
lh := r.lastHandled
|
||||
debounce := r.Debounce
|
||||
r.mu.Unlock()
|
||||
|
||||
if debounce <= 0 {
|
||||
debounce = 5 * time.Second
|
||||
}
|
||||
if le.IsZero() {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
if d := time.Since(le); d < debounce {
|
||||
return ctrl.Result{RequeueAfter: debounce - d}, nil
|
||||
}
|
||||
if !lh.Before(le) {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
newHash, err := r.computeConfigHash()
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
deploy := &appsv1.Deployment{}
|
||||
if err := r.Get(ctx, types.NamespacedName{Namespace: "cozy-system", Name: "cozystack-api"}, deploy); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
if deploy.Spec.Template.Annotations == nil {
|
||||
deploy.Spec.Template.Annotations = map[string]string{}
|
||||
}
|
||||
oldHash := deploy.Spec.Template.Annotations["cozystack.io/config-hash"]
|
||||
|
||||
if oldHash == newHash && oldHash != "" {
|
||||
r.mu.Lock()
|
||||
r.lastHandled = le
|
||||
r.mu.Unlock()
|
||||
logger.Info("No changes in CRD config; skipping restart", "hash", newHash)
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
patch := client.MergeFrom(deploy.DeepCopy())
|
||||
deploy.Spec.Template.Annotations["cozystack.io/config-hash"] = newHash
|
||||
|
||||
if err := r.Patch(ctx, deploy, patch); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
r.lastHandled = le
|
||||
r.mu.Unlock()
|
||||
|
||||
logger.Info("Updated cozystack-api podTemplate config-hash; rollout triggered",
|
||||
"old", oldHash, "new", newHash)
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
280
internal/controller/kubeovnplunger/kubeovn_plunger.go
Normal file
280
internal/controller/kubeovnplunger/kubeovn_plunger.go
Normal file
@@ -0,0 +1,280 @@
|
||||
package kubeovnplunger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cozystack/cozystack/internal/sse"
|
||||
"github.com/cozystack/cozystack/pkg/ovnstatus"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
)
|
||||
|
||||
var (
|
||||
srv *sse.Server
|
||||
)
|
||||
|
||||
const (
|
||||
rescanInterval = 1 * time.Minute
|
||||
)
|
||||
|
||||
// KubeOVNPlunger watches the ovn-central cluster members
|
||||
type KubeOVNPlunger struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
ClientSet kubernetes.Interface
|
||||
REST *rest.Config
|
||||
Registry prometheus.Registerer
|
||||
metrics metrics
|
||||
lastLeader map[string]string
|
||||
seenCIDs map[string]map[string]struct{}
|
||||
}
|
||||
|
||||
// Reconcile runs the checks on the ovn-central members to see if their views of the cluster are consistent
|
||||
func (r *KubeOVNPlunger) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
deploy := &appsv1.Deployment{}
|
||||
if err := r.Get(ctx, req.NamespacedName, deploy); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
iphints := map[string]string{}
|
||||
for _, env := range deploy.Spec.Template.Spec.Containers[0].Env {
|
||||
if env.Name != "NODE_IPS" {
|
||||
continue
|
||||
}
|
||||
for _, ip := range strings.Split(env.Value, ",") {
|
||||
iphints[ip] = ""
|
||||
}
|
||||
break
|
||||
}
|
||||
if len(iphints) == 0 {
|
||||
l.Info("WARNING: running without IP hints, some error conditions cannot be detected")
|
||||
}
|
||||
pods := &corev1.PodList{}
|
||||
|
||||
if err := r.List(ctx, pods, client.InNamespace(req.Namespace), client.MatchingLabels(map[string]string{"app": req.Name})); err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("list ovn-central pods: %w", err)
|
||||
}
|
||||
|
||||
nbmv := make([]ovnstatus.MemberView, 0, len(pods.Items))
|
||||
sbmv := make([]ovnstatus.MemberView, 0, len(pods.Items))
|
||||
nbSnaps := make([]ovnstatus.HealthSnapshot, 0, len(pods.Items))
|
||||
sbSnaps := make([]ovnstatus.HealthSnapshot, 0, len(pods.Items))
|
||||
// TODO: get real iphints
|
||||
for i := range pods.Items {
|
||||
o := ovnstatus.OVNClient{}
|
||||
o.ApplyDefaults()
|
||||
o.Runner = func(ctx context.Context, bin string, args ...string) (string, error) {
|
||||
cmd := append([]string{bin}, args...)
|
||||
eo := ExecOptions{
|
||||
Namespace: req.Namespace,
|
||||
Pod: pods.Items[i].Name,
|
||||
Container: pods.Items[i].Spec.Containers[0].Name,
|
||||
Command: cmd,
|
||||
}
|
||||
res, err := r.ExecPod(ctx, eo)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return res.Stdout, nil
|
||||
}
|
||||
nb, sb, err1, err2 := o.HealthBoth(ctx)
|
||||
if err1 != nil || err2 != nil {
|
||||
l.Error(fmt.Errorf("health check failed: nb=%w, sb=%w", err1, err2), "pod", pods.Items[i].Name)
|
||||
continue
|
||||
}
|
||||
nbSnaps = append(nbSnaps, nb)
|
||||
sbSnaps = append(sbSnaps, sb)
|
||||
nbmv = append(nbmv, ovnstatus.BuildMemberView(nb))
|
||||
sbmv = append(sbmv, ovnstatus.BuildMemberView(sb))
|
||||
}
|
||||
r.recordAndPruneCIDs("nb", cidFromSnaps(nbSnaps))
|
||||
r.recordAndPruneCIDs("sb", cidFromSnaps(sbSnaps))
|
||||
nbmv = ovnstatus.NormalizeViews(nbmv)
|
||||
sbmv = ovnstatus.NormalizeViews(sbmv)
|
||||
nbecv := ovnstatus.AnalyzeConsensusWithIPHints(nbmv, &ovnstatus.Hints{ExpectedIPs: iphints})
|
||||
sbecv := ovnstatus.AnalyzeConsensusWithIPHints(sbmv, &ovnstatus.Hints{ExpectedIPs: iphints})
|
||||
expected := len(iphints)
|
||||
r.WriteClusterMetrics("nb", nbSnaps, nbecv, expected)
|
||||
r.WriteClusterMetrics("sb", sbSnaps, sbecv, expected)
|
||||
r.WriteMemberMetrics("nb", nbSnaps, nbmv, nbecv)
|
||||
r.WriteMemberMetrics("sb", sbSnaps, sbmv, sbecv)
|
||||
srv.Publish(nbecv.PrettyString() + sbecv.PrettyString())
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// SetupWithManager attaches a generic ticker to trigger a reconcile every <interval> seconds
|
||||
func (r *KubeOVNPlunger) SetupWithManager(mgr ctrl.Manager, kubeOVNNamespace, appName string) error {
|
||||
r.REST = rest.CopyConfig(mgr.GetConfig())
|
||||
cs, err := kubernetes.NewForConfig(r.REST)
|
||||
if err != nil {
|
||||
return fmt.Errorf("build clientset: %w", err)
|
||||
}
|
||||
r.ClientSet = cs
|
||||
ch := make(chan event.GenericEvent, 10)
|
||||
mapFunc := func(context.Context, client.Object) []reconcile.Request {
|
||||
return []reconcile.Request{{
|
||||
NamespacedName: types.NamespacedName{Namespace: kubeOVNNamespace, Name: appName},
|
||||
}}
|
||||
}
|
||||
mapper := handler.EnqueueRequestsFromMapFunc(mapFunc)
|
||||
srv = sse.New(sse.Options{
|
||||
Addr: ":18080",
|
||||
AllowCORS: true,
|
||||
})
|
||||
r.initMetrics()
|
||||
r.lastLeader = make(map[string]string)
|
||||
r.seenCIDs = map[string]map[string]struct{}{"nb": {}, "sb": {}}
|
||||
if err := ctrl.NewControllerManagedBy(mgr).
|
||||
Named("kubeovnplunger").
|
||||
WatchesRawSource(source.Channel(ch, mapper)).
|
||||
Complete(r); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = mgr.Add(manager.RunnableFunc(func(ctx context.Context) error {
|
||||
go srv.ListenAndServe()
|
||||
<-ctx.Done()
|
||||
_ = srv.Shutdown(context.Background())
|
||||
return nil
|
||||
}))
|
||||
return mgr.Add(manager.RunnableFunc(func(ctx context.Context) error {
|
||||
ticker := time.NewTicker(rescanInterval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-ticker.C:
|
||||
ch <- event.GenericEvent{
|
||||
Object: &metav1.PartialObjectMetadata{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: kubeOVNNamespace,
|
||||
Name: appName,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
type ExecOptions struct {
|
||||
Namespace string
|
||||
Pod string
|
||||
Container string
|
||||
Command []string // e.g. []string{"sh", "-c", "echo hi"}
|
||||
Stdin io.Reader // optional
|
||||
TTY bool // if true, stderr is merged into stdout
|
||||
Timeout time.Duration // optional overall timeout
|
||||
}
|
||||
|
||||
type ExecResult struct {
|
||||
Stdout string
|
||||
Stderr string
|
||||
ExitCode *int // nil if not determinable
|
||||
}
|
||||
|
||||
// ExecPod runs a command in a pod and returns stdout/stderr/exit code.
|
||||
func (r *KubeOVNPlunger) ExecPod(ctx context.Context, opts ExecOptions) (*ExecResult, error) {
|
||||
if opts.Namespace == "" || opts.Pod == "" || opts.Container == "" {
|
||||
return nil, fmt.Errorf("namespace, pod, and container are required")
|
||||
}
|
||||
|
||||
req := r.ClientSet.CoreV1().RESTClient().
|
||||
Post().
|
||||
Resource("pods").
|
||||
Namespace(opts.Namespace).
|
||||
Name(opts.Pod).
|
||||
SubResource("exec").
|
||||
VersionedParams(&corev1.PodExecOptions{
|
||||
Container: opts.Container,
|
||||
Command: opts.Command,
|
||||
Stdin: opts.Stdin != nil,
|
||||
Stdout: true,
|
||||
Stderr: !opts.TTY,
|
||||
TTY: opts.TTY,
|
||||
}, scheme.ParameterCodec)
|
||||
|
||||
exec, err := remotecommand.NewSPDYExecutor(r.REST, "POST", req.URL())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("spdy executor: %w", err)
|
||||
}
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
streamCtx := ctx
|
||||
if opts.Timeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
streamCtx, cancel = context.WithTimeout(ctx, opts.Timeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
streamErr := exec.StreamWithContext(streamCtx, remotecommand.StreamOptions{
|
||||
Stdin: opts.Stdin,
|
||||
Stdout: &stdout,
|
||||
Stderr: &stderr,
|
||||
Tty: opts.TTY,
|
||||
})
|
||||
|
||||
res := &ExecResult{Stdout: stdout.String(), Stderr: stderr.String()}
|
||||
if streamErr != nil {
|
||||
// Try to surface exit code instead of treating all failures as transport errors
|
||||
type exitCoder interface{ ExitStatus() int }
|
||||
if ec, ok := streamErr.(exitCoder); ok {
|
||||
code := ec.ExitStatus()
|
||||
res.ExitCode = &code
|
||||
return res, nil
|
||||
}
|
||||
return res, fmt.Errorf("exec stream: %w", streamErr)
|
||||
}
|
||||
zero := 0
|
||||
res.ExitCode = &zero
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (r *KubeOVNPlunger) recordAndPruneCIDs(db, currentCID string) {
|
||||
|
||||
// Mark current as seen
|
||||
if r.seenCIDs[db] == nil {
|
||||
r.seenCIDs[db] = map[string]struct{}{}
|
||||
}
|
||||
if currentCID != "" {
|
||||
r.seenCIDs[db][currentCID] = struct{}{}
|
||||
}
|
||||
|
||||
// Build a set of "still active" CIDs this cycle (could be none if you failed to collect)
|
||||
active := map[string]struct{}{}
|
||||
if currentCID != "" {
|
||||
active[currentCID] = struct{}{}
|
||||
}
|
||||
|
||||
// Any seen CID that isn't active now is stale -> delete all its series
|
||||
for cid := range r.seenCIDs[db] {
|
||||
if _, ok := active[cid]; ok {
|
||||
continue
|
||||
}
|
||||
r.deleteAllFor(db, cid)
|
||||
delete(r.seenCIDs[db], cid)
|
||||
}
|
||||
}
|
||||
34
internal/controller/kubeovnplunger/kubeovn_plunger_test.go
Normal file
34
internal/controller/kubeovnplunger/kubeovn_plunger_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package kubeovnplunger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
var testPlunger *KubeOVNPlunger
|
||||
|
||||
func init() {
|
||||
scheme := runtime.NewScheme()
|
||||
cfg := config.GetConfigOrDie()
|
||||
c, _ := client.New(cfg, client.Options{})
|
||||
cs, _ := kubernetes.NewForConfig(cfg)
|
||||
testPlunger = &KubeOVNPlunger{
|
||||
Client: c,
|
||||
Scheme: scheme,
|
||||
ClientSet: cs,
|
||||
REST: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlungerGetsStatuses(t *testing.T) {
|
||||
_, err := testPlunger.Reconcile(context.Background(), ctrl.Request{})
|
||||
if err != nil {
|
||||
t.Errorf("error should be nil but it's %s", err)
|
||||
}
|
||||
}
|
||||
423
internal/controller/kubeovnplunger/metrics.go
Normal file
423
internal/controller/kubeovnplunger/metrics.go
Normal file
@@ -0,0 +1,423 @@
|
||||
package kubeovnplunger
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/cozystack/cozystack/pkg/ovnstatus"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
)
|
||||
|
||||
type metrics struct {
|
||||
// --- Core cluster health (per DB/cid) ---
|
||||
clusterQuorum *prometheus.GaugeVec // 1/0
|
||||
allAgree *prometheus.GaugeVec // 1/0
|
||||
membersExpected *prometheus.GaugeVec
|
||||
membersObserved *prometheus.GaugeVec
|
||||
ipsExpected *prometheus.GaugeVec
|
||||
ipsObserved *prometheus.GaugeVec
|
||||
excessMembers *prometheus.GaugeVec
|
||||
missingMembers *prometheus.GaugeVec
|
||||
unexpectedIPsCount *prometheus.GaugeVec
|
||||
missingExpectedIPsCount *prometheus.GaugeVec
|
||||
ipConflictsCount *prometheus.GaugeVec
|
||||
sidAddrDisagreements *prometheus.GaugeVec
|
||||
|
||||
// --- Consensus summary (per DB/cid) ---
|
||||
consensusMajoritySize *prometheus.GaugeVec
|
||||
consensusMinoritySize *prometheus.GaugeVec
|
||||
consensusDiffsTotal *prometheus.GaugeVec
|
||||
|
||||
// --- Detail exports (sparse, keyed by IP/SID) ---
|
||||
unexpectedIPGauge *prometheus.GaugeVec // {db,cid,ip} -> 1
|
||||
missingExpectedIPGauge *prometheus.GaugeVec // {db,cid,ip} -> 1
|
||||
ipConflictGauge *prometheus.GaugeVec // {db,cid,ip} -> count(sids)
|
||||
suspectStaleGauge *prometheus.GaugeVec // {db,cid,sid} -> 1
|
||||
|
||||
// --- Per-member liveness/freshness (per DB/cid/sid[/ip]) ---
|
||||
memberConnected *prometheus.GaugeVec // {db,cid,sid,ip}
|
||||
memberLeader *prometheus.GaugeVec // {db,cid,sid}
|
||||
memberLastMsgMs *prometheus.GaugeVec // {db,cid,sid}
|
||||
memberIndex *prometheus.GaugeVec // {db,cid,sid}
|
||||
memberIndexGap *prometheus.GaugeVec // {db,cid,sid}
|
||||
memberReporter *prometheus.GaugeVec // {db,cid,sid}
|
||||
memberMissingReporter *prometheus.GaugeVec // {db,cid,sid}
|
||||
|
||||
// --- Ops/housekeeping ---
|
||||
leaderTransitionsTotal *prometheus.CounterVec // {db,cid}
|
||||
collectErrorsTotal *prometheus.CounterVec // {db,cid}
|
||||
publishEventsTotal *prometheus.CounterVec // {db,cid}
|
||||
snapshotTimestampSec *prometheus.GaugeVec // {db,cid}
|
||||
}
|
||||
|
||||
func (r *KubeOVNPlunger) initMetrics() {
|
||||
p := promauto.With(r.Registry)
|
||||
|
||||
ns := "ovn"
|
||||
|
||||
// --- Core cluster health ---
|
||||
r.metrics.clusterQuorum = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "quorum",
|
||||
Help: "1 if cluster has quorum, else 0",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.allAgree = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "all_agree",
|
||||
Help: "1 if all members report identical membership",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.membersExpected = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "members_expected",
|
||||
Help: "Expected cluster size (replicas)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.membersObserved = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "members_observed",
|
||||
Help: "Observed members (distinct SIDs across views)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.ipsExpected = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "ips_expected",
|
||||
Help: "Expected distinct member IPs (from k8s hints)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.ipsObserved = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "ips_observed",
|
||||
Help: "Observed distinct member IPs (from OVN views)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.excessMembers = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "excess_members",
|
||||
Help: "Members over expected (>=0)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.missingMembers = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "missing_members",
|
||||
Help: "Members short of expected (>=0)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.unexpectedIPsCount = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "unexpected_ips",
|
||||
Help: "Count of IPs in OVN not present in k8s expected set",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.missingExpectedIPsCount = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "missing_expected_ips",
|
||||
Help: "Count of expected IPs not found in OVN",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.ipConflictsCount = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "ip_conflicts",
|
||||
Help: "Number of IPs claimed by multiple SIDs",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.sidAddrDisagreements = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "cluster", Name: "sid_address_disagreements",
|
||||
Help: "Number of SIDs seen with >1 distinct addresses",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
// --- Consensus summary ---
|
||||
r.metrics.consensusMajoritySize = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "consensus", Name: "majority_size",
|
||||
Help: "Majority group size (0 if none)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.consensusMinoritySize = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "consensus", Name: "minority_size",
|
||||
Help: "Minority group size",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.consensusDiffsTotal = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "consensus", Name: "diffs_total",
|
||||
Help: "Total per-reporter differences vs truth (missing + extra + mismatches)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
// --- Detail exports (sparse) ---
|
||||
r.metrics.unexpectedIPGauge = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "consensus", Name: "unexpected_ip",
|
||||
Help: "Unexpected IP present in OVN; value fixed at 1",
|
||||
}, []string{"db", "cid", "ip"})
|
||||
|
||||
r.metrics.missingExpectedIPGauge = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "consensus", Name: "missing_expected_ip",
|
||||
Help: "Expected IP missing from OVN; value fixed at 1",
|
||||
}, []string{"db", "cid", "ip"})
|
||||
|
||||
r.metrics.ipConflictGauge = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "consensus", Name: "ip_conflict",
|
||||
Help: "Number of SIDs claiming the same IP for this key",
|
||||
}, []string{"db", "cid", "ip"})
|
||||
|
||||
r.metrics.suspectStaleGauge = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "consensus", Name: "suspect_stale",
|
||||
Help: "Suspected stale SID candidate for kick; value fixed at 1 (emit only when remediation is warranted)",
|
||||
}, []string{"db", "cid", "sid"})
|
||||
|
||||
// --- Per-member liveness/freshness ---
|
||||
r.metrics.memberConnected = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "member", Name: "connected",
|
||||
Help: "1 if local server reports connected/quorum, else 0",
|
||||
}, []string{"db", "cid", "sid", "ip"})
|
||||
|
||||
r.metrics.memberLeader = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "member", Name: "leader",
|
||||
Help: "1 if this member is leader, else 0",
|
||||
}, []string{"db", "cid", "sid"})
|
||||
|
||||
r.metrics.memberLastMsgMs = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "member", Name: "last_msg_ms",
|
||||
Help: "Follower->leader 'last msg' age in ms (legacy heuristic). NaN/omit if unknown",
|
||||
}, []string{"db", "cid", "sid"})
|
||||
|
||||
r.metrics.memberIndex = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "member", Name: "index",
|
||||
Help: "Local Raft log index",
|
||||
}, []string{"db", "cid", "sid"})
|
||||
|
||||
r.metrics.memberIndexGap = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "member", Name: "index_gap",
|
||||
Help: "Leader index minus local index (>=0)",
|
||||
}, []string{"db", "cid", "sid"})
|
||||
|
||||
r.metrics.memberReporter = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "member", Name: "reporter",
|
||||
Help: "1 if a self-view from this SID was collected in the scrape cycle",
|
||||
}, []string{"db", "cid", "sid"})
|
||||
|
||||
r.metrics.memberMissingReporter = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "member", Name: "missing_reporter",
|
||||
Help: "1 if SID appears in union but produced no self-view",
|
||||
}, []string{"db", "cid", "sid"})
|
||||
|
||||
// --- Ops/housekeeping ---
|
||||
r.metrics.leaderTransitionsTotal = p.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: ns, Subsystem: "ops", Name: "leader_transitions_total",
|
||||
Help: "Count of observed leader SID changes",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.collectErrorsTotal = p.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: ns, Subsystem: "ops", Name: "collect_errors_total",
|
||||
Help: "Count of errors during health collection/analysis",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.publishEventsTotal = p.NewCounterVec(prometheus.CounterOpts{
|
||||
Namespace: ns, Subsystem: "ops", Name: "publish_events_total",
|
||||
Help: "Count of SSE publish events (optional)",
|
||||
}, []string{"db", "cid"})
|
||||
|
||||
r.metrics.snapshotTimestampSec = p.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Namespace: ns, Subsystem: "ops", Name: "snapshot_timestamp_seconds",
|
||||
Help: "Unix timestamp of the last successful consensus snapshot",
|
||||
}, []string{"db", "cid"})
|
||||
}
|
||||
|
||||
func (r *KubeOVNPlunger) WriteClusterMetrics(db string, snaps []ovnstatus.HealthSnapshot, ecv ovnstatus.ExtendedConsensusResult, expectedReplicas int) {
|
||||
cid := cidFromSnaps(snaps)
|
||||
|
||||
// Core cluster health
|
||||
r.metrics.clusterQuorum.WithLabelValues(db, cid).Set(b2f(ecv.HasMajority))
|
||||
r.metrics.allAgree.WithLabelValues(db, cid).Set(b2f(ecv.AllAgree))
|
||||
r.metrics.membersExpected.WithLabelValues(db, cid).Set(float64(expectedReplicas))
|
||||
r.metrics.membersObserved.WithLabelValues(db, cid).Set(float64(ecv.MembersCount))
|
||||
r.metrics.ipsExpected.WithLabelValues(db, cid).Set(float64(len(ecv.ConsensusResult.TruthView.Members))) // optional; or len(hints.ExpectedIPs)
|
||||
r.metrics.ipsObserved.WithLabelValues(db, cid).Set(float64(ecv.DistinctIPCount))
|
||||
r.metrics.excessMembers.WithLabelValues(db, cid).Set(float64(ecv.ExpectedExcess))
|
||||
r.metrics.missingMembers.WithLabelValues(db, cid).Set(float64(ecv.ExpectedShortfall))
|
||||
r.metrics.unexpectedIPsCount.WithLabelValues(db, cid).Set(float64(len(ecv.UnexpectedIPs)))
|
||||
r.metrics.missingExpectedIPsCount.WithLabelValues(db, cid).Set(float64(len(ecv.MissingExpectedIPs)))
|
||||
r.metrics.ipConflictsCount.WithLabelValues(db, cid).Set(float64(len(ecv.IPConflicts)))
|
||||
|
||||
// Count SIDs with >1 distinct addresses
|
||||
disagree := 0
|
||||
for _, n := range ecv.SIDAddressDisagreements {
|
||||
if n > 1 {
|
||||
disagree++
|
||||
}
|
||||
}
|
||||
r.metrics.sidAddrDisagreements.WithLabelValues(db, cid).Set(float64(disagree))
|
||||
|
||||
// Consensus summary
|
||||
r.metrics.consensusMajoritySize.WithLabelValues(db, cid).Set(float64(len(ecv.MajorityMembers)))
|
||||
r.metrics.consensusMinoritySize.WithLabelValues(db, cid).Set(float64(len(ecv.MinorityMembers)))
|
||||
|
||||
// Sum diffs across reporters (missing + extra + mismatches)
|
||||
totalDiffs := 0
|
||||
for _, d := range ecv.Diffs {
|
||||
totalDiffs += len(d.MissingSIDs) + len(d.ExtraSIDs) + len(d.AddressMismatches)
|
||||
}
|
||||
r.metrics.consensusDiffsTotal.WithLabelValues(db, cid).Set(float64(totalDiffs))
|
||||
|
||||
// Sparse per-key exports (reset then re-emit for this {db,cid})
|
||||
r.metrics.unexpectedIPGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
for _, ip := range ecv.UnexpectedIPs {
|
||||
r.metrics.unexpectedIPGauge.WithLabelValues(db, cid, ip).Set(1)
|
||||
}
|
||||
|
||||
r.metrics.missingExpectedIPGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
for _, ip := range ecv.MissingExpectedIPs {
|
||||
r.metrics.missingExpectedIPGauge.WithLabelValues(db, cid, ip).Set(1)
|
||||
}
|
||||
|
||||
r.metrics.ipConflictGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
for ip, sids := range ecv.IPConflicts {
|
||||
r.metrics.ipConflictGauge.WithLabelValues(db, cid, ip).Set(float64(len(sids)))
|
||||
}
|
||||
|
||||
// Only emit suspects when remediation is warranted (e.g., TooManyMembers / unexpected IPs / conflicts)
|
||||
r.metrics.suspectStaleGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
if ecv.TooManyMembers || len(ecv.UnexpectedIPs) > 0 || len(ecv.IPConflicts) > 0 {
|
||||
for _, sid := range ecv.SuspectStaleSIDs {
|
||||
r.metrics.suspectStaleGauge.WithLabelValues(db, cid, sid).Set(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshot timestamp
|
||||
r.metrics.snapshotTimestampSec.WithLabelValues(db, cid).Set(float64(time.Now().Unix()))
|
||||
}
|
||||
|
||||
func (r *KubeOVNPlunger) WriteMemberMetrics(db string, snaps []ovnstatus.HealthSnapshot, views []ovnstatus.MemberView, ecv ovnstatus.ExtendedConsensusResult) {
|
||||
cid := cidFromSnaps(snaps)
|
||||
|
||||
// Figure out current leader SID (prefer local view from any leader snapshot)
|
||||
curLeader := ""
|
||||
for _, s := range snaps {
|
||||
if s.Local.Leader {
|
||||
curLeader = s.Local.SID
|
||||
break
|
||||
}
|
||||
}
|
||||
// Leader transitions
|
||||
key := db + "|" + cid
|
||||
if prev, ok := r.lastLeader[key]; ok && prev != "" && curLeader != "" && prev != curLeader {
|
||||
r.metrics.leaderTransitionsTotal.WithLabelValues(db, cid).Inc()
|
||||
}
|
||||
if curLeader != "" {
|
||||
r.lastLeader[key] = curLeader
|
||||
}
|
||||
|
||||
// Build quick maps for reporter set & IP per SID (best-effort)
|
||||
reporter := map[string]struct{}{}
|
||||
for _, v := range views {
|
||||
if v.FromSID != "" {
|
||||
reporter[v.FromSID] = struct{}{}
|
||||
}
|
||||
}
|
||||
sidToIP := map[string]string{}
|
||||
for _, v := range views {
|
||||
for sid, addr := range v.Members {
|
||||
if sidToIP[sid] == "" && addr != "" {
|
||||
sidToIP[sid] = ovnstatus.AddrToIP(addr) // expose addrToIP or wrap here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset member vectors for this {db,cid} (avoid stale series)
|
||||
r.metrics.memberConnected.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberLeader.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberLastMsgMs.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberIndex.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberIndexGap.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberReporter.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberMissingReporter.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
|
||||
// Leader index (to compute gaps)
|
||||
lIdx := leaderIndex(snaps, curLeader)
|
||||
|
||||
// Emit one series per snapshot (self view)
|
||||
for _, s := range snaps {
|
||||
sid := s.Local.SID
|
||||
ip := sidToIP[sid]
|
||||
if ip == "" {
|
||||
ip = "unknown"
|
||||
}
|
||||
|
||||
r.metrics.memberConnected.WithLabelValues(db, cid, sid, ip).Set(b2f(s.Local.Connected))
|
||||
r.metrics.memberLeader.WithLabelValues(db, cid, sid).Set(b2f(s.Local.Leader))
|
||||
r.metrics.memberIndex.WithLabelValues(db, cid, sid).Set(float64(s.Local.Index))
|
||||
|
||||
if lIdx != nil && s.Local.Index >= 0 {
|
||||
gap := *lIdx - s.Local.Index
|
||||
if gap < 0 {
|
||||
gap = 0
|
||||
}
|
||||
r.metrics.memberIndexGap.WithLabelValues(db, cid, sid).Set(float64(gap))
|
||||
}
|
||||
|
||||
// Reporter presence
|
||||
_, isReporter := reporter[sid]
|
||||
r.metrics.memberReporter.WithLabelValues(db, cid, sid).Set(b2f(isReporter))
|
||||
}
|
||||
|
||||
// “Missing reporter” SIDs = union − reporters (from ecv)
|
||||
reporterSet := map[string]struct{}{}
|
||||
for sid := range reporter {
|
||||
reporterSet[sid] = struct{}{}
|
||||
}
|
||||
unionSet := map[string]struct{}{}
|
||||
for _, sid := range ecv.UnionMembers {
|
||||
unionSet[sid] = struct{}{}
|
||||
}
|
||||
for sid := range unionSet {
|
||||
if _, ok := reporterSet[sid]; !ok {
|
||||
r.metrics.memberMissingReporter.WithLabelValues(db, cid, sid).Set(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy follower freshness (if you kept LastMsgMs in servers parsing)
|
||||
// We only know LastMsgMs from the Full.Servers in each snapshot; pick the freshest per SID.
|
||||
lastMsg := map[string]int64{}
|
||||
for _, s := range snaps {
|
||||
for _, srv := range s.Full.Servers {
|
||||
if srv.LastMsgMs != nil {
|
||||
cur, ok := lastMsg[srv.SID]
|
||||
if !ok || *srv.LastMsgMs < cur {
|
||||
lastMsg[srv.SID] = *srv.LastMsgMs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for sid, ms := range lastMsg {
|
||||
r.metrics.memberLastMsgMs.WithLabelValues(db, cid, sid).Set(float64(ms))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *KubeOVNPlunger) deleteAllFor(db, cid string) {
|
||||
// Cluster-level vecs (db,cid)
|
||||
r.metrics.clusterQuorum.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.allAgree.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.membersExpected.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.membersObserved.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.ipsExpected.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.ipsObserved.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.excessMembers.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.missingMembers.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.unexpectedIPsCount.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.missingExpectedIPsCount.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.ipConflictsCount.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.sidAddrDisagreements.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
|
||||
r.metrics.consensusMajoritySize.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.consensusMinoritySize.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.consensusDiffsTotal.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
|
||||
// Sparse detail vecs (db,cid,*)
|
||||
r.metrics.unexpectedIPGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.missingExpectedIPGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.ipConflictGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.suspectStaleGauge.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
|
||||
// Per-member vecs (db,cid,*)
|
||||
r.metrics.memberConnected.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberLeader.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberLastMsgMs.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberIndex.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberIndexGap.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberReporter.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.memberMissingReporter.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
|
||||
// Ops vecs (db,cid)
|
||||
r.metrics.leaderTransitionsTotal.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.collectErrorsTotal.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.publishEventsTotal.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
r.metrics.snapshotTimestampSec.DeletePartialMatch(prometheus.Labels{"db": db, "cid": cid})
|
||||
}
|
||||
31
internal/controller/kubeovnplunger/util.go
Normal file
31
internal/controller/kubeovnplunger/util.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package kubeovnplunger
|
||||
|
||||
import "github.com/cozystack/cozystack/pkg/ovnstatus"
|
||||
|
||||
func b2f(b bool) float64 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Pull a cluster UUID (cid) from any snapshots’ Local.CID (falls back to "")
|
||||
func cidFromSnaps(snaps []ovnstatus.HealthSnapshot) string {
|
||||
for _, s := range snaps {
|
||||
if s.Local.CID != "" {
|
||||
return s.Local.CID
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Map SID -> last local index to compute gaps (optional)
|
||||
func leaderIndex(snaps []ovnstatus.HealthSnapshot, leaderSID string) (idx *int64) {
|
||||
for _, s := range snaps {
|
||||
if s.Local.SID == leaderSID && s.Local.Index > 0 {
|
||||
v := s.Local.Index
|
||||
return &v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
367
internal/controller/lineagelabeler/lineage_labeler_controller.go
Normal file
367
internal/controller/lineagelabeler/lineage_labeler_controller.go
Normal file
@@ -0,0 +1,367 @@
|
||||
package lineagelabeler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
|
||||
"github.com/cozystack/cozystack/internal/shared/crdmem"
|
||||
"github.com/cozystack/cozystack/pkg/lineage"
|
||||
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/discovery/cached/memory"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/restmapper"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
var ErrNoAncestors = errors.New("no ancestors")
|
||||
|
||||
type LineageLabelerReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
WatchResourceCSV string
|
||||
|
||||
dynClient dynamic.Interface
|
||||
mapper meta.RESTMapper
|
||||
|
||||
appMap atomic.Value
|
||||
once sync.Once
|
||||
|
||||
mem *crdmem.Memory
|
||||
}
|
||||
|
||||
type chartRef struct{ repo, chart string }
|
||||
type appRef struct{ groupVersion, kind, prefix string }
|
||||
|
||||
func (r *LineageLabelerReconciler) initMapping() {
|
||||
r.once.Do(func() {
|
||||
r.appMap.Store(make(map[chartRef]appRef))
|
||||
})
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) currentMap() map[chartRef]appRef {
|
||||
val := r.appMap.Load()
|
||||
if val == nil {
|
||||
return map[chartRef]appRef{}
|
||||
}
|
||||
return val.(map[chartRef]appRef)
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) Map(hr *helmv2.HelmRelease) (string, string, string, error) {
|
||||
cfg := r.currentMap()
|
||||
s := hr.Spec.Chart.Spec
|
||||
key := chartRef{s.SourceRef.Name, s.Chart}
|
||||
if v, ok := cfg[key]; ok {
|
||||
return v.groupVersion, v.kind, v.prefix, nil
|
||||
}
|
||||
return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name)
|
||||
}
|
||||
|
||||
func parseGVKList(csv string) ([]schema.GroupVersionKind, error) {
|
||||
csv = strings.TrimSpace(csv)
|
||||
if csv == "" {
|
||||
return nil, fmt.Errorf("watch resource list is empty")
|
||||
}
|
||||
parts := strings.Split(csv, ",")
|
||||
out := make([]schema.GroupVersionKind, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
p = strings.TrimSpace(p)
|
||||
s := strings.Split(p, "/")
|
||||
if len(s) == 2 {
|
||||
out = append(out, schema.GroupVersionKind{Group: "", Version: s[0], Kind: s[1]})
|
||||
continue
|
||||
}
|
||||
if len(s) == 3 {
|
||||
out = append(out, schema.GroupVersionKind{Group: s[0], Version: s[1], Kind: s[2]})
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("invalid resource token %q, expected 'group/version/Kind' or 'v1/Kind'", p)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
r.initMapping()
|
||||
|
||||
cfg := rest.CopyConfig(mgr.GetConfig())
|
||||
dc, err := dynamic.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
disco, err := discovery.NewDiscoveryClientForConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cached := memory.NewMemCacheClient(disco)
|
||||
r.dynClient = dc
|
||||
r.mapper = restmapper.NewDeferredDiscoveryRESTMapper(cached)
|
||||
|
||||
if r.mem == nil {
|
||||
r.mem = crdmem.Global()
|
||||
}
|
||||
if err := r.mem.EnsurePrimingWithManager(mgr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gvks, err := parseGVKList(r.WatchResourceCSV)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(gvks) == 0 {
|
||||
return fmt.Errorf("no resources to watch")
|
||||
}
|
||||
|
||||
b := ctrl.NewControllerManagedBy(mgr).Named("lineage-labeler")
|
||||
|
||||
nsPred := predicate.NewPredicateFuncs(func(obj client.Object) bool {
|
||||
ns := obj.GetNamespace()
|
||||
return ns != "" && strings.HasPrefix(ns, "tenant-")
|
||||
})
|
||||
|
||||
primary := gvks[0]
|
||||
primaryObj := &unstructured.Unstructured{}
|
||||
primaryObj.SetGroupVersionKind(primary)
|
||||
b = b.For(primaryObj,
|
||||
builder.WithPredicates(
|
||||
predicate.And(
|
||||
nsPred,
|
||||
predicate.Or(
|
||||
predicate.GenerationChangedPredicate{},
|
||||
predicate.ResourceVersionChangedPredicate{},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
for _, gvk := range gvks[1:] {
|
||||
u := &unstructured.Unstructured{}
|
||||
u.SetGroupVersionKind(gvk)
|
||||
b = b.Watches(u,
|
||||
&handler.EnqueueRequestForObject{},
|
||||
builder.WithPredicates(
|
||||
predicate.And(
|
||||
nsPred,
|
||||
predicate.Or(
|
||||
predicate.GenerationChangedPredicate{},
|
||||
predicate.ResourceVersionChangedPredicate{},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
b = b.Watches(
|
||||
&cozyv1alpha1.CozystackResourceDefinition{},
|
||||
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
|
||||
_ = r.refreshAppMap(ctx)
|
||||
return nil
|
||||
}),
|
||||
)
|
||||
|
||||
_ = r.refreshAppMap(context.Background())
|
||||
|
||||
return b.Complete(r)
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) refreshAppMap(ctx context.Context) error {
|
||||
var items []cozyv1alpha1.CozystackResourceDefinition
|
||||
var err error
|
||||
if r.mem != nil {
|
||||
items, err = r.mem.ListFromCacheOrAPI(ctx, r.Client)
|
||||
} else {
|
||||
var list cozyv1alpha1.CozystackResourceDefinitionList
|
||||
err = r.Client.List(ctx, &list)
|
||||
items = list.Items
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newMap := make(map[chartRef]appRef, len(items))
|
||||
for _, crd := range items {
|
||||
k := chartRef{
|
||||
repo: crd.Spec.Release.Chart.SourceRef.Name,
|
||||
chart: crd.Spec.Release.Chart.Name,
|
||||
}
|
||||
v := appRef{
|
||||
groupVersion: "apps.cozystack.io/v1alpha1",
|
||||
kind: crd.Spec.Application.Kind,
|
||||
prefix: crd.Spec.Release.Prefix,
|
||||
}
|
||||
if _, exists := newMap[k]; exists {
|
||||
continue
|
||||
}
|
||||
newMap[k] = v
|
||||
}
|
||||
r.appMap.Store(newMap)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
if req.Namespace == "" || !strings.HasPrefix(req.Namespace, "tenant-") {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
if len(r.currentMap()) == 0 {
|
||||
_ = r.refreshAppMap(ctx)
|
||||
if len(r.currentMap()) == 0 {
|
||||
return ctrl.Result{RequeueAfter: 2 * time.Second}, nil
|
||||
}
|
||||
}
|
||||
|
||||
gvks, err := parseGVKList(r.WatchResourceCSV)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
var obj *unstructured.Unstructured
|
||||
found := false
|
||||
|
||||
for _, gvk := range gvks {
|
||||
mapping, mErr := r.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
||||
if mErr != nil {
|
||||
continue
|
||||
}
|
||||
ns := req.Namespace
|
||||
if mapping.Scope.Name() != meta.RESTScopeNameNamespace {
|
||||
ns = ""
|
||||
}
|
||||
res, gErr := r.dynClient.Resource(mapping.Resource).Namespace(ns).Get(ctx, req.Name, metav1.GetOptions{})
|
||||
if gErr != nil {
|
||||
if apierrors.IsNotFound(gErr) {
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
obj = res
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
||||
if !found || obj == nil {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
existing := obj.GetLabels()
|
||||
if existing == nil {
|
||||
existing = map[string]string{}
|
||||
}
|
||||
|
||||
keys := []string{
|
||||
"apps.cozystack.io/application.group",
|
||||
"apps.cozystack.io/application.kind",
|
||||
"apps.cozystack.io/application.name",
|
||||
}
|
||||
allPresent := true
|
||||
for _, k := range keys {
|
||||
if _, ok := existing[k]; !ok {
|
||||
allPresent = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allPresent {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
labels, warn, err := r.computeLabels(ctx, obj)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrNoAncestors) {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
if warn != "" {
|
||||
l.V(1).Info("lineage ambiguous; using first ancestor", "name", req.NamespacedName)
|
||||
}
|
||||
|
||||
for k, v := range labels {
|
||||
existing[k] = v
|
||||
}
|
||||
obj.SetLabels(existing)
|
||||
|
||||
// Server-Side Apply: claim ownership of our label keys
|
||||
gvk := obj.GroupVersionKind()
|
||||
patch := &unstructured.Unstructured{}
|
||||
patch.SetGroupVersionKind(gvk)
|
||||
patch.SetNamespace(obj.GetNamespace())
|
||||
patch.SetName(obj.GetName())
|
||||
patch.SetLabels(map[string]string{
|
||||
"apps.cozystack.io/application.group": existing["apps.cozystack.io/application.group"],
|
||||
"apps.cozystack.io/application.kind": existing["apps.cozystack.io/application.kind"],
|
||||
"apps.cozystack.io/application.name": existing["apps.cozystack.io/application.name"],
|
||||
})
|
||||
|
||||
// Use controller-runtime client with Apply patch type and field owner
|
||||
if err := r.Patch(ctx, patch,
|
||||
client.Apply,
|
||||
client.FieldOwner("cozystack/lineage"),
|
||||
client.ForceOwnership(false),
|
||||
); err != nil {
|
||||
if apierrors.IsConflict(err) {
|
||||
return ctrl.Result{RequeueAfter: 500 * time.Millisecond}, nil
|
||||
}
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *LineageLabelerReconciler) computeLabels(ctx context.Context, o *unstructured.Unstructured) (map[string]string, string, error) {
|
||||
owners := lineage.WalkOwnershipGraph(ctx, r.dynClient, r.mapper, r, o)
|
||||
if len(owners) == 0 {
|
||||
return nil, "", ErrNoAncestors
|
||||
}
|
||||
obj, err := owners[0].GetUnstructured(ctx, r.dynClient, r.mapper)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
gv, err := schema.ParseGroupVersion(obj.GetAPIVersion())
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("invalid APIVersion %s: %w", obj.GetAPIVersion(), err)
|
||||
}
|
||||
var warn string
|
||||
if len(owners) > 1 {
|
||||
warn = "ambiguous"
|
||||
}
|
||||
group := gv.Group
|
||||
if len(group) > 63 {
|
||||
group = trimDNSLabel(group[:63])
|
||||
}
|
||||
return map[string]string{
|
||||
"apps.cozystack.io/application.group": group,
|
||||
"apps.cozystack.io/application.kind": obj.GetKind(),
|
||||
"apps.cozystack.io/application.name": obj.GetName(),
|
||||
}, warn, nil
|
||||
}
|
||||
|
||||
func trimDNSLabel(s string) string {
|
||||
for len(s) > 0 {
|
||||
b := s[len(s)-1]
|
||||
if (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') {
|
||||
return s
|
||||
}
|
||||
s = s[:len(s)-1]
|
||||
}
|
||||
return s
|
||||
}
|
||||
99
internal/shared/crdmem/memory.go
Normal file
99
internal/shared/crdmem/memory.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package crdmem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type Memory struct {
|
||||
mu sync.RWMutex
|
||||
data map[string]cozyv1alpha1.CozystackResourceDefinition
|
||||
primed bool
|
||||
primeOnce sync.Once
|
||||
}
|
||||
|
||||
func New() *Memory {
|
||||
return &Memory{data: make(map[string]cozyv1alpha1.CozystackResourceDefinition)}
|
||||
}
|
||||
|
||||
var (
|
||||
global *Memory
|
||||
globalOnce sync.Once
|
||||
)
|
||||
|
||||
func Global() *Memory {
|
||||
globalOnce.Do(func() { global = New() })
|
||||
return global
|
||||
}
|
||||
|
||||
func (m *Memory) Upsert(obj *cozyv1alpha1.CozystackResourceDefinition) {
|
||||
if obj == nil {
|
||||
return
|
||||
}
|
||||
m.mu.Lock()
|
||||
m.data[obj.Name] = *obj.DeepCopy()
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
func (m *Memory) Delete(name string) {
|
||||
m.mu.Lock()
|
||||
delete(m.data, name)
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
func (m *Memory) Snapshot() []cozyv1alpha1.CozystackResourceDefinition {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
out := make([]cozyv1alpha1.CozystackResourceDefinition, 0, len(m.data))
|
||||
for _, v := range m.data {
|
||||
out = append(out, v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (m *Memory) IsPrimed() bool {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.primed
|
||||
}
|
||||
|
||||
type runnable func(context.Context) error
|
||||
|
||||
func (r runnable) Start(ctx context.Context) error { return r(ctx) }
|
||||
|
||||
func (m *Memory) EnsurePrimingWithManager(mgr ctrl.Manager) error {
|
||||
var errOut error
|
||||
m.primeOnce.Do(func() {
|
||||
errOut = mgr.Add(runnable(func(ctx context.Context) error {
|
||||
if ok := mgr.GetCache().WaitForCacheSync(ctx); !ok {
|
||||
return nil
|
||||
}
|
||||
var list cozyv1alpha1.CozystackResourceDefinitionList
|
||||
if err := mgr.GetClient().List(ctx, &list); err == nil {
|
||||
for i := range list.Items {
|
||||
m.Upsert(&list.Items[i])
|
||||
}
|
||||
m.mu.Lock()
|
||||
m.primed = true
|
||||
m.mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
})
|
||||
return errOut
|
||||
}
|
||||
|
||||
func (m *Memory) ListFromCacheOrAPI(ctx context.Context, c client.Client) ([]cozyv1alpha1.CozystackResourceDefinition, error) {
|
||||
if m.IsPrimed() {
|
||||
return m.Snapshot(), nil
|
||||
}
|
||||
var list cozyv1alpha1.CozystackResourceDefinitionList
|
||||
if err := c.List(ctx, &list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list.Items, nil
|
||||
}
|
||||
293
internal/sse/server.go
Normal file
293
internal/sse/server.go
Normal file
@@ -0,0 +1,293 @@
|
||||
// Package sse provides a tiny Server-Sent Events server with pluggable routes.
|
||||
// No external deps; safe for quick demos and small dashboards.
|
||||
package sse
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Options configures the SSE server.
|
||||
type Options struct {
|
||||
// Addr is the listening address, e.g. ":8080" or "127.0.0.1:0".
|
||||
Addr string
|
||||
|
||||
// IndexPath is the path serving a minimal live HTML page ("" to disable).
|
||||
// e.g. "/" or "/status"
|
||||
IndexPath string
|
||||
|
||||
// StreamPath is the SSE endpoint path, e.g. "/stream".
|
||||
StreamPath string
|
||||
|
||||
// Title for the index page (cosmetic).
|
||||
Title string
|
||||
|
||||
// AllowCORS, if true, sets Access-Control-Allow-Origin: * for /stream.
|
||||
AllowCORS bool
|
||||
|
||||
// ClientBuf is the per-client buffered message queue size.
|
||||
// If 0, defaults to 16. When full, new messages are dropped for that client.
|
||||
ClientBuf int
|
||||
|
||||
// Heartbeat sends a comment line every interval to keep connections alive.
|
||||
// If 0, defaults to 25s.
|
||||
Heartbeat time.Duration
|
||||
|
||||
// Logger (optional). If nil, log.Printf is used.
|
||||
Logger *log.Logger
|
||||
}
|
||||
|
||||
// Server is a simple SSE broadcaster.
|
||||
type Server struct {
|
||||
opts Options
|
||||
mux *http.ServeMux
|
||||
http *http.Server
|
||||
|
||||
clientsMu sync.RWMutex
|
||||
clients map[*client]struct{}
|
||||
|
||||
// latest holds the most recent payload (sent to new clients on connect).
|
||||
latestMu sync.RWMutex
|
||||
latest string
|
||||
}
|
||||
|
||||
type client struct {
|
||||
ch chan string
|
||||
closeCh chan struct{}
|
||||
flusher http.Flusher
|
||||
w http.ResponseWriter
|
||||
req *http.Request
|
||||
logf func(string, ...any)
|
||||
heartbeat time.Duration
|
||||
}
|
||||
|
||||
func New(opts Options) *Server {
|
||||
if opts.ClientBuf <= 0 {
|
||||
opts.ClientBuf = 16
|
||||
}
|
||||
if opts.Heartbeat <= 0 {
|
||||
opts.Heartbeat = 25 * time.Second
|
||||
}
|
||||
if opts.Addr == "" {
|
||||
opts.Addr = ":8080"
|
||||
}
|
||||
if opts.StreamPath == "" {
|
||||
opts.StreamPath = "/stream"
|
||||
}
|
||||
if opts.IndexPath == "" {
|
||||
opts.IndexPath = "/"
|
||||
}
|
||||
s := &Server{
|
||||
opts: opts,
|
||||
mux: http.NewServeMux(),
|
||||
clients: make(map[*client]struct{}),
|
||||
}
|
||||
s.routes()
|
||||
s.http = &http.Server{
|
||||
Addr: opts.Addr,
|
||||
Handler: s.mux,
|
||||
ReadHeaderTimeout: 10 * time.Second,
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Server) routes() {
|
||||
if s.opts.IndexPath != "" {
|
||||
s.mux.HandleFunc(s.opts.IndexPath, s.handleIndex)
|
||||
}
|
||||
s.mux.HandleFunc(s.opts.StreamPath, s.handleStream)
|
||||
}
|
||||
|
||||
func (s *Server) logf(format string, args ...any) {
|
||||
if s.opts.Logger != nil {
|
||||
s.opts.Logger.Printf(format, args...)
|
||||
} else {
|
||||
log.Printf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// ListenAndServe starts the HTTP server (blocking).
|
||||
func (s *Server) ListenAndServe() error {
|
||||
s.logf("sse: listening on http://%s (index=%s, stream=%s)", s.http.Addr, s.opts.IndexPath, s.opts.StreamPath)
|
||||
return s.http.ListenAndServe()
|
||||
}
|
||||
|
||||
// Shutdown gracefully stops the server.
|
||||
func (s *Server) Shutdown(ctx context.Context) error {
|
||||
s.clientsMu.Lock()
|
||||
for c := range s.clients {
|
||||
close(c.closeCh)
|
||||
}
|
||||
s.clientsMu.Unlock()
|
||||
return s.http.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// Publish broadcasts a new payload to all clients and stores it as latest.
|
||||
func (s *Server) Publish(payload string) {
|
||||
// Store latest
|
||||
s.latestMu.Lock()
|
||||
s.latest = payload
|
||||
s.latestMu.Unlock()
|
||||
|
||||
// Broadcast
|
||||
s.clientsMu.RLock()
|
||||
defer s.clientsMu.RUnlock()
|
||||
for c := range s.clients {
|
||||
select {
|
||||
case c.ch <- payload:
|
||||
default:
|
||||
// Drop if client is slow (buffer full)
|
||||
if s.opts.Logger != nil {
|
||||
s.opts.Logger.Printf("sse: dropping message to slow client %p", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
page := indexTemplate(s.opts.Title, s.opts.StreamPath)
|
||||
_, _ = w.Write([]byte(page))
|
||||
}
|
||||
|
||||
func (s *Server) handleStream(w http.ResponseWriter, r *http.Request) {
|
||||
// Required SSE headers
|
||||
if s.opts.AllowCORS {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
|
||||
flusher, ok := w.(http.Flusher)
|
||||
if !ok {
|
||||
http.Error(w, "streaming unsupported", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
c := &client{
|
||||
ch: make(chan string, s.opts.ClientBuf),
|
||||
closeCh: make(chan struct{}),
|
||||
flusher: flusher,
|
||||
w: w,
|
||||
req: r,
|
||||
logf: s.logf,
|
||||
heartbeat: s.opts.Heartbeat,
|
||||
}
|
||||
|
||||
// Register client
|
||||
s.clientsMu.Lock()
|
||||
s.clients[c] = struct{}{}
|
||||
s.clientsMu.Unlock()
|
||||
|
||||
// Initial comment to open the stream for some proxies
|
||||
fmt.Fprintf(w, ": connected %s\n\n", time.Now().Format(time.RFC3339))
|
||||
flusher.Flush()
|
||||
|
||||
// Send latest if any
|
||||
s.latestMu.RLock()
|
||||
latest := s.latest
|
||||
s.latestMu.RUnlock()
|
||||
if latest != "" {
|
||||
writeSSE(w, latest)
|
||||
flusher.Flush()
|
||||
}
|
||||
|
||||
// Start pump
|
||||
go c.pump()
|
||||
|
||||
// Block until client disconnects
|
||||
<-r.Context().Done()
|
||||
|
||||
// Unregister client
|
||||
close(c.closeCh)
|
||||
s.clientsMu.Lock()
|
||||
delete(s.clients, c)
|
||||
s.clientsMu.Unlock()
|
||||
}
|
||||
|
||||
func (c *client) pump() {
|
||||
t := time.NewTicker(c.heartbeat)
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-c.closeCh:
|
||||
return
|
||||
case msg := <-c.ch:
|
||||
writeSSE(c.w, msg)
|
||||
c.flusher.Flush()
|
||||
case <-t.C:
|
||||
// heartbeat comment (keeps connections alive through proxies)
|
||||
fmt.Fprint(c.w, ": hb\n\n")
|
||||
c.flusher.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeSSE(w http.ResponseWriter, msg string) {
|
||||
// Split on lines; each needs its own "data:" field per the SSE spec
|
||||
lines := strings.Split(strings.TrimRight(msg, "\n"), "\n")
|
||||
for _, ln := range lines {
|
||||
fmt.Fprintf(w, "data: %s\n", ln)
|
||||
}
|
||||
fmt.Fprint(w, "\n")
|
||||
}
|
||||
|
||||
// Minimal index page with live updates
|
||||
func indexTemplate(title, streamPath string) string {
|
||||
if title == "" {
|
||||
title = "SSE Stream"
|
||||
}
|
||||
if streamPath == "" {
|
||||
streamPath = "/stream"
|
||||
}
|
||||
const tpl = `<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>{{.Title}}</title>
|
||||
<style>
|
||||
body { font-family: system-ui, sans-serif; margin: 2rem; }
|
||||
pre { background:#111; color:#eee; padding:1rem; border-radius:12px; white-space:pre-wrap;}
|
||||
.status { margin-bottom: 1rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{.Title}}</h1>
|
||||
<div class="status">Connecting…</div>
|
||||
<pre id="out"></pre>
|
||||
<script>
|
||||
const statusEl = document.querySelector('.status');
|
||||
const out = document.getElementById('out');
|
||||
const es = new EventSource('{{.Stream}}');
|
||||
es.onmessage = (e) => {
|
||||
// Replace content with the latest full snapshot
|
||||
if (e.data === "") return;
|
||||
// We accumulate until a blank 'data:' terminator; simpler approach: reset on first line.
|
||||
// For this demo, server always sends full content in one event, so just overwrite.
|
||||
out.textContent = (out._acc ?? "") + e.data + "\n";
|
||||
};
|
||||
es.addEventListener('open', () => { statusEl.textContent = "Connected"; out._acc = ""; });
|
||||
es.addEventListener('error', () => { statusEl.textContent = "Disconnected (browser will retry)…"; out._acc = ""; });
|
||||
// Optional: keep the latest only per message
|
||||
es.onmessage = (e) => {
|
||||
out.textContent = e.data + "\n";
|
||||
statusEl.textContent = "Connected";
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>`
|
||||
page, _ := template.New("idx").Parse(tpl)
|
||||
var b strings.Builder
|
||||
_ = page.Execute(&b, map[string]any{
|
||||
"Title": title,
|
||||
"Stream": streamPath,
|
||||
})
|
||||
return b.String()
|
||||
}
|
||||
@@ -8,16 +8,11 @@ generate:
|
||||
|
||||
image:
|
||||
docker buildx build images/clickhouse-backup \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/clickhouse-backup:$(call settag,$(CLICKHOUSE_BACKUP_TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/clickhouse-backup:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/clickhouse-backup.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/clickhouse-backup:$(call settag,$(CLICKHOUSE_BACKUP_TAG))@$$(yq e '."containerimage.digest"' images/clickhouse-backup.json -o json -r)" \
|
||||
> images/clickhouse-backup.tag
|
||||
rm -f images/clickhouse-backup.json
|
||||
|
||||
@@ -7,16 +7,11 @@ image: image-nginx
|
||||
|
||||
image-nginx:
|
||||
docker buildx build images/nginx-cache \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/nginx-cache:$(call settag,$(NGINX_CACHE_TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/nginx-cache:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/nginx-cache.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/nginx-cache:$(call settag,$(NGINX_CACHE_TAG))@$$(yq e '."containerimage.digest"' images/nginx-cache.json -o json -r)" \
|
||||
> images/nginx-cache.tag
|
||||
rm -f images/nginx-cache.json
|
||||
|
||||
@@ -16,7 +16,7 @@ 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: 0.27.0
|
||||
version: 0.29.0
|
||||
|
||||
# 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
|
||||
|
||||
@@ -12,52 +12,37 @@ image: image-ubuntu-container-disk image-kubevirt-cloud-provider image-kubevirt-
|
||||
|
||||
image-ubuntu-container-disk:
|
||||
docker buildx build images/ubuntu-container-disk \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--build-arg KUBERNETES_VERSION=${KUBERNETES_VERSION} \
|
||||
--tag $(REGISTRY)/ubuntu-container-disk:$(call settag,$(KUBERNETES_VERSION)) \
|
||||
--tag $(REGISTRY)/ubuntu-container-disk:$(call settag,$(KUBERNETES_VERSION)-$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/ubuntu-container-disk:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/ubuntu-container-disk.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/ubuntu-container-disk:$(call settag,$(KUBERNETES_VERSION))@$$(yq e '."containerimage.digest"' images/ubuntu-container-disk.json -o json -r)" \
|
||||
> images/ubuntu-container-disk.tag
|
||||
rm -f images/ubuntu-container-disk.json
|
||||
|
||||
image-kubevirt-cloud-provider:
|
||||
docker buildx build images/kubevirt-cloud-provider \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/kubevirt-cloud-provider:$(call settag,$(KUBERNETES_PKG_TAG)) \
|
||||
--tag $(REGISTRY)/kubevirt-cloud-provider:$(call settag,$(KUBERNETES_PKG_TAG)-$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/kubevirt-cloud-provider:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/kubevirt-cloud-provider.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/kubevirt-cloud-provider:$(call settag,$(KUBERNETES_PKG_TAG))@$$(yq e '."containerimage.digest"' images/kubevirt-cloud-provider.json -o json -r)" \
|
||||
> images/kubevirt-cloud-provider.tag
|
||||
rm -f images/kubevirt-cloud-provider.json
|
||||
|
||||
image-kubevirt-csi-driver:
|
||||
docker buildx build images/kubevirt-csi-driver \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/kubevirt-csi-driver:$(call settag,$(KUBERNETES_PKG_TAG)) \
|
||||
--tag $(REGISTRY)/kubevirt-csi-driver:$(call settag,$(KUBERNETES_PKG_TAG)-$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/kubevirt-csi-driver:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/kubevirt-csi-driver.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/kubevirt-csi-driver:$(call settag,$(KUBERNETES_PKG_TAG))@$$(yq e '."containerimage.digest"' images/kubevirt-csi-driver.json -o json -r)" \
|
||||
> images/kubevirt-csi-driver.tag
|
||||
IMAGE=$$(cat images/kubevirt-csi-driver.tag) \
|
||||
@@ -67,17 +52,12 @@ image-kubevirt-csi-driver:
|
||||
|
||||
image-cluster-autoscaler:
|
||||
docker buildx build images/cluster-autoscaler \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/cluster-autoscaler:$(call settag,$(KUBERNETES_PKG_TAG)) \
|
||||
--tag $(REGISTRY)/cluster-autoscaler:$(call settag,$(KUBERNETES_PKG_TAG)-$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/cluster-autoscaler:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/cluster-autoscaler.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/cluster-autoscaler:$(call settag,$(KUBERNETES_PKG_TAG))@$$(yq e '."containerimage.digest"' images/cluster-autoscaler.json -o json -r)" \
|
||||
> images/cluster-autoscaler.tag
|
||||
rm -f images/cluster-autoscaler.json
|
||||
|
||||
@@ -93,7 +93,7 @@ See the reference for components utilized in this service:
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------- | ------- |
|
||||
| `version` | Kubernetes version given as vMAJOR.MINOR. Available are versions from 1.28 to 1.33. | `string` | `v1.32` |
|
||||
| `version` | Kubernetes version given as vMAJOR.MINOR. Available are versions from 1.28 to 1.33. | `string` | `v1.33` |
|
||||
| `host` | Hostname used to access the Kubernetes cluster externally. Defaults to `<cluster-name>.<tenant-host>` when empty. | `string` | `""` |
|
||||
| `nodeGroups` | Worker nodes configuration | `map[string]object` | `{...}` |
|
||||
| `nodeGroups[name].minReplicas` | Minimum amount of replicas | `int` | `0` |
|
||||
@@ -139,6 +139,8 @@ See the reference for components utilized in this service:
|
||||
| `addons.velero` | Velero | `object` | `{}` |
|
||||
| `addons.velero.enabled` | Enable Velero for backup and recovery of a tenant Kubernetes cluster. | `bool` | `false` |
|
||||
| `addons.velero.valuesOverride` | Custom values to override | `object` | `{}` |
|
||||
| `addons.coredns` | Coredns | `object` | `{}` |
|
||||
| `addons.coredns.valuesOverride` | Custom values to override | `object` | `{}` |
|
||||
|
||||
|
||||
### Kubernetes Control Plane Configuration
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.27.0@sha256:e4fbb7d2043f25b90cc8840468d0880e9d3d72ae8b1c8801bf8c35f944cc485d
|
||||
ghcr.io/cozystack/cozystack/cluster-autoscaler:0.29.0@sha256:2d39989846c3579dd020b9f6c77e6e314cc81aa344eaac0f6d633e723c17196d
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.27.0@sha256:5335c044313b69ee13b30ca4941687e509005e55f4ae25723861edbf2fbd6dd2
|
||||
ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.29.0@sha256:5335c044313b69ee13b30ca4941687e509005e55f4ae25723861edbf2fbd6dd2
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.27.0@sha256:c35987e8b37ad3b34a9a32fe6e80eee77b4c57b99090ca5cdbc3d16c25edb3b9
|
||||
ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.29.0@sha256:3a3bc912f70ccba1e9f92a0754179dbdc4c01f24073467b6d1406c77da794863
|
||||
|
||||
@@ -127,9 +127,6 @@ spec:
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.controlPlane.scheduler.resourcesPreset .Values.controlPlane.scheduler.resources $) | nindent 6 }}
|
||||
dataStoreName: "{{ $etcd }}"
|
||||
addons:
|
||||
coreDNS:
|
||||
dnsServiceIPs:
|
||||
- 10.95.0.10
|
||||
konnectivity:
|
||||
server:
|
||||
port: 8132
|
||||
|
||||
47
packages/apps/kubernetes/templates/helmreleases/coredns.yaml
Normal file
47
packages/apps/kubernetes/templates/helmreleases/coredns.yaml
Normal file
@@ -0,0 +1,47 @@
|
||||
{{- define "cozystack.defaultCoreDNSValues" -}}
|
||||
coredns:
|
||||
service:
|
||||
clusterIP: "10.95.0.10"
|
||||
{{- end }}
|
||||
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-coredns
|
||||
labels:
|
||||
cozystack.io/repository: system
|
||||
cozystack.io/target-cluster-name: {{ .Release.Name }}
|
||||
spec:
|
||||
interval: 5m
|
||||
releaseName: coredns
|
||||
chart:
|
||||
spec:
|
||||
chart: cozy-coredns
|
||||
reconcileStrategy: Revision
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: cozystack-system
|
||||
namespace: cozy-system
|
||||
version: '>= 0.0.0-0'
|
||||
kubeConfig:
|
||||
secretRef:
|
||||
name: {{ .Release.Name }}-admin-kubeconfig
|
||||
key: super-admin.svc
|
||||
targetNamespace: kube-system
|
||||
storageNamespace: kube-system
|
||||
install:
|
||||
createNamespace: true
|
||||
remediation:
|
||||
retries: -1
|
||||
upgrade:
|
||||
remediation:
|
||||
retries: -1
|
||||
values:
|
||||
{{- toYaml (deepCopy .Values.addons.coredns.valuesOverride | mergeOverwrite (fromYaml (include "cozystack.defaultCoreDNSValues" .))) | nindent 4 }}
|
||||
dependsOn:
|
||||
{{- if lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" .Release.Namespace .Release.Name }}
|
||||
- name: {{ .Release.Name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
- name: {{ .Release.Name }}-cilium
|
||||
namespace: {{ .Release.Namespace }}
|
||||
@@ -41,6 +41,7 @@ spec:
|
||||
{{ .Release.Name }}-fluxcd
|
||||
{{ .Release.Name }}-gpu-operator
|
||||
{{ .Release.Name }}-velero
|
||||
{{ .Release.Name }}-coredns
|
||||
-p '{"spec": {"suspend": true}}'
|
||||
--type=merge --field-manager=flux-client-side-apply || true
|
||||
---
|
||||
@@ -81,6 +82,7 @@ rules:
|
||||
- {{ .Release.Name }}-fluxcd
|
||||
- {{ .Release.Name }}-gpu-operator
|
||||
- {{ .Release.Name }}-velero
|
||||
- {{ .Release.Name }}-coredns
|
||||
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
"cilium": {
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"coredns": {
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"fluxcd": {
|
||||
"enabled": false,
|
||||
"valuesOverride": {}
|
||||
@@ -45,6 +48,7 @@
|
||||
"required": [
|
||||
"certManager",
|
||||
"cilium",
|
||||
"coredns",
|
||||
"fluxcd",
|
||||
"gatewayAPI",
|
||||
"gpuOperator",
|
||||
@@ -97,6 +101,24 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"coredns": {
|
||||
"description": "Coredns",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"valuesOverride": {}
|
||||
},
|
||||
"required": [
|
||||
"valuesOverride"
|
||||
],
|
||||
"properties": {
|
||||
"valuesOverride": {
|
||||
"description": "Custom values to override",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"x-kubernetes-preserve-unknown-fields": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"fluxcd": {
|
||||
"description": "Flux CD",
|
||||
"type": "object",
|
||||
@@ -698,7 +720,7 @@
|
||||
"version": {
|
||||
"description": "Kubernetes version given as vMAJOR.MINOR. Available are versions from 1.28 to 1.33.",
|
||||
"type": "string",
|
||||
"default": "v1.32",
|
||||
"default": "v1.33",
|
||||
"enum": [
|
||||
"v1.28",
|
||||
"v1.29",
|
||||
|
||||
@@ -5,7 +5,7 @@ storageClass: replicated
|
||||
|
||||
## @section Application-specific parameters
|
||||
## @param version {string} Kubernetes version given as vMAJOR.MINOR. Available are versions from 1.28 to 1.33.
|
||||
version: "v1.32"
|
||||
version: "v1.33"
|
||||
## @param host {string} Hostname used to access the Kubernetes cluster externally. Defaults to `<cluster-name>.<tenant-host>` when empty.
|
||||
host: ""
|
||||
|
||||
@@ -122,6 +122,13 @@ addons:
|
||||
enabled: false
|
||||
valuesOverride: {}
|
||||
|
||||
## @field addons.coredns {coredns} Coredns
|
||||
##
|
||||
coredns:
|
||||
## @field coredns.valuesOverride {object} Custom values to override
|
||||
##
|
||||
valuesOverride: {}
|
||||
|
||||
## @section Kubernetes Control Plane Configuration
|
||||
##
|
||||
## @param controlPlane {controlPlane} Control Plane Configuration
|
||||
|
||||
@@ -8,16 +8,11 @@ generate:
|
||||
|
||||
image:
|
||||
docker buildx build images/mariadb-backup \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/mariadb-backup:$(call settag,$(MARIADB_BACKUP_TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/mariadb-backup:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/mariadb-backup.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/mariadb-backup:$(call settag,$(MARIADB_BACKUP_TAG))@$$(yq e '."containerimage.digest"' images/mariadb-backup.json -o json -r)" \
|
||||
> images/mariadb-backup.tag
|
||||
rm -f images/mariadb-backup.json
|
||||
|
||||
@@ -4,4 +4,4 @@ description: Separated tenant namespace
|
||||
icon: /logos/tenant.svg
|
||||
|
||||
type: application
|
||||
version: 1.12.0
|
||||
version: 1.14.0
|
||||
|
||||
@@ -20,5 +20,7 @@ spec:
|
||||
version: "*"
|
||||
interval: 1m0s
|
||||
timeout: 5m0s
|
||||
upgrade:
|
||||
force: true
|
||||
values: {}
|
||||
{{- end }}
|
||||
|
||||
@@ -19,10 +19,10 @@ spec:
|
||||
namespace: cozy-public
|
||||
install:
|
||||
remediation:
|
||||
retries: 10
|
||||
retries: -1
|
||||
upgrade:
|
||||
remediation:
|
||||
retries: 10
|
||||
retries: -1
|
||||
interval: 1m0s
|
||||
timeout: 10m0s
|
||||
{{- end }}
|
||||
|
||||
@@ -20,7 +20,11 @@ metadata:
|
||||
name: allow-external-communication
|
||||
namespace: {{ include "tenant.name" . }}
|
||||
spec:
|
||||
endpointSelector: {}
|
||||
endpointSelector:
|
||||
matchExpressions:
|
||||
- key: policy.cozystack.io/allow-external-communication
|
||||
operator: NotIn
|
||||
values: ["false"]
|
||||
ingress:
|
||||
- fromEntities:
|
||||
- world
|
||||
|
||||
@@ -7,4 +7,21 @@ metadata:
|
||||
spec:
|
||||
hard:
|
||||
{{- include "cozy-lib.resources.flatten" (list .Values.resourceQuotas $) | nindent 6 }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: LimitRange
|
||||
metadata:
|
||||
name: tenant-range-limits
|
||||
namespace: {{ include "tenant.name" . }}
|
||||
spec:
|
||||
limits:
|
||||
- default:
|
||||
cpu: "250m"
|
||||
memory: "128Mi"
|
||||
ephemeral-storage: "2Gi"
|
||||
defaultRequest:
|
||||
cpu: "25m"
|
||||
memory: "128Mi"
|
||||
ephemeral-storage: "50Mi"
|
||||
type: Container
|
||||
{{- end }}
|
||||
|
||||
@@ -17,6 +17,12 @@ spec:
|
||||
kind: HelmRepository
|
||||
name: cozystack-extra
|
||||
namespace: cozy-public
|
||||
install:
|
||||
remediation:
|
||||
retries: -1
|
||||
upgrade:
|
||||
remediation:
|
||||
retries: -1
|
||||
interval: 1m0s
|
||||
timeout: 5m0s
|
||||
timeout: 10m0s
|
||||
{{- end }}
|
||||
|
||||
@@ -69,7 +69,9 @@ kubernetes 0.26.0 9584e5f5
|
||||
kubernetes 0.26.1 0e47e1e8
|
||||
kubernetes 0.26.2 8ddbe32e
|
||||
kubernetes 0.26.3 c02a3818
|
||||
kubernetes 0.27.0 HEAD
|
||||
kubernetes 0.27.0 6cd5e746
|
||||
kubernetes 0.28.0 7f477eec
|
||||
kubernetes 0.29.0 HEAD
|
||||
mysql 0.1.0 263e47be
|
||||
mysql 0.2.0 c24a103f
|
||||
mysql 0.3.0 53f2365e
|
||||
@@ -163,55 +165,16 @@ tcp-balancer 0.4.2 4369b031
|
||||
tcp-balancer 0.5.0 08cb7c0f
|
||||
tcp-balancer 0.5.1 c02a3818
|
||||
tcp-balancer 0.6.0 HEAD
|
||||
tenant 1.10.0 4369b031
|
||||
tenant 1.11.0 08cb7c0f
|
||||
tenant 1.11.1 28c9fcd6
|
||||
tenant 1.11.2 c02a3818
|
||||
tenant 1.12.0 HEAD
|
||||
virtual-machine 0.1.4 f2015d65
|
||||
virtual-machine 0.1.5 263e47be
|
||||
virtual-machine 0.2.0 c0685f43
|
||||
virtual-machine 0.3.0 6c5cf5bf
|
||||
virtual-machine 0.4.0 b8e33d19
|
||||
virtual-machine 0.5.0 1ec10165
|
||||
virtual-machine 0.6.0 4e68e65c
|
||||
virtual-machine 0.7.0 e23286a3
|
||||
virtual-machine 0.7.1 0ab39f20
|
||||
virtual-machine 0.8.0 3fa4dd3a
|
||||
virtual-machine 0.8.1 93c46161
|
||||
virtual-machine 0.8.2 de19450f
|
||||
virtual-machine 0.9.0 721c12a7
|
||||
virtual-machine 0.9.1 93bdf411
|
||||
virtual-machine 0.10.0 6130f43d
|
||||
virtual-machine 0.10.2 632224a3
|
||||
virtual-machine 0.11.0 4369b031
|
||||
virtual-machine 0.12.0 acd4663a
|
||||
virtual-machine 0.12.1 909208ba
|
||||
virtual-machine 0.12.2 8ddbe32e
|
||||
virtual-machine 0.12.3 c02a3818
|
||||
virtual-machine 0.13.0 HEAD
|
||||
tenant 1.13.0 8f1975d1
|
||||
tenant 1.14.0 HEAD
|
||||
virtual-machine 0.14.0 HEAD
|
||||
vm-disk 0.1.0 d971f2ff
|
||||
vm-disk 0.1.1 6130f43d
|
||||
vm-disk 0.1.2 632224a3
|
||||
vm-disk 0.2.0 4369b031
|
||||
vm-disk 0.3.0 c02a3818
|
||||
vm-disk 0.4.0 HEAD
|
||||
vm-instance 0.1.0 1ec10165
|
||||
vm-instance 0.2.0 84f3ccc0
|
||||
vm-instance 0.3.0 4e68e65c
|
||||
vm-instance 0.4.0 e23286a3
|
||||
vm-instance 0.4.1 0ab39f20
|
||||
vm-instance 0.5.0 3fa4dd3a
|
||||
vm-instance 0.5.1 de19450f
|
||||
vm-instance 0.6.0 721c12a7
|
||||
vm-instance 0.7.0 6130f43d
|
||||
vm-instance 0.7.2 632224a3
|
||||
vm-instance 0.8.0 4369b031
|
||||
vm-instance 0.9.0 acd4663a
|
||||
vm-instance 0.10.0 909208ba
|
||||
vm-instance 0.10.1 8ddbe32e
|
||||
vm-instance 0.10.2 c02a3818
|
||||
vm-instance 0.11.0 HEAD
|
||||
vm-instance 0.12.0 HEAD
|
||||
vpn 0.1.0 263e47be
|
||||
vpn 0.2.0 53f2365e
|
||||
vpn 0.3.0 6c5cf5bf
|
||||
|
||||
@@ -17,10 +17,10 @@ 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: 0.13.0
|
||||
version: 0.14.0
|
||||
|
||||
# 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: 0.13.0
|
||||
appVersion: 0.14.0
|
||||
|
||||
@@ -6,14 +6,12 @@ metadata:
|
||||
name: {{ include "virtual-machine.fullname" . }}
|
||||
labels:
|
||||
{{- include "virtual-machine.labels" . | nindent 4 }}
|
||||
{{- if eq .Values.externalMethod "WholeIP" }}
|
||||
annotations:
|
||||
networking.cozystack.io/wholeIP: "true"
|
||||
{{- end }}
|
||||
spec:
|
||||
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
|
||||
externalTrafficPolicy: Local
|
||||
{{- if (include "cozy-lib.network.disableLoadBalancerNodePorts" $ | fromYaml) }}
|
||||
{{- if ((include "cozy-lib.network.disableLoadBalancerNodePorts" $) | fromYaml) }}
|
||||
allocateLoadBalancerNodePorts: false
|
||||
{{- end }}
|
||||
selector:
|
||||
@@ -29,3 +27,27 @@ spec:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: {{ include "virtual-machine.fullname" . }}
|
||||
spec:
|
||||
endpointSelector:
|
||||
matchLabels:
|
||||
{{- include "virtual-machine.selectorLabels" . | nindent 6 }}
|
||||
ingress:
|
||||
- fromEntities:
|
||||
- cluster
|
||||
- fromEntities:
|
||||
- world
|
||||
{{- if eq .Values.externalMethod "PortList" }}
|
||||
toPorts:
|
||||
- ports:
|
||||
{{- range .Values.externalPorts }}
|
||||
- port: {{ quote . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
egress:
|
||||
- toEntities:
|
||||
- world
|
||||
|
||||
@@ -51,7 +51,7 @@ spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: update-resources
|
||||
image: bitnami/kubectl:latest
|
||||
image: docker.io/alpine/k8s:1.33.4
|
||||
resources:
|
||||
requests:
|
||||
memory: "16Mi"
|
||||
@@ -64,14 +64,14 @@ spec:
|
||||
- |
|
||||
{{- if $needUpdateType }}
|
||||
echo "Patching VirtualMachine for instancetype update..."
|
||||
kubectl patch virtualmachine {{ $vmName }} -n {{ $namespace }} \
|
||||
kubectl patch virtualmachines.kubevirt.io {{ $vmName }} -n {{ $namespace }} \
|
||||
--type merge \
|
||||
-p '{"spec":{"instancetype":{"name": "{{ $instanceType }}", "revisionName": null}}}'
|
||||
{{- end }}
|
||||
|
||||
{{- if $needUpdateProfile }}
|
||||
echo "Patching VirtualMachine for preference update..."
|
||||
kubectl patch virtualmachine {{ $vmName }} -n {{ $namespace }} \
|
||||
kubectl patch virtualmachines.kubevirt.io {{ $vmName }} -n {{ $namespace }} \
|
||||
--type merge \
|
||||
-p '{"spec":{"preference":{"name": "{{ $instanceProfile }}", "revisionName": null}}}'
|
||||
{{- end }}
|
||||
|
||||
@@ -62,6 +62,7 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
policy.cozystack.io/allow-external-communication: "false"
|
||||
kubevirt.io/allow-pod-bridge-network-live-migration: "true"
|
||||
labels:
|
||||
{{- include "virtual-machine.labels" . | nindent 8 }}
|
||||
|
||||
@@ -19,7 +19,7 @@ spec:
|
||||
backoffLimit: 1
|
||||
containers:
|
||||
- name: resize
|
||||
image: bitnami/kubectl
|
||||
image: docker.io/alpine/k8s:1.33.4
|
||||
command: ["sh", "-xec"]
|
||||
args:
|
||||
- |
|
||||
|
||||
@@ -17,10 +17,10 @@ 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: 0.11.0
|
||||
version: 0.12.0
|
||||
|
||||
# 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: 0.11.0
|
||||
appVersion: 0.12.0
|
||||
|
||||
@@ -6,14 +6,12 @@ metadata:
|
||||
name: {{ include "virtual-machine.fullname" . }}
|
||||
labels:
|
||||
{{- include "virtual-machine.labels" . | nindent 4 }}
|
||||
{{- if eq .Values.externalMethod "WholeIP" }}
|
||||
annotations:
|
||||
networking.cozystack.io/wholeIP: "true"
|
||||
{{- end }}
|
||||
spec:
|
||||
type: {{ ternary "LoadBalancer" "ClusterIP" .Values.external }}
|
||||
externalTrafficPolicy: Local
|
||||
{{- if (include "cozy-lib.network.disableLoadBalancerNodePorts" $ | fromYaml) }}
|
||||
{{- if ((include "cozy-lib.network.disableLoadBalancerNodePorts" $) | fromYaml) }}
|
||||
allocateLoadBalancerNodePorts: false
|
||||
{{- end }}
|
||||
selector:
|
||||
@@ -29,3 +27,27 @@ spec:
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: {{ include "virtual-machine.fullname" . }}
|
||||
spec:
|
||||
endpointSelector:
|
||||
matchLabels:
|
||||
{{- include "virtual-machine.selectorLabels" . | nindent 6 }}
|
||||
ingress:
|
||||
- fromEntities:
|
||||
- cluster
|
||||
- fromEntities:
|
||||
- world
|
||||
{{- if eq .Values.externalMethod "PortList" }}
|
||||
toPorts:
|
||||
- ports:
|
||||
{{- range .Values.externalPorts }}
|
||||
- port: {{ quote . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
egress:
|
||||
- toEntities:
|
||||
- world
|
||||
|
||||
@@ -41,7 +41,7 @@ spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: update-resources
|
||||
image: bitnami/kubectl:latest
|
||||
image: docker.io/alpine/k8s:1.33.4
|
||||
resources:
|
||||
requests:
|
||||
memory: "16Mi"
|
||||
@@ -54,14 +54,14 @@ spec:
|
||||
- |
|
||||
{{- if $needUpdateType }}
|
||||
echo "Patching VirtualMachine for instancetype update..."
|
||||
kubectl patch virtualmachine {{ $vmName }} -n {{ $namespace }} \
|
||||
kubectl patch virtualmachines.kubevirt.io {{ $vmName }} -n {{ $namespace }} \
|
||||
--type merge \
|
||||
-p '{"spec":{"instancetype":{"name": "{{ $instanceType }}", "revisionName": null}}}'
|
||||
{{- end }}
|
||||
|
||||
{{- if $needUpdateProfile }}
|
||||
echo "Patching VirtualMachine for preference update..."
|
||||
kubectl patch virtualmachine {{ $vmName }} -n {{ $namespace }} \
|
||||
kubectl patch virtualmachines.kubevirt.io {{ $vmName }} -n {{ $namespace }} \
|
||||
--type merge \
|
||||
-p '{"spec":{"preference":{"name": "{{ $instanceProfile }}", "revisionName": null}}}'
|
||||
{{- end }}
|
||||
|
||||
@@ -26,6 +26,7 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
policy.cozystack.io/allow-external-communication: "false"
|
||||
kubevirt.io/allow-pod-bridge-network-live-migration: "true"
|
||||
labels:
|
||||
{{- include "virtual-machine.labels" . | nindent 8 }}
|
||||
|
||||
@@ -24,15 +24,11 @@ image: pre-checks image-matchbox image-cozystack image-talos
|
||||
|
||||
image-cozystack:
|
||||
docker buildx build -f images/cozystack/Dockerfile ../../.. \
|
||||
--provenance false \
|
||||
--tag $(REGISTRY)/installer:$(call settag,$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/installer:latest \
|
||||
--platform linux/amd64 \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/installer.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
IMAGE="$(REGISTRY)/installer:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/installer.json -o json -r)" \
|
||||
yq -i '.cozystack.image = strenv(IMAGE)' values.yaml
|
||||
rm -f images/installer.json
|
||||
@@ -45,15 +41,12 @@ image-matchbox:
|
||||
test -f ../../../_out/assets/kernel-amd64 || make talos-kernel
|
||||
test -f ../../../_out/assets/initramfs-metal-amd64.xz || make talos-initramfs
|
||||
docker buildx build -f images/matchbox/Dockerfile ../../.. \
|
||||
--provenance false \
|
||||
--tag $(REGISTRY)/matchbox:$(call settag,$(TAG)) \
|
||||
--tag $(REGISTRY)/matchbox:$(call settag,$(TALOS_VERSION)-$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/matchbox:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/matchbox.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/matchbox:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/matchbox.json -o json -r)" \
|
||||
> ../../extra/bootbox/images/matchbox.tag
|
||||
rm -f images/matchbox.json
|
||||
|
||||
@@ -34,7 +34,7 @@ FROM alpine:3.22
|
||||
|
||||
RUN wget -O- https://github.com/cozystack/cozypkg/raw/refs/heads/main/hack/install.sh | sh -s -- -v 1.1.0
|
||||
|
||||
RUN apk add --no-cache make kubectl coreutils
|
||||
RUN apk add --no-cache make kubectl coreutils git jq
|
||||
|
||||
COPY --from=builder /src/scripts /cozystack/scripts
|
||||
COPY --from=builder /src/packages/core /cozystack/packages/core
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
cozystack:
|
||||
image: ghcr.io/cozystack/cozystack/installer:v0.35.2@sha256:1f1caa8b79869848e45b514c1bf29f96a196aa6cd439fa3c7212c30b0271638d
|
||||
image: ghcr.io/cozystack/cozystack/installer:v0.36.0-beta.4@sha256:51cb9828af3bdceac289de3b1161625065db22535a961958530e9c751880ee96
|
||||
|
||||
@@ -70,6 +70,12 @@ releases:
|
||||
privileged: true
|
||||
dependsOn: [cilium,kubeovn,cert-manager]
|
||||
|
||||
- name: kubeovn-plunger
|
||||
releaseName: kubeovn-plunger
|
||||
chart: cozy-kubeovn-plunger
|
||||
namespace: cozy-kubeovn
|
||||
dependsOn: [cilium,kubeovn]
|
||||
|
||||
- name: cozy-proxy
|
||||
releaseName: cozystack
|
||||
chart: cozy-cozy-proxy
|
||||
|
||||
@@ -77,7 +77,7 @@ spec:
|
||||
{{- $values = merge . $values }}
|
||||
{{- end }}
|
||||
{{- with index $cozyConfig.data (printf "values-%s" $x.name) }}
|
||||
{{- $values = merge (fromYaml .) $values }}
|
||||
{{- $values = mergeOverwrite $values (fromYaml .) }}
|
||||
{{- end }}
|
||||
{{- with $values }}
|
||||
values:
|
||||
|
||||
@@ -16,16 +16,11 @@ image: image-e2e-sandbox
|
||||
|
||||
image-e2e-sandbox:
|
||||
docker buildx build -f images/e2e-sandbox/Dockerfile images/e2e-sandbox \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/e2e-sandbox:$(call settag,$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/e2e-sandbox:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/e2e-sandbox.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
IMAGE="$(REGISTRY)/e2e-sandbox:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/e2e-sandbox.json -o json -r)" \
|
||||
yq -i '.e2e.image = strenv(IMAGE)' values.yaml
|
||||
rm -f images/e2e-sandbox.json
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
e2e:
|
||||
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.35.2@sha256:aef4672d4d6c3f28b2fcab966ff76dd6dca1917d5c22bf23aff2f37d4d906245
|
||||
image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.36.0-beta.4@sha256:c70ad3321fc14ca831c84bf6e7e6e5409bfe8130703173c277ca51db740c6cb3
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/matchbox:v0.35.2@sha256:83ec3d69d9e45bc964b803e6d52dc3c6902ebb1573efbc3fa1bd9adfdadf968b
|
||||
ghcr.io/cozystack/cozystack/matchbox:v0.36.0-beta.4@sha256:764c547a352c9c1d0442e43cdfd0ef50b216bd7f6e5514c777b1d90c4d95da92
|
||||
|
||||
@@ -3,4 +3,4 @@ name: etcd
|
||||
description: Storage for Kubernetes clusters
|
||||
icon: /logos/etcd.svg
|
||||
type: application
|
||||
version: 2.10.0
|
||||
version: 2.10.1
|
||||
|
||||
@@ -56,6 +56,9 @@ spec:
|
||||
{{- end }}
|
||||
{{- if $rawConstraints }}
|
||||
{{- $rawConstraints | fromYaml | toYaml | nindent 6 }}
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: etcd
|
||||
{{- else }}
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: 1
|
||||
|
||||
@@ -25,7 +25,7 @@ spec:
|
||||
serviceAccountName: etcd-hook
|
||||
containers:
|
||||
- name: kubectl
|
||||
image: bitnami/kubectl:latest
|
||||
image: docker.io/alpine/k8s:1.33.4
|
||||
command:
|
||||
- sh
|
||||
args:
|
||||
|
||||
@@ -3,4 +3,4 @@ name: ingress
|
||||
description: NGINX Ingress Controller
|
||||
icon: /logos/ingress-nginx.svg
|
||||
type: application
|
||||
version: 1.8.0
|
||||
version: 1.9.0
|
||||
|
||||
@@ -4,9 +4,13 @@
|
||||
|
||||
### Common parameters
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| ---------------- | ----------------------------------------------------------------- | ----------- | ------- |
|
||||
| `replicas` | Number of ingress-nginx replicas | `int` | `2` |
|
||||
| `whitelist` | List of client networks | `[]*string` | `[]` |
|
||||
| `clouflareProxy` | Restoring original visitor IPs when Cloudflare proxied is enabled | `bool` | `false` |
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | ------- |
|
||||
| `replicas` | Number of ingress-nginx replicas | `int` | `2` |
|
||||
| `whitelist` | List of client networks | `[]*string` | `[]` |
|
||||
| `cloudflareProxy` | Restoring original visitor IPs when Cloudflare proxied is enabled | `bool` | `false` |
|
||||
| `resources` | Explicit CPU and memory configuration for each ingress-nginx replica. When left empty, the preset defined in `resourcesPreset` is applied. | `*object` | `{}` |
|
||||
| `resources.cpu` | CPU available to each replica | `*quantity` | `null` |
|
||||
| `resources.memory` | Memory (RAM) available to each replica | `*quantity` | `null` |
|
||||
| `resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `micro` |
|
||||
|
||||
|
||||
@@ -15,14 +15,21 @@ spec:
|
||||
name: cozystack-system
|
||||
namespace: cozy-system
|
||||
version: '>= 0.0.0-0'
|
||||
install:
|
||||
remediation:
|
||||
retries: -1
|
||||
upgrade:
|
||||
remediation:
|
||||
retries: -1
|
||||
interval: 1m0s
|
||||
timeout: 5m0s
|
||||
timeout: 10m0s
|
||||
values:
|
||||
ingress-nginx:
|
||||
fullnameOverride: {{ trimPrefix "tenant-" .Release.Namespace }}-ingress
|
||||
controller:
|
||||
replicaCount: {{ .Values.replicas }}
|
||||
ingressClass: {{ .Release.Namespace }}
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 10 }}
|
||||
ingressClassResource:
|
||||
name: {{ .Release.Namespace }}
|
||||
controllerValue: k8s.io/ingress-nginx-{{ .Release.Namespace }}
|
||||
@@ -43,12 +50,12 @@ spec:
|
||||
type: LoadBalancer
|
||||
externalTrafficPolicy: Local
|
||||
{{- end }}
|
||||
{{- if or .Values.whitelist .Values.clouflareProxy }}
|
||||
{{- if or .Values.whitelist .Values.cloudflareProxy }}
|
||||
config:
|
||||
{{- with .Values.whitelist }}
|
||||
whitelist-source-range: "{{ join "," . }}"
|
||||
{{- end }}
|
||||
{{- if .Values.clouflareProxy }}
|
||||
{{- if .Values.cloudflareProxy }}
|
||||
set_real_ip_from: "{{ include "ingress.cloudflare-ips" . }}"
|
||||
use-forwarded-headers: "true"
|
||||
server-snippet: "real_ip_header CF-Connecting-IP;"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"title": "Chart Values",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"clouflareProxy": {
|
||||
"cloudflareProxy": {
|
||||
"description": "Restoring original visitor IPs when Cloudflare proxied is enabled",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
@@ -12,6 +12,53 @@
|
||||
"type": "integer",
|
||||
"default": 2
|
||||
},
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for each ingress-nginx replica. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "CPU available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "Memory (RAM) available to each replica",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "micro",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
},
|
||||
"whitelist": {
|
||||
"description": "List of client networks",
|
||||
"type": "array",
|
||||
|
||||
@@ -11,5 +11,16 @@ replicas: 2
|
||||
## - "10.100.0.0/16"
|
||||
whitelist: []
|
||||
|
||||
## @param clouflareProxy {bool} Restoring original visitor IPs when Cloudflare proxied is enabled
|
||||
clouflareProxy: false
|
||||
## @param cloudflareProxy {bool} Restoring original visitor IPs when Cloudflare proxied is enabled
|
||||
cloudflareProxy: false
|
||||
|
||||
## @param resources {*resources} Explicit CPU and memory configuration for each ingress-nginx replica. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @field resources.cpu {*quantity} CPU available to each replica
|
||||
## @field resources.memory {*quantity} Memory (RAM) available to each replica
|
||||
## Example:
|
||||
## resources:
|
||||
## cpu: 4000m
|
||||
## memory: 4Gi
|
||||
resources: {}
|
||||
## @param resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
resourcesPreset: "micro"
|
||||
|
||||
@@ -10,16 +10,11 @@ generate:
|
||||
|
||||
image:
|
||||
docker buildx build images/grafana \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/grafana:$(call settag,$(GRAFANA_TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/grafana:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/grafana.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/grafana:$(call settag,$(GRAFANA_TAG))@$$(yq e '."containerimage.digest"' images/grafana.json -o json -r)" \
|
||||
> images/grafana.tag
|
||||
rm -f images/grafana.json
|
||||
|
||||
@@ -16,7 +16,7 @@ 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: 0.6.0
|
||||
version: 0.7.0
|
||||
|
||||
# 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
|
||||
|
||||
@@ -4,19 +4,55 @@
|
||||
|
||||
### Common parameters
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------ | ------------------- | -------- |
|
||||
| `host` | The hostname used to access the SeaweedFS externally (defaults to 's3' subdomain for the tenant host). | `*string` | `""` |
|
||||
| `topology` | The topology of the SeaweedFS cluster. (allowed values: Simple, MultiZone, Client) | `string` | `Simple` |
|
||||
| `replicationFactor` | Replication factor: number of replicas for each volume in the SeaweedFS cluster. | `int` | `2` |
|
||||
| `replicas` | Number of replicas | `int` | `2` |
|
||||
| `size` | Persistent Volume size | `quantity` | `10Gi` |
|
||||
| `storageClass` | StorageClass used to store the data | `*string` | `""` |
|
||||
| `zones` | A map of zones for MultiZone topology. Each zone can have its own number of replicas and size. | `map[string]object` | `{...}` |
|
||||
| `zones[name].replicas` | Number of replicas in the zone | `int` | `0` |
|
||||
| `zones[name].size` | Zone storage size | `quantity` | `""` |
|
||||
| `filer` | Filer service configuration | `*object` | `{}` |
|
||||
| `filer.grpcHost` | The hostname used to expose or access the filer service externally. | `*string` | `""` |
|
||||
| `filer.grpcPort` | The port used to access the filer service externally. | `*int` | `443` |
|
||||
| `filer.whitelist` | A list of IP addresses or CIDR ranges that are allowed to access the filer service. | `[]*string` | `[]` |
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------ | --------- | -------- |
|
||||
| `host` | The hostname used to access the SeaweedFS externally (defaults to 's3' subdomain for the tenant host). | `*string` | `""` |
|
||||
| `topology` | The topology of the SeaweedFS cluster. (allowed values: Simple, MultiZone, Client) | `string` | `Simple` |
|
||||
| `replicationFactor` | Replication factor: number of replicas for each volume in the SeaweedFS cluster. | `int` | `2` |
|
||||
|
||||
|
||||
### SeaweedFS Components Configuration
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------- |
|
||||
| `db` | Database Configuration | `object` | `{}` |
|
||||
| `db.replicas` | Number of database replicas | `*int` | `2` |
|
||||
| `db.size` | Persistent Volume size | `*quantity` | `10Gi` |
|
||||
| `db.storageClass` | StorageClass used to store the data | `*string` | `""` |
|
||||
| `db.resources` | Explicit CPU and memory configuration for the database. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `db.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `db.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `db.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `master` | Master service configuration | `*object` | `{}` |
|
||||
| `master.replicas` | Number of master replicas | `*int` | `3` |
|
||||
| `master.resources` | Explicit CPU and memory configuration for the master. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `master.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `master.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `master.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `filer` | Filer service configuration | `*object` | `{}` |
|
||||
| `filer.replicas` | Number of filer replicas | `*int` | `2` |
|
||||
| `filer.resources` | Explicit CPU and memory configuration for the filer. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `filer.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `filer.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `filer.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `filer.grpcHost` | The hostname used to expose or access the filer service externally. | `*string` | `""` |
|
||||
| `filer.grpcPort` | The port used to access the filer service externally. | `*int` | `443` |
|
||||
| `filer.whitelist` | A list of IP addresses or CIDR ranges that are allowed to access the filer service. | `[]*string` | `[]` |
|
||||
| `volume` | Volume service configuration | `*object` | `{}` |
|
||||
| `volume.replicas` | Number of volume replicas | `*int` | `2` |
|
||||
| `volume.size` | Persistent Volume size | `*quantity` | `10Gi` |
|
||||
| `volume.storageClass` | StorageClass used to store the data | `*string` | `""` |
|
||||
| `volume.resources` | Explicit CPU and memory configuration for the volume. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `volume.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `volume.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `volume.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
| `volume.zones` | A map of zones for MultiZone topology. Each zone can have its own number of replicas and size. | `map[string]object` | `{}` |
|
||||
| `volume.zones.replicas` | Number of replicas in the zone | `*int` | `null` |
|
||||
| `volume.zones.size` | Zone storage size | `*quantity` | `null` |
|
||||
| `s3` | S3 service configuration | `*object` | `{}` |
|
||||
| `s3.replicas` | Number of s3 replicas | `*int` | `2` |
|
||||
| `s3.resources` | Explicit CPU and memory configuration for the s3. When left empty, the preset defined in `resourcesPreset` is applied. | `object` | `{}` |
|
||||
| `s3.resources.cpu` | The number of CPU cores allocated | `*quantity` | `null` |
|
||||
| `s3.resources.memory` | The amount of memory allocated | `*quantity` | `null` |
|
||||
| `s3.resourcesPreset` | Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`. | `string` | `small` |
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.35.2@sha256:93ffb888755b2914fbbfc402a1daf450d628bced71629c53627b1b759740df72
|
||||
ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.36.0-beta.4@sha256:24451989b15b6801b33ad355a5507307d0333bf9afd240f1db0aca9c92f6b2ad
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
{{- fail "Invalid value for .Values.replicationFactor. Must be at least 1." }}
|
||||
{{- end }}
|
||||
{{- if eq .Values.topology "MultiZone" }}
|
||||
{{- if (eq (len .Values.zones) 0) }}
|
||||
{{- if (eq (len .Values.volume.zones) 0) }}
|
||||
{{- fail "Zones must be defined for MultiZone topology." }}
|
||||
{{- end }}
|
||||
{{- if and (hasKey .Values "zones") (gt (int .Values.replicationFactor) (len .Values.zones)) }}
|
||||
{{- fail "replicationFactor must be less than or equal to the number of zones defined in .Values.zones." }}
|
||||
{{- if and (hasKey .Values.volume "zones") (gt (int .Values.replicationFactor) (len .Values.volume.zones)) }}
|
||||
{{- fail "replicationFactor must be less than or equal to the number of zones defined in .Values.volume.zones." }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -51,11 +51,24 @@ spec:
|
||||
name: cozystack-system
|
||||
namespace: cozy-system
|
||||
version: '>= 0.0.0-0'
|
||||
install:
|
||||
remediation:
|
||||
retries: -1
|
||||
upgrade:
|
||||
remediation:
|
||||
retries: -1
|
||||
interval: 1m0s
|
||||
timeout: 5m0s
|
||||
timeout: 10m0s
|
||||
values:
|
||||
global:
|
||||
serviceAccountName: "{{ .Release.Namespace }}-seaweedfs"
|
||||
db:
|
||||
replicas: {{ .Values.db.replicas }}
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.db.resourcesPreset .Values.db.resources $) | nindent 8 }}
|
||||
size: {{ .Values.db.size }}
|
||||
{{- with .Values.db.storageClass }}
|
||||
storageClass: {{ . }}
|
||||
{{- end }}
|
||||
seaweedfs:
|
||||
master:
|
||||
{{ if eq .Values.topology "Simple" }}
|
||||
@@ -63,36 +76,25 @@ spec:
|
||||
{{- else if eq .Values.topology "MultiZone" }}
|
||||
defaultReplicaPlacement: "{{ sub .Values.replicationFactor 1 }}00"
|
||||
{{- end }}
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
replicas: {{ .Values.master.replicas }}
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.master.resourcesPreset .Values.master.resources $) | nindent 10 }}
|
||||
volume:
|
||||
{{ if eq .Values.topology "MultiZone" }}
|
||||
enabled: false
|
||||
{{- end }}
|
||||
replicas: {{ .Values.replicas }}
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
replicas: {{ .Values.volume.replicas }}
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.volume.resourcesPreset .Values.volume.resources $) | nindent 10 }}
|
||||
dataDirs:
|
||||
- name: data1
|
||||
type: "persistentVolumeClaim"
|
||||
size: "{{ .Values.size }}"
|
||||
{{- with .Values.storageClass }}
|
||||
size: "{{ .Values.volume.size }}"
|
||||
{{- with .Values.volume.storageClass }}
|
||||
storageClass: {{ . }}
|
||||
{{- end }}
|
||||
maxVolumes: 0
|
||||
{{ if eq .Values.topology "MultiZone" }}
|
||||
volumes:
|
||||
{{- range $zoneName, $zone := .Values.zones }}
|
||||
{{- range $zoneName, $zone := .Values.volume.zones }}
|
||||
{{ $zoneName }}:
|
||||
{{ with $zone.replicas }}
|
||||
replicas: {{ . }}
|
||||
@@ -107,8 +109,8 @@ spec:
|
||||
{{- end }}
|
||||
{{- if $zone.storageClass }}
|
||||
storageClass: {{ $zone.storageClass }}
|
||||
{{- else if $.Values.storageClass }}
|
||||
storageClass: {{ $.Values.storageClass }}
|
||||
{{- else if $.Values.volume.storageClass }}
|
||||
storageClass: {{ $.Values.volume.storageClass }}
|
||||
{{- end }}
|
||||
maxVolumes: 0
|
||||
nodeSelector: |
|
||||
@@ -124,13 +126,7 @@ spec:
|
||||
{{- end }}
|
||||
s3:
|
||||
domainName: {{ .Values.host | default (printf "s3.%s" $host) }}
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.filer.resourcesPreset .Values.filer.resources $) | nindent 10 }}
|
||||
s3:
|
||||
ingress:
|
||||
className: {{ $ingress }}
|
||||
@@ -144,6 +140,7 @@ spec:
|
||||
- hosts:
|
||||
- {{ .Values.host | default (printf "s3.%s" $host) }}
|
||||
secretName: {{ .Release.Name }}-s3-ingress-tls
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.s3.resourcesPreset .Values.s3.resources $) | nindent 10 }}
|
||||
cosi:
|
||||
driverName: "{{ .Release.Namespace }}.seaweedfs.objectstorage.k8s.io"
|
||||
bucketClassName: "{{ .Release.Namespace }}"
|
||||
@@ -160,8 +157,8 @@ kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}-master
|
||||
spec:
|
||||
replicas: 3
|
||||
minReplicas: 2
|
||||
replicas: {{ .Values.master.replicas }}
|
||||
minReplicas: {{ div .Values.master.replicas 2 | add1 }}
|
||||
kind: seaweedfs
|
||||
type: master
|
||||
selector:
|
||||
@@ -174,7 +171,7 @@ kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}-filer
|
||||
spec:
|
||||
replicas: 2
|
||||
replicas: {{ .Values.filer.replicas }}
|
||||
minReplicas: 1
|
||||
kind: seaweedfs
|
||||
type: filer
|
||||
@@ -182,27 +179,47 @@ spec:
|
||||
app.kubernetes.io/component: filer
|
||||
app.kubernetes.io/name: seaweedfs
|
||||
version: {{ $.Chart.Version }}
|
||||
|
||||
{{ if eq .Values.topology "Simple" }}
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}-volume
|
||||
spec:
|
||||
replicas: {{ .Values.replicas }}
|
||||
minReplicas: {{ div .Values.replicas 2 | add1 }}
|
||||
replicas: {{ .Values.volume.replicas }}
|
||||
minReplicas: 1
|
||||
kind: seaweedfs
|
||||
type: volume
|
||||
selector:
|
||||
app.kubernetes.io/component: volume
|
||||
app.kubernetes.io/name: seaweedfs
|
||||
version: {{ $.Chart.Version }}
|
||||
{{- else if eq .Values.topology "MultiZone" }}
|
||||
{{- range $zoneName, $zoneSpec := .Values.volume.zones }}
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}-volume-{{ $zoneName }}
|
||||
spec:
|
||||
replicas: {{ default $.Values.volume.replicas $zoneSpec.replicas }}
|
||||
minReplicas: 1
|
||||
kind: seaweedfs
|
||||
type: volume
|
||||
selector:
|
||||
app.kubernetes.io/component: volume-{{ $zoneName }}
|
||||
app.kubernetes.io/name: seaweedfs
|
||||
version: {{ $.Chart.Version }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}-db
|
||||
spec:
|
||||
replicas: 2
|
||||
replicas: {{ .Values.db.replicas }}
|
||||
minReplicas: 1
|
||||
kind: seaweedfs
|
||||
type: postgres
|
||||
@@ -210,4 +227,18 @@ spec:
|
||||
cnpg.io/cluster: seaweedfs-db
|
||||
cnpg.io/podRole: instance
|
||||
version: {{ $.Chart.Version }}
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}-s3
|
||||
spec:
|
||||
replicas: {{ .Values.s3.replicas }}
|
||||
minReplicas: 1
|
||||
kind: seaweedfs
|
||||
type: s3
|
||||
selector:
|
||||
app.kubernetes.io/component: s3
|
||||
app.kubernetes.io/name: seaweedfs
|
||||
version: {{ $.Chart.Version }}
|
||||
{{- end }}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
{{- if not (eq .Values.topology "Client") }}
|
||||
apiVersion: autoscaling.k8s.io/v1
|
||||
kind: VerticalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-filer
|
||||
spec:
|
||||
targetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
name: {{ .Release.Name }}-filer
|
||||
updatePolicy:
|
||||
updateMode: Auto
|
||||
resourcePolicy:
|
||||
containerPolicies:
|
||||
- containerName: seaweedfs
|
||||
minAllowed:
|
||||
cpu: 25m
|
||||
memory: 64Mi
|
||||
maxAllowed:
|
||||
cpu: "1"
|
||||
memory: 2048Mi
|
||||
|
||||
---
|
||||
|
||||
apiVersion: autoscaling.k8s.io/v1
|
||||
kind: VerticalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-master
|
||||
spec:
|
||||
targetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
name: {{ .Release.Name }}-master
|
||||
updatePolicy:
|
||||
updateMode: Auto
|
||||
resourcePolicy:
|
||||
containerPolicies:
|
||||
- containerName: seaweedfs
|
||||
minAllowed:
|
||||
cpu: 25m
|
||||
memory: 64Mi
|
||||
maxAllowed:
|
||||
cpu: "1"
|
||||
memory: 2048Mi
|
||||
|
||||
---
|
||||
|
||||
apiVersion: autoscaling.k8s.io/v1
|
||||
kind: VerticalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-volume
|
||||
spec:
|
||||
targetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
name: {{ .Release.Name }}-volume
|
||||
updatePolicy:
|
||||
updateMode: Auto
|
||||
resourcePolicy:
|
||||
containerPolicies:
|
||||
- containerName: seaweedfs
|
||||
minAllowed:
|
||||
cpu: 25m
|
||||
memory: 64Mi
|
||||
maxAllowed:
|
||||
cpu: "1"
|
||||
memory: 2048Mi
|
||||
{{- end }}
|
||||
@@ -2,14 +2,108 @@
|
||||
"title": "Chart Values",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"db": {
|
||||
"description": "Database Configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"replicas": 2,
|
||||
"resources": {},
|
||||
"resourcesPreset": "small",
|
||||
"size": "10Gi",
|
||||
"storageClass": ""
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
],
|
||||
"properties": {
|
||||
"replicas": {
|
||||
"description": "Number of database replicas",
|
||||
"type": "integer",
|
||||
"default": 2
|
||||
},
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for the database. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "The number of CPU cores allocated",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "The amount of memory allocated",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "small",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
},
|
||||
"size": {
|
||||
"description": "Persistent Volume size",
|
||||
"default": "10Gi",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"storageClass": {
|
||||
"description": "StorageClass used to store the data",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"filer": {
|
||||
"description": "Filer service configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"grpcHost": "",
|
||||
"grpcPort": 443,
|
||||
"replicas": 2,
|
||||
"resources": {},
|
||||
"resourcesPreset": "small",
|
||||
"whitelist": {}
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
],
|
||||
"properties": {
|
||||
"grpcHost": {
|
||||
"description": "The hostname used to expose or access the filer service externally.",
|
||||
@@ -20,6 +114,58 @@
|
||||
"type": "integer",
|
||||
"default": 443
|
||||
},
|
||||
"replicas": {
|
||||
"description": "Number of filer replicas",
|
||||
"type": "integer",
|
||||
"default": 2
|
||||
},
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for the filer. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "The number of CPU cores allocated",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "The amount of memory allocated",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "small",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
},
|
||||
"whitelist": {
|
||||
"description": "A list of IP addresses or CIDR ranges that are allowed to access the filer service.",
|
||||
"type": "array",
|
||||
@@ -34,33 +180,144 @@
|
||||
"description": "The hostname used to access the SeaweedFS externally (defaults to 's3' subdomain for the tenant host).",
|
||||
"type": "string"
|
||||
},
|
||||
"replicas": {
|
||||
"description": "Number of replicas",
|
||||
"type": "integer",
|
||||
"default": 2
|
||||
"master": {
|
||||
"description": "Master service configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"replicas": 3,
|
||||
"resources": {},
|
||||
"resourcesPreset": "small"
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
],
|
||||
"properties": {
|
||||
"replicas": {
|
||||
"description": "Number of master replicas",
|
||||
"type": "integer",
|
||||
"default": 3
|
||||
},
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for the master. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "The number of CPU cores allocated",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "The amount of memory allocated",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "small",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"replicationFactor": {
|
||||
"description": "Replication factor: number of replicas for each volume in the SeaweedFS cluster.",
|
||||
"type": "integer",
|
||||
"default": 2
|
||||
},
|
||||
"size": {
|
||||
"description": "Persistent Volume size",
|
||||
"default": "10Gi",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
"s3": {
|
||||
"description": "S3 service configuration",
|
||||
"type": "object",
|
||||
"default": {
|
||||
"replicas": 2,
|
||||
"resources": {},
|
||||
"resourcesPreset": "small"
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"storageClass": {
|
||||
"description": "StorageClass used to store the data",
|
||||
"type": "string"
|
||||
"properties": {
|
||||
"replicas": {
|
||||
"description": "Number of s3 replicas",
|
||||
"type": "integer",
|
||||
"default": 2
|
||||
},
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for the s3. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "The number of CPU cores allocated",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "The amount of memory allocated",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "small",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"topology": {
|
||||
"description": "The topology of the SeaweedFS cluster. (allowed values: Simple, MultiZone, Client)",
|
||||
@@ -72,33 +329,117 @@
|
||||
"Client"
|
||||
]
|
||||
},
|
||||
"zones": {
|
||||
"description": "A map of zones for MultiZone topology. Each zone can have its own number of replicas and size.",
|
||||
"volume": {
|
||||
"description": "Volume service configuration",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"replicas",
|
||||
"size"
|
||||
],
|
||||
"properties": {
|
||||
"replicas": {
|
||||
"description": "Number of replicas in the zone",
|
||||
"type": "integer"
|
||||
},
|
||||
"size": {
|
||||
"description": "Zone storage size",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"default": {
|
||||
"replicas": 2,
|
||||
"resources": {},
|
||||
"resourcesPreset": "small",
|
||||
"size": "10Gi",
|
||||
"storageClass": "",
|
||||
"zones": {}
|
||||
},
|
||||
"required": [
|
||||
"resources",
|
||||
"resourcesPreset"
|
||||
],
|
||||
"properties": {
|
||||
"replicas": {
|
||||
"description": "Number of volume replicas",
|
||||
"type": "integer",
|
||||
"default": 2
|
||||
},
|
||||
"resources": {
|
||||
"description": "Explicit CPU and memory configuration for the volume. When left empty, the preset defined in `resourcesPreset` is applied.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"description": "The number of CPU cores allocated",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"memory": {
|
||||
"description": "The amount of memory allocated",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"resourcesPreset": {
|
||||
"description": "Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.",
|
||||
"type": "string",
|
||||
"default": "small",
|
||||
"enum": [
|
||||
"nano",
|
||||
"micro",
|
||||
"small",
|
||||
"medium",
|
||||
"large",
|
||||
"xlarge",
|
||||
"2xlarge"
|
||||
]
|
||||
},
|
||||
"size": {
|
||||
"description": "Persistent Volume size",
|
||||
"default": "10Gi",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
},
|
||||
"storageClass": {
|
||||
"description": "StorageClass used to store the data",
|
||||
"type": "string"
|
||||
},
|
||||
"zones": {
|
||||
"description": "A map of zones for MultiZone topology. Each zone can have its own number of replicas and size.",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"replicas": {
|
||||
"description": "Number of replicas in the zone",
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
"size": {
|
||||
"description": "Zone storage size",
|
||||
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
],
|
||||
"x-kubernetes-int-or-string": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,42 +4,93 @@
|
||||
host: ""
|
||||
|
||||
## @param topology {string enum:"Simple,MultiZone,Client"} The topology of the SeaweedFS cluster. (allowed values: Simple, MultiZone, Client)
|
||||
##
|
||||
topology: Simple
|
||||
|
||||
## @param replicationFactor {int} Replication factor: number of replicas for each volume in the SeaweedFS cluster.
|
||||
replicationFactor: 2
|
||||
|
||||
## @param replicas {int} Number of replicas
|
||||
## @section SeaweedFS Components Configuration
|
||||
##
|
||||
replicas: 2
|
||||
## @param size {quantity} Persistent Volume size
|
||||
size: 10Gi
|
||||
## @param storageClass {*string} StorageClass used to store the data
|
||||
storageClass: ""
|
||||
## @param db {db} Database Configuration
|
||||
db:
|
||||
## @field db.replicas {*int} Number of database replicas
|
||||
## @field db.size {*quantity} Persistent Volume size
|
||||
## @field db.storageClass {*string} StorageClass used to store the data
|
||||
replicas: 2
|
||||
size: "10Gi"
|
||||
storageClass: ""
|
||||
## @field db.resources {resources} Explicit CPU and memory configuration for the database. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @field resources {*resources} Resource configuration for etcd
|
||||
## @field resources.cpu {*quantity} The number of CPU cores allocated
|
||||
## @field resources.memory {*quantity} The amount of memory allocated
|
||||
## e.g:
|
||||
## resources:
|
||||
## cpu: 4000m
|
||||
## memory: 4Gi
|
||||
##
|
||||
## @field db.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
resources: {}
|
||||
resourcesPreset: "small"
|
||||
|
||||
## @param zones {map[string]zone} A map of zones for MultiZone topology. Each zone can have its own number of replicas and size.
|
||||
## @field zone.replicas {int} Number of replicas in the zone
|
||||
## @field zone.size {quantity} Zone storage size
|
||||
## Example:
|
||||
## zones:
|
||||
## dc1:
|
||||
## replicas: 2
|
||||
## size: 10Gi
|
||||
## dc2:
|
||||
## replicas: 2
|
||||
## size: 10Gi
|
||||
## dc3:
|
||||
## replicas: 2
|
||||
## size: 10Gi
|
||||
zones: {}
|
||||
## @param master {*master} Master service configuration
|
||||
master:
|
||||
## @field master.replicas {*int} Number of master replicas
|
||||
## @field master.resources {resources} Explicit CPU and memory configuration for the master. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @field master.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
replicas: 3
|
||||
resources: {}
|
||||
resourcesPreset: "small"
|
||||
|
||||
## @param filer {*filer} Filer service configuration
|
||||
## @field filer.grpcHost {*string} The hostname used to expose or access the filer service externally.
|
||||
## @field filer.grpcPort {*int} The port used to access the filer service externally.
|
||||
## TODO: select a more appropriate type after resolving https://github.com/cozystack/cozyvalues-gen/issues/4
|
||||
## @field filer.whitelist {[]*string} A list of IP addresses or CIDR ranges that are allowed to access the filer service.
|
||||
filer:
|
||||
## @field filer.replicas {*int} Number of filer replicas
|
||||
## @field filer.resources {resources} Explicit CPU and memory configuration for the filer. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @field filer.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
replicas: 2
|
||||
resources: {}
|
||||
resourcesPreset: "small"
|
||||
|
||||
## @field filer.grpcHost {*string} The hostname used to expose or access the filer service externally.
|
||||
## @field filer.grpcPort {*int} The port used to access the filer service externally.
|
||||
## @field filer.whitelist {[]*string} A list of IP addresses or CIDR ranges that are allowed to access the filer service.
|
||||
grpcHost: ""
|
||||
grpcPort: 443
|
||||
whitelist: []
|
||||
|
||||
## @param volume {*volume} Volume service configuration
|
||||
volume:
|
||||
## @field volume.replicas {*int} Number of volume replicas
|
||||
## @field volume.size {*quantity} Persistent Volume size
|
||||
## @field volume.storageClass {*string} StorageClass used to store the data
|
||||
## @field volume.resources {resources} Explicit CPU and memory configuration for the volume. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @field volume.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
replicas: 2
|
||||
size: 10Gi
|
||||
storageClass: ""
|
||||
resources: {}
|
||||
resourcesPreset: "small"
|
||||
|
||||
## @field volume.zones {map[string]zone} A map of zones for MultiZone topology. Each zone can have its own number of replicas and size.
|
||||
## @field zone.replicas {*int} Number of replicas in the zone
|
||||
## @field zone.size {*quantity} Zone storage size
|
||||
## Example:
|
||||
## zones:
|
||||
## dc1:
|
||||
## replicas: 2
|
||||
## size: 10Gi
|
||||
## dc2:
|
||||
## replicas: 2
|
||||
## size: 10Gi
|
||||
## dc3:
|
||||
## replicas: 2
|
||||
## size: 10Gi
|
||||
zones: {}
|
||||
|
||||
## @param s3 {*s3} S3 service configuration
|
||||
s3:
|
||||
## @field s3.replicas {*int} Number of s3 replicas
|
||||
## @field s3.resources {resources} Explicit CPU and memory configuration for the s3. When left empty, the preset defined in `resourcesPreset` is applied.
|
||||
## @field s3.resourcesPreset {string enum:"nano,micro,small,medium,large,xlarge,2xlarge"} Default sizing preset used when `resources` is omitted. Allowed values: `nano`, `micro`, `small`, `medium`, `large`, `xlarge`, `2xlarge`.
|
||||
replicas: 2
|
||||
resources: {}
|
||||
resourcesPreset: "small"
|
||||
|
||||
@@ -16,7 +16,8 @@ etcd 2.7.0 632224a3
|
||||
etcd 2.8.0 4369b031
|
||||
etcd 2.9.0 8ddbe32e
|
||||
etcd 2.9.1 c02a3818
|
||||
etcd 2.10.0 HEAD
|
||||
etcd 2.10.0 7f477eec
|
||||
etcd 2.10.1 HEAD
|
||||
info 1.0.0 93bdf411
|
||||
info 1.0.1 632224a3
|
||||
info 1.1.0 c02a3818
|
||||
@@ -29,7 +30,8 @@ ingress 1.4.0 fd240701
|
||||
ingress 1.5.0 93bdf411
|
||||
ingress 1.6.0 632224a3
|
||||
ingress 1.7.0 c02a3818
|
||||
ingress 1.8.0 HEAD
|
||||
ingress 1.8.0 8f1975d1
|
||||
ingress 1.9.0 HEAD
|
||||
monitoring 1.0.0 d7cfa53c
|
||||
monitoring 1.1.0 25221fdc
|
||||
monitoring 1.2.0 f81be075
|
||||
@@ -62,4 +64,5 @@ seaweedfs 0.3.0 45a7416c
|
||||
seaweedfs 0.4.0 632224a3
|
||||
seaweedfs 0.4.1 8c86905b
|
||||
seaweedfs 0.5.0 9584e5f5
|
||||
seaweedfs 0.6.0 HEAD
|
||||
seaweedfs 0.6.0 8f1975d1
|
||||
seaweedfs 0.7.0 HEAD
|
||||
|
||||
@@ -172,3 +172,17 @@
|
||||
{{- $xmsMi := min (div $memoryLimitInt 4194304) (div $memoryRequestInt 1048576) }}
|
||||
{{- printf `-Xms%dm -Xmx%dm` $xmsMi $xmxMi }}
|
||||
{{- end }}
|
||||
|
||||
{{- define "cozy-lib.resources.flatten" -}}
|
||||
{{- $out := dict -}}
|
||||
{{- $res := include "cozy-lib.resources.sanitize" . | fromYaml -}}
|
||||
{{- range $section, $values := $res }}
|
||||
{{- range $k, $v := $values }}
|
||||
{{- $key := printf "%s.%s" $section $k }}
|
||||
{{- if ne $key "limits.storage" }}
|
||||
{{- $_ := set $out $key $v }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- $out | toYaml }}
|
||||
{{- end }}
|
||||
|
||||
@@ -12,16 +12,11 @@ image: image-s3manager
|
||||
|
||||
image-s3manager:
|
||||
docker buildx build images/s3manager \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/s3manager:$(call settag,$(S3MANAGER_TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/s3manager:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/s3manager.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
echo "$(REGISTRY)/s3manager:$(call settag,$(S3MANAGER_TAG))@$$(yq e '."containerimage.digest"' images/s3manager.json -o json -r)" \
|
||||
> images/s3manager.tag
|
||||
rm -f images/s3manager.json
|
||||
|
||||
@@ -1 +1 @@
|
||||
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:084c6e1665762cae38bac235c79c00d4ec61b5e73ad46ebf7eed596b2c5fcd0a
|
||||
ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:b9a401defb90a822087e50e7ab6afd9b4db7e71728030f92c7d320ac46889053
|
||||
|
||||
@@ -17,17 +17,12 @@ update:
|
||||
|
||||
image:
|
||||
docker buildx build images/cilium \
|
||||
--provenance false \
|
||||
--builder=$(BUILDER) \
|
||||
--platform=$(PLATFORM) \
|
||||
--tag $(REGISTRY)/cilium:$(call settag,$(CILIUM_TAG)) \
|
||||
--tag $(REGISTRY)/cilium:$(call settag,$(CILIUM_TAG)-$(TAG)) \
|
||||
--cache-from type=registry,ref=$(REGISTRY)/cilium:latest \
|
||||
--cache-to type=inline \
|
||||
--metadata-file images/cilium.json \
|
||||
--push=$(PUSH) \
|
||||
--label "org.opencontainers.image.source=https://github.com/cozystack/cozystack" \
|
||||
--load=$(LOAD)
|
||||
$(BUILDX_ARGS)
|
||||
REPOSITORY="$(REGISTRY)/cilium" \
|
||||
yq -i '.cilium.image.repository = strenv(REPOSITORY)' values.yaml
|
||||
TAG=$(call settag,$(CILIUM_TAG)) \
|
||||
|
||||
3
packages/system/coredns/Chart.yaml
Normal file
3
packages/system/coredns/Chart.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
apiVersion: v2
|
||||
name: cozy-coredns
|
||||
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process
|
||||
10
packages/system/coredns/Makefile
Normal file
10
packages/system/coredns/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
export NAME=coredns
|
||||
export NAMESPACE=kube-system
|
||||
|
||||
include ../../../scripts/package.mk
|
||||
|
||||
update:
|
||||
rm -rf charts
|
||||
helm repo add cozy-coredns https://coredns.github.io/helm
|
||||
helm repo update cozy-coredns
|
||||
helm pull cozy-coredns/coredns --untar --untardir charts
|
||||
23
packages/system/coredns/charts/coredns/.helmignore
Normal file
23
packages/system/coredns/charts/coredns/.helmignore
Normal file
@@ -0,0 +1,23 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
OWNERS
|
||||
*.tests
|
||||
24
packages/system/coredns/charts/coredns/Chart.yaml
Normal file
24
packages/system/coredns/charts/coredns/Chart.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
annotations:
|
||||
artifacthub.io/changes: |
|
||||
- kind: changed
|
||||
description: Bump to CoreDNS 1.12.3
|
||||
apiVersion: v2
|
||||
appVersion: 1.12.3
|
||||
description: CoreDNS is a DNS server that chains plugins and provides Kubernetes DNS
|
||||
Services
|
||||
home: https://coredns.io
|
||||
icon: https://coredns.io/images/CoreDNS_Colour_Horizontal.png
|
||||
keywords:
|
||||
- coredns
|
||||
- dns
|
||||
- kubedns
|
||||
maintainers:
|
||||
- name: mrueg
|
||||
- name: haad
|
||||
- name: hagaibarel
|
||||
- name: shubham-cmyk
|
||||
name: coredns
|
||||
sources:
|
||||
- https://github.com/coredns/coredns
|
||||
type: application
|
||||
version: 1.43.2
|
||||
316
packages/system/coredns/charts/coredns/README.md
Normal file
316
packages/system/coredns/charts/coredns/README.md
Normal file
@@ -0,0 +1,316 @@
|
||||
# CoreDNS
|
||||
|
||||
[CoreDNS](https://coredns.io/) is a DNS server that chains plugins and provides DNS Services
|
||||
|
||||
# TL;DR;
|
||||
|
||||
```console
|
||||
$ helm repo add coredns https://coredns.github.io/helm
|
||||
$ helm --namespace=kube-system install coredns coredns/coredns
|
||||
```
|
||||
|
||||
## Introduction
|
||||
|
||||
This chart bootstraps a [CoreDNS](https://github.com/coredns/coredns) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. This chart will provide DNS Services and can be deployed in multiple configuration to support various scenarios listed below:
|
||||
|
||||
- CoreDNS as a cluster dns service and a drop-in replacement for Kube/SkyDNS. This is the default mode and CoreDNS is deployed as cluster-service in kube-system namespace. This mode is chosen by setting `isClusterService` to true.
|
||||
- CoreDNS as an external dns service. In this mode CoreDNS is deployed as any kubernetes app in user specified namespace. The CoreDNS service can be exposed outside the cluster by using using either the NodePort or LoadBalancer type of service. This mode is chosen by setting `isClusterService` to false.
|
||||
- CoreDNS as an external dns provider for kubernetes federation. This is a sub case of 'external dns service' which uses etcd plugin for CoreDNS backend. This deployment mode as a dependency on `etcd-operator` chart, which needs to be pre-installed.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes 1.10 or later
|
||||
|
||||
## Installing the Chart
|
||||
|
||||
The chart can be installed as follows:
|
||||
|
||||
```console
|
||||
$ helm repo add coredns https://coredns.github.io/helm
|
||||
$ helm --namespace=kube-system install coredns coredns/coredns
|
||||
```
|
||||
|
||||
The command deploys CoreDNS on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists various ways to override default configuration during deployment.
|
||||
|
||||
> **Tip**: List all releases using `helm list --all-namespaces`
|
||||
|
||||
## OCI installing
|
||||
|
||||
The chart can also be installed using the following:
|
||||
|
||||
```console
|
||||
$ helm --namespace=kube-system install coredns oci://ghcr.io/coredns/charts/coredns --version 1.38.0
|
||||
```
|
||||
|
||||
The command deploys the `1.38.0` version of CoreDNS on the Kubernetes cluster in the default configuration.
|
||||
|
||||
## Helm Unit Testing & Debugging Guide
|
||||
|
||||
This document explains how to write, run, and debug Helm unit tests for this chart using [helm-unittest](https://github.com/helm-unittest/helm-unittest).
|
||||
|
||||
---
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Install the Helm unittest plugin:
|
||||
|
||||
```bash
|
||||
helm plugin install https://github.com/helm-unittest/helm-unittest
|
||||
```
|
||||
|
||||
### Running Unit Tests
|
||||
|
||||
Run all unit tests in the chart folder (e.g., `./coredns`):
|
||||
|
||||
```bash
|
||||
helm unittest ./coredns
|
||||
```
|
||||
|
||||
To output results in **JUnit XML** format:
|
||||
|
||||
```bash
|
||||
mkdir -p test-results
|
||||
helm unittest --strict \
|
||||
--output-type JUnit \
|
||||
--output-file test-results/helm-unittest-report.xml \
|
||||
./coredns
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Debugging Helm Charts
|
||||
|
||||
Render the chart templates with real values to debug:
|
||||
|
||||
```bash
|
||||
helm template ./coredns --debug
|
||||
```
|
||||
## YAML Intellisense in VS Code
|
||||
|
||||
Add this line at the top of your unit test YAML files for schema validation and autocompletion in VS Code:
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/helm-unittest/helm-unittest/main/schema/helm-testsuite.json
|
||||
```
|
||||
|
||||
This improves YAML editing and error highlighting for test definitions.
|
||||
|
||||
## Uninstalling the Chart
|
||||
|
||||
To uninstall/delete the `coredns` deployment:
|
||||
|
||||
```console
|
||||
$ helm uninstall coredns
|
||||
```
|
||||
|
||||
The command removes all the Kubernetes components associated with the chart and deletes the release.
|
||||
|
||||
## Configuration
|
||||
|
||||
| Parameter | Description | Default |
|
||||
| :--------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------- |
|
||||
| `image.repository` | The image repository to pull from | coredns/coredns |
|
||||
| `image.tag` | The image tag to pull from (derived from Chart.yaml) | `` |
|
||||
| `image.pullPolicy` | Image pull policy | IfNotPresent |
|
||||
| `image.pullSecrets` | Specify container image pull secrets | `[]` |
|
||||
| `replicaCount` | Number of replicas | 1 |
|
||||
| `resources.limits.cpu` | Container maximum CPU | `100m` |
|
||||
| `resources.limits.memory` | Container maximum memory | `128Mi` |
|
||||
| `resources.requests.cpu` | Container requested CPU | `100m` |
|
||||
| `resources.requests.memory` | Container requested memory | `128Mi` |
|
||||
| `serviceType` | Kubernetes Service type | `ClusterIP` |
|
||||
| `prometheus.service.enabled` | Set this to `true` to create Service for Prometheus metrics | `false` |
|
||||
| `prometheus.service.annotations` | Annotations to add to the metrics Service | `{prometheus.io/scrape: "true", prometheus.io/port: "9153"}` |
|
||||
| `prometheus.service.selector` | Pod selector | `{}` |
|
||||
| `prometheus.monitor.enabled` | Set this to `true` to create ServiceMonitor for Prometheus operator | `false` |
|
||||
| `prometheus.monitor.additionalLabels` | Additional labels that can be used so ServiceMonitor will be discovered by Prometheus | {} |
|
||||
| `prometheus.monitor.namespace` | Selector to select which namespaces the Endpoints objects are discovered from. | `""` |
|
||||
| `prometheus.monitor.interval` | Scrape interval for polling the metrics endpoint. (E.g. "30s") | `""` |
|
||||
| `prometheus.monitor.selector` | Service selector | `{}` |
|
||||
| `service.clusterIP` | IP address to assign to service | `""` |
|
||||
| `service.clusterIPs` | IP addresses to assign to service | `[]` |
|
||||
| `service.loadBalancerIP` | IP address to assign to load balancer (if supported) | `""` |
|
||||
| `service.externalIPs` | External IP addresses | [] |
|
||||
| `service.externalTrafficPolicy` | Enable client source IP preservation | [] |
|
||||
| `service.ipFamilyPolicy` | Service dual-stack policy | `""` |
|
||||
| `service.annotations` | Annotations to add to service | {} |
|
||||
| `service.selector` | Pod selector | `{}` |
|
||||
| `service.trafficDistribution` | Service traffic routing strategy | |
|
||||
| `serviceAccount.create` | If true, create & use serviceAccount | false |
|
||||
| `serviceAccount.name` | If not set & create is true, use template fullname | |
|
||||
| `rbac.create` | If true, create & use RBAC resources | true |
|
||||
| `rbac.pspEnable` | Specifies whether a PodSecurityPolicy should be created. | `false` |
|
||||
| `isClusterService` | Specifies whether chart should be deployed as cluster-service or normal k8s app. | true |
|
||||
| `priorityClassName` | Name of Priority Class to assign pods | `""` |
|
||||
| `securityContext` | securityContext definition for pods | capabilities.add.NET_BIND_SERVICE |
|
||||
| `servers` | Configuration for CoreDNS and plugins | See values.yml |
|
||||
| `livenessProbe.enabled` | Enable/disable the Liveness probe | `true` |
|
||||
| `livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | `60` |
|
||||
| `livenessProbe.periodSeconds` | How often to perform the probe | `10` |
|
||||
| `livenessProbe.timeoutSeconds` | When the probe times out | `5` |
|
||||
| `livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | `5` |
|
||||
| `livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed. | `1` |
|
||||
| `readinessProbe.enabled` | Enable/disable the Readiness probe | `true` |
|
||||
| `readinessProbe.initialDelaySeconds` | Delay before readiness probe is initiated | `30` |
|
||||
| `readinessProbe.periodSeconds` | How often to perform the probe | `10` |
|
||||
| `readinessProbe.timeoutSeconds` | When the probe times out | `5` |
|
||||
| `readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | `5` |
|
||||
| `readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed. | `1` |
|
||||
| `affinity` | Affinity settings for pod assignment | {} |
|
||||
| `nodeSelector` | Node labels for pod assignment | {} |
|
||||
| `tolerations` | Tolerations for pod assignment | [] |
|
||||
| `zoneFiles` | Configure custom Zone files | [] |
|
||||
| `extraContainers` | Optional array of sidecar containers | [] |
|
||||
| `extraVolumes` | Optional array of volumes to create | [] |
|
||||
| `extraVolumeMounts` | Optional array of volumes to mount inside the CoreDNS container | [] |
|
||||
| `extraSecrets` | Optional array of secrets to mount inside the CoreDNS container | [] |
|
||||
| `env` | Optional array of environment variables for CoreDNS container | [] |
|
||||
| `customLabels` | Optional labels for Deployment(s), Pod, Service, ServiceMonitor objects | {} |
|
||||
| `customAnnotations` | Optional annotations for Deployment(s), Pod, Service, ServiceMonitor objects |
|
||||
| `rollingUpdate.maxUnavailable` | Maximum number of unavailable replicas during rolling update | `1` |
|
||||
| `rollingUpdate.maxSurge` | Maximum number of pods created above desired number of pods | `25%` |
|
||||
| `podDisruptionBudget` | Optional PodDisruptionBudget | {} |
|
||||
| `podAnnotations` | Optional Pod only Annotations | {} |
|
||||
| `terminationGracePeriodSeconds` | Optional duration in seconds the pod needs to terminate gracefully. | 30 |
|
||||
| `hpa.enabled` | Enable Hpa autoscaler instead of proportional one | `false` |
|
||||
| `hpa.minReplicas` | Hpa minimum number of CoreDNS replicas | `1` |
|
||||
| `hpa.maxReplicas` | Hpa maximum number of CoreDNS replicas | `2` |
|
||||
| `hpa.metrics` | Metrics definitions used by Hpa to scale up and down | {} |
|
||||
| `autoscaler.enabled` | Optionally enabled a cluster-proportional-autoscaler for CoreDNS | `false` |
|
||||
| `autoscaler.coresPerReplica` | Number of cores in the cluster per CoreDNS replica | `256` |
|
||||
| `autoscaler.nodesPerReplica` | Number of nodes in the cluster per CoreDNS replica | `16` |
|
||||
| `autoscaler.min` | Min size of replicaCount | 0 |
|
||||
| `autoscaler.max` | Max size of replicaCount | 0 (aka no max) |
|
||||
| `autoscaler.includeUnschedulableNodes` | Should the replicas scale based on the total number or only schedulable nodes | `false` |
|
||||
| `autoscaler.preventSinglePointFailure` | If true does not allow single points of failure to form | `true` |
|
||||
| `autoscaler.customFlags` | A list of custom flags to pass into cluster-proportional-autoscaler | (no args) |
|
||||
| `autoscaler.image.repository` | The image repository to pull autoscaler from | registry.k8s.io/cpa/cluster-proportional-autoscaler |
|
||||
| `autoscaler.image.tag` | The image tag to pull autoscaler from | `1.8.5` |
|
||||
| `autoscaler.image.pullPolicy` | Image pull policy for the autoscaler | IfNotPresent |
|
||||
| `autoscaler.image.pullSecrets` | Specify container image pull secrets | `[]` |
|
||||
| `autoscaler.priorityClassName` | Optional priority class for the autoscaler pod. `priorityClassName` used if not set. | `""` |
|
||||
| `autoscaler.affinity` | Affinity settings for pod assignment for autoscaler | {} |
|
||||
| `autoscaler.nodeSelector` | Node labels for pod assignment for autoscaler | {} |
|
||||
| `autoscaler.tolerations` | Tolerations for pod assignment for autoscaler | [] |
|
||||
| `autoscaler.resources.limits.cpu` | Container maximum CPU for cluster-proportional-autoscaler | `20m` |
|
||||
| `autoscaler.resources.limits.memory` | Container maximum memory for cluster-proportional-autoscaler | `10Mi` |
|
||||
| `autoscaler.resources.requests.cpu` | Container requested CPU for cluster-proportional-autoscaler | `20m` |
|
||||
| `autoscaler.resources.requests.memory` | Container requested memory for cluster-proportional-autoscaler | `10Mi` |
|
||||
| `autoscaler.configmap.annotations` | Annotations to add to autoscaler config map. For example to stop CI renaming them | {} |
|
||||
| `autoscaler.livenessProbe.enabled` | Enable/disable the Liveness probe | `true` |
|
||||
| `autoscaler.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | `10` |
|
||||
| `autoscaler.livenessProbe.periodSeconds` | How often to perform the probe | `5` |
|
||||
| `autoscaler.livenessProbe.timeoutSeconds` | When the probe times out | `5` |
|
||||
| `autoscaler.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | `3` |
|
||||
| `autoscaler.livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed. | `1` |
|
||||
| `autoscaler.extraContainers` | Optional array of sidecar containers | [] |
|
||||
| `deployment.enabled` | Optionally disable the main deployment and its respective resources. | `true` |
|
||||
| `deployment.name` | Name of the deployment if `deployment.enabled` is true. Otherwise the name of an existing deployment for the autoscaler or HPA to target. | `""` |
|
||||
| `deployment.annotations` | Annotations to add to the main deployment | `{}` |
|
||||
| `deployment.selector` | Pod selector | `{}` |
|
||||
| `clusterRole.nameOverride` | ClusterRole name override | |
|
||||
|
||||
See `values.yaml` for configuration notes. Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
|
||||
|
||||
```console
|
||||
$ helm install coredns \
|
||||
coredns/coredns \
|
||||
--set rbac.create=false
|
||||
```
|
||||
|
||||
The above command disables automatic creation of RBAC rules.
|
||||
|
||||
Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example,
|
||||
|
||||
```console
|
||||
$ helm install coredns coredns/coredns -f values.yaml
|
||||
```
|
||||
|
||||
> **Tip**: You can use the default [values.yaml](/charts/coredns/values.yaml)
|
||||
|
||||
## Caveats
|
||||
|
||||
The chart will automatically determine which protocols to listen on based on
|
||||
the protocols you define in your zones. This means that you could potentially
|
||||
use both "TCP" and "UDP" on a single port.
|
||||
Some cloud environments like "GCE" or "Azure container service" cannot
|
||||
create external loadbalancers with both "TCP" and "UDP" protocols. So
|
||||
When deploying CoreDNS with `serviceType="LoadBalancer"` on such cloud
|
||||
environments, make sure you do not attempt to use both protocols at the same
|
||||
time.
|
||||
|
||||
## Autoscaling
|
||||
|
||||
By setting `autoscaler.enabled = true` a
|
||||
[cluster-proportional-autoscaler](https://github.com/kubernetes-incubator/cluster-proportional-autoscaler)
|
||||
will be deployed. This will default to a coredns replica for every 256 cores, or
|
||||
16 nodes in the cluster. These can be changed with `autoscaler.coresPerReplica`
|
||||
and `autoscaler.nodesPerReplica`. When cluster is using large nodes (with more
|
||||
cores), `coresPerReplica` should dominate. If using small nodes,
|
||||
`nodesPerReplica` should dominate.
|
||||
|
||||
This also creates a ServiceAccount, ClusterRole, and ClusterRoleBinding for
|
||||
the autoscaler deployment.
|
||||
|
||||
`replicaCount` is ignored if this is enabled.
|
||||
|
||||
By setting `hpa.enabled = true` a [Horizontal Pod Autoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/)
|
||||
is enabled for Coredns deployment. This can scale number of replicas based on meitrics
|
||||
like CpuUtilization, MemoryUtilization or Custom ones.
|
||||
|
||||
## Adopting existing CoreDNS resources
|
||||
|
||||
If you do not want to delete the existing CoreDNS resources in your cluster, you can adopt the resources into a release as of Helm 3.2.0.
|
||||
|
||||
You will also need to annotate and label your existing resources to allow Helm to assume control of them. See: https://github.com/helm/helm/pull/7649
|
||||
|
||||
```
|
||||
annotations:
|
||||
meta.helm.sh/release-name: your-release-name
|
||||
meta.helm.sh/release-namespace: your-release-namespace
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
```
|
||||
|
||||
Once you have annotated and labeled all the resources this chart specifies, you may need to locally template the chart and compare against existing manifest to ensure there are no changes/diffs.s If
|
||||
you have been careful this should not diff and leave all the resources unmodified and now under management of helm.
|
||||
|
||||
Some values to investigate to help adopt your existing manifests to the Helm release are:
|
||||
|
||||
- k8sAppLabelOverride
|
||||
- service.name
|
||||
- customLabels
|
||||
|
||||
In some cases, you will need to orphan delete your existing deployment since selector labels are immutable.
|
||||
|
||||
```
|
||||
kubectl delete deployment coredns --cascade=orphan
|
||||
```
|
||||
|
||||
This will delete the deployment and leave the replicaset to ensure no downtime in the cluster. You will need to manually delete the replicaset AFTER Helm has released a new deployment.
|
||||
|
||||
Here is an example script to modify the annotations and labels of existing resources:
|
||||
|
||||
WARNING: Substitute YOUR_HELM_RELEASE_NAME_HERE with the name of your helm release.
|
||||
|
||||
```
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
for kind in config service serviceAccount; do
|
||||
echo "setting annotations and labels on $kind/coredns"
|
||||
kubectl -n kube-system annotate --overwrite $kind coredns meta.helm.sh/release-name=YOUR_HELM_RELEASE_NAME_HERE
|
||||
kubectl -n kube-system annotate --overwrite $kind coredns meta.helm.sh/release-namespace=kube-system
|
||||
kubectl -n kube-system label --overwrite $kind coredns app.kubernetes.io/managed-by=Helm
|
||||
done
|
||||
```
|
||||
|
||||
NOTE: Sometimes, previous deployments of kube-dns that have been migrated to CoreDNS still use kube-dns for the service name as well.
|
||||
|
||||
```
|
||||
echo "setting annotations and labels on service/kube-dns"
|
||||
kubectl -n kube-system annotate --overwrite service kube-dns meta.helm.sh/release-name=YOUR_HELM_RELEASE_NAME_HERE
|
||||
kubectl -n kube-system annotate --overwrite service kube-dns meta.helm.sh/release-namespace=kube-system
|
||||
kubectl -n kube-system label --overwrite service kube-dns app.kubernetes.io/managed-by=Helm
|
||||
```
|
||||
30
packages/system/coredns/charts/coredns/templates/NOTES.txt
Normal file
30
packages/system/coredns/charts/coredns/templates/NOTES.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
{{- if .Values.isClusterService }}
|
||||
CoreDNS is now running in the cluster as a cluster-service.
|
||||
{{- else }}
|
||||
CoreDNS is now running in the cluster.
|
||||
It can be accessed using the below endpoint
|
||||
{{- if contains "NodePort" .Values.serviceType }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "coredns.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo "$NODE_IP:$NODE_PORT"
|
||||
{{- else if contains "LoadBalancer" .Values.serviceType }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status by running 'kubectl get svc -w {{ template "coredns.fullname" . }}'
|
||||
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "coredns.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
|
||||
echo $SERVICE_IP
|
||||
{{- else if contains "ClusterIP" .Values.serviceType }}
|
||||
"{{ template "coredns.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local"
|
||||
from within the cluster
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
It can be tested with the following:
|
||||
|
||||
1. Launch a Pod with DNS tools:
|
||||
|
||||
kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstools
|
||||
|
||||
2. Query the DNS server:
|
||||
|
||||
/ # host kubernetes
|
||||
236
packages/system/coredns/charts/coredns/templates/_helpers.tpl
Normal file
236
packages/system/coredns/charts/coredns/templates/_helpers.tpl
Normal file
@@ -0,0 +1,236 @@
|
||||
{{/* vim: set filetype=mustache: */}}
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "coredns.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
*/}}
|
||||
{{- define "coredns.fullname" -}}
|
||||
{{- if .Values.fullnameOverride -}}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- else -}}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride -}}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "coredns.labels" -}}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name | quote }}
|
||||
helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"
|
||||
{{- if .Values.isClusterService }}
|
||||
k8s-app: {{ template "coredns.k8sapplabel" . }}
|
||||
kubernetes.io/cluster-service: "true"
|
||||
kubernetes.io/name: "CoreDNS"
|
||||
{{- end }}
|
||||
app.kubernetes.io/name: {{ template "coredns.name" . }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Common labels with autoscaler
|
||||
*/}}
|
||||
{{- define "coredns.labels.autoscaler" -}}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name | quote }}
|
||||
helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"
|
||||
{{- if .Values.isClusterService }}
|
||||
k8s-app: {{ template "coredns.k8sapplabel" . }}-autoscaler
|
||||
kubernetes.io/cluster-service: "true"
|
||||
kubernetes.io/name: "CoreDNS"
|
||||
{{- end }}
|
||||
app.kubernetes.io/name: {{ template "coredns.name" . }}-autoscaler
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Allow k8s-app label to be overridden
|
||||
*/}}
|
||||
{{- define "coredns.k8sapplabel" -}}
|
||||
{{- default .Chart.Name .Values.k8sAppLabelOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Generate the list of ports automatically from the server definitions
|
||||
*/}}
|
||||
{{- define "coredns.servicePorts" -}}
|
||||
{{/* Set ports to be an empty dict */}}
|
||||
{{- $ports := dict -}}
|
||||
{{/* Iterate through each of the server blocks */}}
|
||||
{{- range .Values.servers -}}
|
||||
{{/* Capture port to avoid scoping awkwardness */}}
|
||||
{{- $port := toString .port -}}
|
||||
{{- $serviceport := default .port .servicePort -}}
|
||||
|
||||
{{/* If none of the server blocks has mentioned this port yet take note of it */}}
|
||||
{{- if not (hasKey $ports $port) -}}
|
||||
{{- $ports := set $ports $port (dict "istcp" false "isudp" false "serviceport" $serviceport) -}}
|
||||
{{- end -}}
|
||||
{{/* Retrieve the inner dict that holds the protocols for a given port */}}
|
||||
{{- $innerdict := index $ports $port -}}
|
||||
|
||||
{{/*
|
||||
Look at each of the zones and check which protocol they serve
|
||||
At the moment the following are supported by CoreDNS:
|
||||
UDP: dns://
|
||||
TCP: tls://, grpc://, https://
|
||||
*/}}
|
||||
{{- range .zones -}}
|
||||
{{- if has (default "" .scheme) (list "dns://" "") -}}
|
||||
{{/* Optionally enable tcp for this service as well */}}
|
||||
{{- if eq (default false .use_tcp) true }}
|
||||
{{- $innerdict := set $innerdict "istcp" true -}}
|
||||
{{- end }}
|
||||
{{- $innerdict := set $innerdict "isudp" true -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if has (default "" .scheme) (list "tls://" "grpc://" "https://") -}}
|
||||
{{- $innerdict := set $innerdict "istcp" true -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* If none of the zones specify scheme, default to dns:// udp */}}
|
||||
{{- if and (not (index $innerdict "istcp")) (not (index $innerdict "isudp")) -}}
|
||||
{{- $innerdict := set $innerdict "isudp" true -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if .nodePort -}}
|
||||
{{- $innerdict := set $innerdict "nodePort" .nodePort -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Write the dict back into the outer dict */}}
|
||||
{{- $ports := set $ports $port $innerdict -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Write out the ports according to the info collected above */}}
|
||||
{{- range $port, $innerdict := $ports -}}
|
||||
{{- $portList := list -}}
|
||||
{{- if index $innerdict "isudp" -}}
|
||||
{{- $portList = append $portList (dict "port" (get $innerdict "serviceport") "protocol" "UDP" "name" (printf "udp-%s" $port) "targetPort" ($port | int)) -}}
|
||||
{{- end -}}
|
||||
{{- if index $innerdict "istcp" -}}
|
||||
{{- $portList = append $portList (dict "port" (get $innerdict "serviceport") "protocol" "TCP" "name" (printf "tcp-%s" $port) "targetPort" ($port | int)) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- range $portDict := $portList -}}
|
||||
{{- if index $innerdict "nodePort" -}}
|
||||
{{- $portDict := set $portDict "nodePort" (get $innerdict "nodePort" | int) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- printf "- %s\n" (toJson $portDict) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Generate the list of ports automatically from the server definitions
|
||||
*/}}
|
||||
{{- define "coredns.containerPorts" -}}
|
||||
{{/* Set ports to be an empty dict */}}
|
||||
{{- $ports := dict -}}
|
||||
{{/* Iterate through each of the server blocks */}}
|
||||
{{- range .Values.servers -}}
|
||||
{{/* Capture port to avoid scoping awkwardness */}}
|
||||
{{- $port := toString .port -}}
|
||||
|
||||
{{/* If none of the server blocks has mentioned this port yet take note of it */}}
|
||||
{{- if not (hasKey $ports $port) -}}
|
||||
{{- $ports := set $ports $port (dict "istcp" false "isudp" false) -}}
|
||||
{{- end -}}
|
||||
{{/* Retrieve the inner dict that holds the protocols for a given port */}}
|
||||
{{- $innerdict := index $ports $port -}}
|
||||
|
||||
{{/*
|
||||
Look at each of the zones and check which protocol they serve
|
||||
At the moment the following are supported by CoreDNS:
|
||||
UDP: dns://
|
||||
TCP: tls://, grpc://, https://
|
||||
*/}}
|
||||
{{- range .zones -}}
|
||||
{{- if has (default "" .scheme) (list "dns://" "") -}}
|
||||
{{/* Optionally enable tcp for this service as well */}}
|
||||
{{- if eq (default false .use_tcp) true }}
|
||||
{{- $innerdict := set $innerdict "istcp" true -}}
|
||||
{{- end }}
|
||||
{{- $innerdict := set $innerdict "isudp" true -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if has (default "" .scheme) (list "tls://" "grpc://" "https://") -}}
|
||||
{{- $innerdict := set $innerdict "istcp" true -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* If none of the zones specify scheme, default to dns:// udp */}}
|
||||
{{- if and (not (index $innerdict "istcp")) (not (index $innerdict "isudp")) -}}
|
||||
{{- $innerdict := set $innerdict "isudp" true -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- if .hostPort -}}
|
||||
{{- $innerdict := set $innerdict "hostPort" .hostPort -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Write the dict back into the outer dict */}}
|
||||
{{- $ports := set $ports $port $innerdict -}}
|
||||
|
||||
{{/* Fetch port from the configuration if the prometheus section exists */}}
|
||||
{{- range .plugins -}}
|
||||
{{- if eq .name "prometheus" -}}
|
||||
{{- $prometheus_addr := toString .parameters -}}
|
||||
{{- $prometheus_addr_list := regexSplit ":" $prometheus_addr -1 -}}
|
||||
{{- $prometheus_port := index $prometheus_addr_list 1 -}}
|
||||
{{- $ports := set $ports $prometheus_port (dict "istcp" true "isudp" false) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Write out the ports according to the info collected above */}}
|
||||
{{- range $port, $innerdict := $ports -}}
|
||||
{{- $portList := list -}}
|
||||
{{- if index $innerdict "isudp" -}}
|
||||
{{- $portList = append $portList (dict "containerPort" ($port | int) "protocol" "UDP" "name" (printf "udp-%s" $port)) -}}
|
||||
{{- end -}}
|
||||
{{- if index $innerdict "istcp" -}}
|
||||
{{- $portList = append $portList (dict "containerPort" ($port | int) "protocol" "TCP" "name" (printf "tcp-%s" $port)) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- range $portDict := $portList -}}
|
||||
{{- if index $innerdict "hostPort" -}}
|
||||
{{- $portDict := set $portDict "hostPort" (get $innerdict "hostPort" | int) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- printf "- %s\n" (toJson $portDict) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "coredns.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create -}}
|
||||
{{ default (include "coredns.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else -}}
|
||||
{{ default "default" .Values.serviceAccount.name }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "coredns.clusterRoleName" -}}
|
||||
{{- if and .Values.clusterRole .Values.clusterRole.nameOverride -}}
|
||||
{{ .Values.clusterRole.nameOverride }}
|
||||
{{- else -}}
|
||||
{{ template "coredns.fullname" . }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,30 @@
|
||||
{{- if and .Values.autoscaler.enabled .Values.rbac.create }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: {{ template "coredns.fullname" . }}-autoscaler
|
||||
labels: {{- include "coredns.labels.autoscaler" . | nindent 4 }}
|
||||
{{- if .Values.customLabels }}
|
||||
{{ toYaml .Values.customLabels | indent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["list","watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["replicationcontrollers/scale"]
|
||||
verbs: ["get", "update"]
|
||||
- apiGroups: ["extensions", "apps"]
|
||||
resources: ["deployments/scale", "replicasets/scale"]
|
||||
verbs: ["get", "update"]
|
||||
# Remove the configmaps rule once below issue is fixed:
|
||||
# kubernetes-incubator/cluster-proportional-autoscaler#16
|
||||
- apiGroups: [""]
|
||||
resources: ["configmaps"]
|
||||
verbs: ["get", "create"]
|
||||
{{- end }}
|
||||
@@ -0,0 +1,36 @@
|
||||
{{- if and .Values.deployment.enabled .Values.rbac.create }}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: {{ template "coredns.clusterRoleName" . }}
|
||||
labels: {{- include "coredns.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- endpoints
|
||||
- services
|
||||
- pods
|
||||
- namespaces
|
||||
verbs:
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- discovery.k8s.io
|
||||
resources:
|
||||
- endpointslices
|
||||
verbs:
|
||||
- list
|
||||
- watch
|
||||
{{- if .Values.rbac.pspEnable }}
|
||||
- apiGroups:
|
||||
- policy
|
||||
- extensions
|
||||
resources:
|
||||
- podsecuritypolicies
|
||||
verbs:
|
||||
- use
|
||||
resourceNames:
|
||||
- {{ template "coredns.fullname" . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,23 @@
|
||||
{{- if and .Values.autoscaler.enabled .Values.rbac.create }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ template "coredns.fullname" . }}-autoscaler
|
||||
labels: {{- include "coredns.labels.autoscaler" . | nindent 4 }}
|
||||
{{- if .Values.customLabels }}
|
||||
{{ toYaml .Values.customLabels | indent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ template "coredns.fullname" . }}-autoscaler
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ template "coredns.fullname" . }}-autoscaler
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,15 @@
|
||||
{{- if and .Values.deployment.enabled .Values.rbac.create }}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ template "coredns.clusterRoleName" . }}
|
||||
labels: {{- include "coredns.labels" . | nindent 4 }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ template "coredns.clusterRoleName" . }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ template "coredns.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,33 @@
|
||||
{{- if .Values.autoscaler.enabled }}
|
||||
---
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: {{ template "coredns.fullname" . }}-autoscaler
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels: {{- include "coredns.labels.autoscaler" . | nindent 4 }}
|
||||
{{- if .Values.customLabels }}
|
||||
{{- toYaml .Values.customLabels | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if or .Values.autoscaler.configmap.annotations .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- if .Values.customAnnotations }}
|
||||
{{- toYaml .Values.customAnnotations | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaler.configmap.annotations -}}
|
||||
{{ toYaml .Values.autoscaler.configmap.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
data:
|
||||
# When cluster is using large nodes(with more cores), "coresPerReplica" should dominate.
|
||||
# If using small nodes, "nodesPerReplica" should dominate.
|
||||
linear: |-
|
||||
{
|
||||
"coresPerReplica": {{ .Values.autoscaler.coresPerReplica | float64 }},
|
||||
"nodesPerReplica": {{ .Values.autoscaler.nodesPerReplica | float64 }},
|
||||
"preventSinglePointFailure": {{ .Values.autoscaler.preventSinglePointFailure }},
|
||||
"min": {{ .Values.autoscaler.min | int }},
|
||||
"max": {{ .Values.autoscaler.max | int }},
|
||||
"includeUnschedulableNodes": {{ .Values.autoscaler.includeUnschedulableNodes }}
|
||||
}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,37 @@
|
||||
{{- if .Values.deployment.enabled }}
|
||||
{{- if not .Values.deployment.skipConfig }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ template "coredns.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels: {{- include "coredns.labels" . | nindent 4 }}
|
||||
{{- if .Values.customLabels }}
|
||||
{{ toYaml .Values.customLabels | indent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
data:
|
||||
Corefile: |-
|
||||
{{- range $name, $conf := .Values.extraConfig }}
|
||||
{{ $name }}{{ if $conf.parameters }} {{ $conf.parameters }}{{ end }}
|
||||
{{- end }}
|
||||
{{ range .Values.servers }}
|
||||
{{- range $idx, $zone := .zones }}{{ if $idx }} {{ else }}{{ end }}{{ default "" $zone.scheme }}{{ default "." $zone.zone }}{{ else }}.{{ end -}}
|
||||
{{- if .port }}:{{ .port }} {{ end -}}
|
||||
{
|
||||
{{- range .plugins }}
|
||||
{{ .name }}{{ if .parameters }} {{ .parameters }}{{ end }}{{ if .configBlock }} {
|
||||
{{ .configBlock | indent 12 }}
|
||||
}{{ end }}
|
||||
{{- end }}
|
||||
}
|
||||
{{ end }}
|
||||
{{- range .Values.zoneFiles }}
|
||||
{{ .filename }}: {{ toYaml .contents | indent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,98 @@
|
||||
{{- if and (.Values.autoscaler.enabled) (not .Values.hpa.enabled) }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ template "coredns.fullname" . }}-autoscaler
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels: {{- include "coredns.labels.autoscaler" . | nindent 4 }}
|
||||
{{- if .Values.customLabels }}
|
||||
{{ toYaml .Values.customLabels | indent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: {{ .Release.Name | quote }}
|
||||
{{- if .Values.isClusterService }}
|
||||
k8s-app: {{ template "coredns.k8sapplabel" . }}-autoscaler
|
||||
{{- end }}
|
||||
app.kubernetes.io/name: {{ template "coredns.name" . }}-autoscaler
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- if .Values.isClusterService }}
|
||||
{{- if not (hasKey .Values.customLabels "k8s-app")}}
|
||||
k8s-app: {{ template "coredns.k8sapplabel" . }}-autoscaler
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/name: {{ template "coredns.name" . }}-autoscaler
|
||||
app.kubernetes.io/instance: {{ .Release.Name | quote }}
|
||||
{{- if .Values.customLabels }}
|
||||
{{ toYaml .Values.customLabels | nindent 8 }}
|
||||
{{- end }}
|
||||
annotations:
|
||||
checksum/configmap: {{ include (print $.Template.BasePath "/configmap-autoscaler.yaml") . | sha256sum }}
|
||||
{{- if .Values.isClusterService }}
|
||||
scheduler.alpha.kubernetes.io/tolerations: '[{"key":"CriticalAddonsOnly", "operator":"Exists"}]'
|
||||
{{- end }}
|
||||
{{- with .Values.autoscaler.podAnnotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
serviceAccountName: {{ template "coredns.fullname" . }}-autoscaler
|
||||
{{- $priorityClassName := default .Values.priorityClassName .Values.autoscaler.priorityClassName }}
|
||||
{{- if $priorityClassName }}
|
||||
priorityClassName: {{ $priorityClassName | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaler.affinity }}
|
||||
affinity:
|
||||
{{ toYaml .Values.autoscaler.affinity | indent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaler.tolerations }}
|
||||
tolerations:
|
||||
{{ toYaml .Values.autoscaler.tolerations | indent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaler.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{ toYaml .Values.autoscaler.nodeSelector | indent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaler.image.pullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{ toYaml .Values.autoscaler.image.pullSecrets | indent 8 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: autoscaler
|
||||
image: "{{ .Values.autoscaler.image.repository }}:{{ .Values.autoscaler.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.autoscaler.image.pullPolicy }}
|
||||
resources:
|
||||
{{ toYaml .Values.autoscaler.resources | indent 10 }}
|
||||
{{- if .Values.autoscaler.livenessProbe.enabled }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: {{ .Values.autoscaler.livenessProbe.initialDelaySeconds }}
|
||||
periodSeconds: {{ .Values.autoscaler.livenessProbe.periodSeconds }}
|
||||
timeoutSeconds: {{ .Values.autoscaler.livenessProbe.timeoutSeconds }}
|
||||
successThreshold: {{ .Values.autoscaler.livenessProbe.successThreshold }}
|
||||
failureThreshold: {{ .Values.autoscaler.livenessProbe.failureThreshold }}
|
||||
{{- end }}
|
||||
command:
|
||||
- /cluster-proportional-autoscaler
|
||||
- --namespace={{ .Release.Namespace }}
|
||||
- --configmap={{ template "coredns.fullname" . }}-autoscaler
|
||||
- --target=Deployment/{{ default (include "coredns.fullname" .) .Values.deployment.name }}
|
||||
- --logtostderr=true
|
||||
- --v=2
|
||||
{{- if .Values.autoscaler.customFlags }}
|
||||
{{ toYaml .Values.autoscaler.customFlags | indent 10 }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaler.extraContainers }}
|
||||
{{ toYaml .Values.autoscaler.extraContainers | indent 6 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
174
packages/system/coredns/charts/coredns/templates/deployment.yaml
Normal file
174
packages/system/coredns/charts/coredns/templates/deployment.yaml
Normal file
@@ -0,0 +1,174 @@
|
||||
{{- if .Values.deployment.enabled }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ default (include "coredns.fullname" .) .Values.deployment.name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels: {{- include "coredns.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/version: {{ .Values.image.tag | default .Chart.AppVersion | replace ":" "-" | replace "@" "_" | trunc 63 | trimSuffix "-" | quote }}
|
||||
{{- if .Values.customLabels }}
|
||||
{{ toYaml .Values.customLabels | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if or .Values.deployment.annotations .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- if .Values.customAnnotations }}
|
||||
{{- toYaml .Values.customAnnotations | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.annotations }}
|
||||
{{- toYaml .Values.deployment.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if and (not .Values.autoscaler.enabled) (not .Values.hpa.enabled) }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
{{- end }}
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: {{ .Values.rollingUpdate.maxUnavailable }}
|
||||
maxSurge: {{ .Values.rollingUpdate.maxSurge }}
|
||||
selector:
|
||||
{{- if .Values.deployment.selector }}
|
||||
{{- toYaml .Values.deployment.selector | nindent 4 }}
|
||||
{{- else }}
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: {{ .Release.Name | quote }}
|
||||
{{- if .Values.isClusterService }}
|
||||
k8s-app: {{ template "coredns.k8sapplabel" . }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/name: {{ template "coredns.name" . }}
|
||||
{{- end }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- if .Values.isClusterService }}
|
||||
k8s-app: {{ template "coredns.k8sapplabel" . }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/name: {{ template "coredns.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name | quote }}
|
||||
{{- if .Values.customLabels }}
|
||||
{{ toYaml .Values.customLabels | indent 8 }}
|
||||
{{- end }}
|
||||
annotations:
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
|
||||
{{- if .Values.isClusterService }}
|
||||
scheduler.alpha.kubernetes.io/tolerations: '[{"key":"CriticalAddonsOnly", "operator":"Exists"}]'
|
||||
{{- end }}
|
||||
{{- if .Values.podAnnotations }}
|
||||
{{ toYaml .Values.podAnnotations | indent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.podSecurityContext }}
|
||||
securityContext: {{ toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.terminationGracePeriodSeconds }}
|
||||
terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ template "coredns.serviceAccountName" . }}
|
||||
{{- if .Values.priorityClassName }}
|
||||
priorityClassName: {{ .Values.priorityClassName | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.isClusterService }}
|
||||
dnsPolicy: Default
|
||||
{{- end }}
|
||||
{{- if .Values.affinity }}
|
||||
affinity:
|
||||
{{ toYaml .Values.affinity | indent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.topologySpreadConstraints }}
|
||||
topologySpreadConstraints:
|
||||
{{ tpl (toYaml .Values.topologySpreadConstraints) $ | indent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.tolerations }}
|
||||
tolerations:
|
||||
{{ toYaml .Values.tolerations | indent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{ toYaml .Values.nodeSelector | indent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.image.pullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{ toYaml .Values.image.pullSecrets | indent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.initContainers }}
|
||||
initContainers:
|
||||
{{ toYaml .Values.initContainers | indent 8 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: "coredns"
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
args: [ "-conf", "/etc/coredns/Corefile" ]
|
||||
volumeMounts:
|
||||
- name: config-volume
|
||||
mountPath: /etc/coredns
|
||||
{{- range .Values.extraSecrets }}
|
||||
- name: {{ .name }}
|
||||
mountPath: {{ .mountPath }}
|
||||
readOnly: true
|
||||
{{- end }}
|
||||
{{- if .Values.extraVolumeMounts }}
|
||||
{{- toYaml .Values.extraVolumeMounts | nindent 8}}
|
||||
{{- end }}
|
||||
{{- if .Values.env }}
|
||||
env:
|
||||
{{- toYaml .Values.env | nindent 10}}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{ toYaml .Values.resources | indent 10 }}
|
||||
ports:
|
||||
{{ include "coredns.containerPorts" . | indent 8 }}
|
||||
{{- if .Values.livenessProbe.enabled }}
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
|
||||
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
|
||||
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
|
||||
successThreshold: {{ .Values.livenessProbe.successThreshold }}
|
||||
failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
|
||||
{{- end }}
|
||||
{{- if .Values.readinessProbe.enabled }}
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 8181
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
|
||||
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
|
||||
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
|
||||
successThreshold: {{ .Values.readinessProbe.successThreshold }}
|
||||
failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
|
||||
{{- end }}
|
||||
{{- if .Values.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- if .Values.extraContainers }}
|
||||
{{ toYaml .Values.extraContainers | indent 6 }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
- name: config-volume
|
||||
configMap:
|
||||
name: {{ template "coredns.fullname" . }}
|
||||
items:
|
||||
- key: Corefile
|
||||
path: Corefile
|
||||
{{ range .Values.zoneFiles }}
|
||||
- key: {{ .filename }}
|
||||
path: {{ .filename }}
|
||||
{{ end }}
|
||||
{{- range .Values.extraSecrets }}
|
||||
- name: {{ .name }}
|
||||
secret:
|
||||
secretName: {{ .name }}
|
||||
defaultMode: {{ default 400 .defaultMode }}
|
||||
{{- end }}
|
||||
{{- if .Values.extraVolumes }}
|
||||
{{ toYaml .Values.extraVolumes | indent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
33
packages/system/coredns/charts/coredns/templates/hpa.yaml
Normal file
33
packages/system/coredns/charts/coredns/templates/hpa.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
{{- if and (.Values.hpa.enabled) (not .Values.autoscaler.enabled) }}
|
||||
---
|
||||
{{- if .Capabilities.APIVersions.Has "autoscaling/v2" }}
|
||||
apiVersion: autoscaling/v2
|
||||
{{- else }}
|
||||
apiVersion: autoscaling/v2beta2
|
||||
{{- end }}
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ template "coredns.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels: {{- include "coredns.labels" . | nindent 4 }}
|
||||
{{- if .Values.customLabels }}
|
||||
{{ toYaml .Values.customLabels | indent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ default (include "coredns.fullname" .) .Values.deployment.name }}
|
||||
minReplicas: {{ .Values.hpa.minReplicas }}
|
||||
maxReplicas: {{ .Values.hpa.maxReplicas }}
|
||||
metrics:
|
||||
{{ toYaml .Values.hpa.metrics | indent 4 }}
|
||||
{{- if .Values.hpa.behavior }}
|
||||
behavior:
|
||||
{{ toYaml .Values.hpa.behavior | indent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,26 @@
|
||||
{{- if and .Values.deployment.enabled .Values.podDisruptionBudget -}}
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: {{ template "coredns.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels: {{- include "coredns.labels" . | nindent 4 }}
|
||||
{{- if .Values.customLabels }}
|
||||
{{ toYaml .Values.customLabels | indent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if not .Values.podDisruptionBudget.selector }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: {{ .Release.Name | quote }}
|
||||
{{- if .Values.isClusterService }}
|
||||
k8s-app: {{ template "coredns.k8sapplabel" . }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/name: {{ template "coredns.name" . }}
|
||||
{{- end }}
|
||||
{{ toYaml .Values.podDisruptionBudget | indent 2 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,39 @@
|
||||
{{- if and .Values.deployment.enabled .Values.prometheus.service.enabled }}
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ template "coredns.fullname" . }}-metrics
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels: {{- include "coredns.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: metrics
|
||||
{{- if .Values.customLabels }}
|
||||
{{ toYaml .Values.customLabels | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if or .Values.prometheus.service.annotations .Values.service.annotations .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- if .Values.prometheus.service.annotations }}
|
||||
{{- toYaml .Values.prometheus.service.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.service.annotations }}
|
||||
{{- toYaml .Values.service.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.customAnnotations }}
|
||||
{{- toYaml .Values.customAnnotations | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
spec:
|
||||
selector:
|
||||
{{- if .Values.prometheus.service.selector }}
|
||||
{{- toYaml .Values.prometheus.service.selector | nindent 4 }}
|
||||
{{- else }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name | quote }}
|
||||
{{- if .Values.isClusterService }}
|
||||
k8s-app: {{ template "coredns.k8sapplabel" . }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/name: {{ template "coredns.name" . }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: metrics
|
||||
port: 9153
|
||||
targetPort: 9153
|
||||
{{- end }}
|
||||
@@ -0,0 +1,61 @@
|
||||
{{- if .Values.deployment.enabled }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ default (include "coredns.fullname" .) .Values.service.name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels: {{- include "coredns.labels" . | nindent 4 }}
|
||||
{{- if .Values.customLabels }}
|
||||
{{ toYaml .Values.customLabels | indent 4 }}
|
||||
{{- end }}
|
||||
{{- if or .Values.service.annotations .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- if .Values.service.annotations }}
|
||||
{{- toYaml .Values.service.annotations | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.customAnnotations }}
|
||||
{{- toYaml .Values.customAnnotations | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
spec:
|
||||
selector:
|
||||
{{- if .Values.service.selector }}
|
||||
{{- toYaml .Values.service.selector | nindent 4 }}
|
||||
{{- else }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name | quote }}
|
||||
{{- if .Values.isClusterService }}
|
||||
k8s-app: {{ template "coredns.k8sapplabel" . }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/name: {{ template "coredns.name" . }}
|
||||
{{- end }}
|
||||
{{- if .Values.service.clusterIP }}
|
||||
clusterIP: {{ .Values.service.clusterIP }}
|
||||
{{- end }}
|
||||
{{- if .Values.service.clusterIPs }}
|
||||
clusterIPs:
|
||||
{{ toYaml .Values.service.clusterIPs | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.service.externalIPs }}
|
||||
externalIPs:
|
||||
{{- toYaml .Values.service.externalIPs | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.service.externalTrafficPolicy }}
|
||||
externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }}
|
||||
{{- end }}
|
||||
{{- if .Values.service.loadBalancerIP }}
|
||||
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
|
||||
{{- end }}
|
||||
{{- if .Values.service.loadBalancerClass }}
|
||||
loadBalancerClass: {{ .Values.service.loadBalancerClass }}
|
||||
{{- end }}
|
||||
ports:
|
||||
{{ include "coredns.servicePorts" . | indent 2 -}}
|
||||
type: {{ default "ClusterIP" .Values.serviceType }}
|
||||
{{- if .Values.service.ipFamilyPolicy }}
|
||||
ipFamilyPolicy: {{ .Values.service.ipFamilyPolicy }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.service.trafficDistribution }}
|
||||
trafficDistribution: {{ .Values.service.trafficDistribution }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,20 @@
|
||||
{{- if and .Values.autoscaler.enabled .Values.rbac.create }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ template "coredns.fullname" . }}-autoscaler
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels: {{- include "coredns.labels.autoscaler" . | nindent 4 }}
|
||||
{{- if .Values.customLabels }}
|
||||
{{ toYaml .Values.customLabels | indent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaler.image.pullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{ toYaml .Values.autoscaler.image.pullSecrets | indent 2 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user