Stage 1: TerdutServer full lifecycle (Deployment, Service, both database
CI / test (push) Successful in 1m46s
CI / test (push) Successful in 1m46s
paths, self-registration bootstrap)
Replaces the bring-your-own-only Stage 1 (commit 1be7cf2) wholesale, per
the redesign in the previous two commits: the operator creates every
server it manages, so self-registration (DESIGN.md §6) is the only
bootstrap path, and Deployment/Service/database management builds
together with it (ROADMAP.md Stage 1) rather than behind a separate
later stage.
Grounded in terdut-server's actual chart (charts/terdut-server/templates/
deployment.yaml, values.yaml), not reconstructed from DESIGN.md's
illustrative YAML alone -- env var names, the password-via-PGPASSWORD
convention, the Recreate deployment strategy, /healthz probes, and the
TERDUT_OPERATOR_MODE=true decision (always on here, unlike the chart's
default-off: every write this operator's own future controllers make
goes through a service account already) all match that source exactly.
- api/v1alpha1: full TerdutServerSpec (image, replicas, networking,
database, sweeper, deadman, notify, oidc, passwordLogin, allowedTeams).
spec.database is a oneOf (dsn xor postgresClusterRef) via CEL
XValidation. No spec.credentialsSecretRef -- removed entirely in the
prior redesign commit, not carried forward.
- internal/controller:
- terdutserver_deployment.go: Deployment + Service via CreateOrUpdate,
owned (OwnerReference), env built field-for-field against the chart.
- terdutserver_database.go: both §8 paths. The Zalando path resolves
the postgresql.acid.zalan.do CR by convention (database/role both
"terdut", matching every DESIGN.md example) and only ever confirms
its generated credentials Secret exists -- never reads the value,
same "wire a secretKeyRef, don't read it" posture the DSN path takes.
classifyClusterGetError is its own function specifically so the
CRD-not-installed case (meta.IsNoMatchError) is unit-testable without
a real client.
- terdutserver_bootstrap.go: self-registration, checkpointed against
both real crash windows (DESIGN.md §6 point 1) -- an admin-key
checkpoint Secret, and adopt-via-GET+mint-new-key on a 409 from
creating the service account. BootstrapStateLost is its own error
type so Reconcile can route it to a condition instead of an infinite
retry.
- terdutserver_controller.go: ties it together -- finalizer add, DB
resolution, Deployment/Service reconcile, wait for a ready replica,
bootstrap, Ready/Bootstrapped/DatabaseReady conditions. Finalizer on
delete only removes the generated Secrets: terdut-server's API can't
delete a user or service account, only revoke keys, so there's
nothing server-side to undo.
- internal/tdclient: added Bootstrap, CreateInstanceServiceAccount,
GetServiceAccountByName, CreateServiceAccountKey, matching
terdut-server's real handlers' request/response shapes (internal/api/
users.go, service_accounts.go in that repo) field-for-field.
- Tests: envtest suite covering the full DSN-path lifecycle end to end
(finalizer -> Deployment/Service -> simulated readiness -> real
bootstrap against an httptest.Server fake), the adopt-on-409 recovery
path, BootstrapStateLost, both Zalando outcomes (cluster not found;
cluster + Secret found -> real DSN -> Ready), and deletion. A minimal
test-only stub of the Zalando CRD (internal/controller/testdata) lets
envtest create fixture objects without a real postgres-operator
installed. 74.0%/44.7% coverage, 0 lint issues.
- Two things scoped down from §8's full ambition, called out in code and
ROADMAP.md rather than silently dropped: no live watch on the
Zalando-generated Secret for rotation (periodic resync notices
eventually, not immediately), no Gateway API HTTPRoute creation from
spec.networking (would add a new dependency; nothing about proving
bootstrap works depends on external ingress existing). Both are
near-term follow-ups.
Verified locally: make fmt lint test build all clean.
This commit is contained in:
@@ -5,13 +5,12 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// SecretKeyRef names one data key inside a Secret. Unlike a typical
|
||||
// cross-namespace reference, there is deliberately no namespace field here:
|
||||
// every Secret this type points at (DESIGN.md §6) lives in the operator's
|
||||
// own namespace, always, by construction — never anywhere else, so there is
|
||||
// nothing for a namespace field to vary. What does vary is the key: fixed
|
||||
// ("token") when the controller generated the Secret itself, whatever a
|
||||
// human chose when it was handed to the controller instead.
|
||||
// SecretKeyRef names one data key inside a Secret. Every use of this type in
|
||||
// TerdutServerSpec resolves in the TerdutServer's own namespace (it's wired
|
||||
// straight into the Deployment's pod spec as a secretKeyRef env source,
|
||||
// which Kubernetes itself only allows same-namespace) -- unlike the
|
||||
// generated credentials Secret (DESIGN.md §6), which always lives in the
|
||||
// operator's own namespace and is never referenced through this type.
|
||||
type SecretKeyRef struct {
|
||||
// name is the Secret's name.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
@@ -22,6 +21,148 @@ type SecretKeyRef struct {
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
// ImageSpec is the terdut-server image to run.
|
||||
type ImageSpec struct {
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
Repository string `json:"repository"`
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
// NetworkingSpec is how this TerdutServer is reached from outside the
|
||||
// cluster.
|
||||
//
|
||||
// hostname/gatewayListener describe the intended Gateway API HTTPRoute
|
||||
// (matching charts/terdut-server's own templates/httpproxy.yaml, despite its
|
||||
// name — that chart carries a Gateway API HTTPRoute, not a Contour
|
||||
// HTTPProxy), but creating that HTTPRoute isn't implemented yet: it needs
|
||||
// the Gateway API types as a new dependency, and nothing about proving a
|
||||
// TerdutServer boots and bootstraps a real server depends on external
|
||||
// ingress existing. Tracked as a near-term follow-up, not deferred to a
|
||||
// later ROADMAP.md stage the way Deployment/database/bootstrap once were.
|
||||
type NetworkingSpec struct {
|
||||
// hostname the HTTPRoute will carry once it exists.
|
||||
// +optional
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
|
||||
// servicePort is both the Service's port and the HTTPRoute's backend
|
||||
// port once it exists. Defaults to 8080, matching the chart's own
|
||||
// service.port default.
|
||||
// +kubebuilder:default=8080
|
||||
// +optional
|
||||
ServicePort int32 `json:"servicePort,omitempty"`
|
||||
|
||||
// gatewayListener is the HTTPRoute's sectionName once it exists. Empty
|
||||
// attaches to every matching listener, including plaintext HTTP.
|
||||
// +optional
|
||||
GatewayListener string `json:"gatewayListener,omitempty"`
|
||||
}
|
||||
|
||||
// PostgresClusterRef names a Zalando postgres-operator `postgresql` CR
|
||||
// (`acid.zalan.do/v1`) in the same namespace (DESIGN.md §8). By convention
|
||||
// — matching every example in DESIGN.md and the chart's own — the database
|
||||
// and role this operator consumes from it are both named "terdut"; this
|
||||
// isn't configurable in v1 (there is no field for it because the design
|
||||
// doesn't have one yet, not an oversight here).
|
||||
type PostgresClusterRef struct {
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// DatabaseSpec is the Postgres connection this TerdutServer uses. Exactly
|
||||
// one of dsn or postgresClusterRef must be set (DESIGN.md §8) — this
|
||||
// operator provisions no database either way, only wires up one that
|
||||
// exists.
|
||||
// +kubebuilder:validation:XValidation:rule="(has(self.dsn) ? 1 : 0) + (has(self.postgresClusterRef) ? 1 : 0) == 1",message="exactly one of dsn or postgresClusterRef must be set"
|
||||
type DatabaseSpec struct {
|
||||
// dsn is a DSN with no password in it, e.g.
|
||||
// "postgres://terdut@terdut-postgres:5432/terdut?sslmode=require" --
|
||||
// mutually exclusive with postgresClusterRef.
|
||||
// +optional
|
||||
DSN string `json:"dsn,omitempty"`
|
||||
|
||||
// passwordSecretRef is where PGPASSWORD comes from for the dsn path.
|
||||
// pgx falls back to libpq's environment variables for anything the DSN
|
||||
// omits, so the password never appears in the DSN string itself. Unused
|
||||
// on the postgresClusterRef path -- the Zalando-generated Secret is
|
||||
// wired in directly instead.
|
||||
// +optional
|
||||
PasswordSecretRef *SecretKeyRef `json:"passwordSecretRef,omitempty"`
|
||||
|
||||
// postgresClusterRef names a Zalando postgres-operator CR instead of a
|
||||
// plain DSN -- mutually exclusive with dsn.
|
||||
// +optional
|
||||
PostgresClusterRef *PostgresClusterRef `json:"postgresClusterRef,omitempty"`
|
||||
}
|
||||
|
||||
// SweeperSpec controls incident auto-resolve/archive timing. Values are
|
||||
// Go duration strings (e.g. "6h"), passed straight through to the
|
||||
// TERDUT_STALE_AFTER/TERDUT_ARCHIVE_AFTER env vars exactly as written --
|
||||
// not a structured metav1.Duration, since terdut-server parses them itself
|
||||
// and a round-trip through a different type would buy nothing.
|
||||
type SweeperSpec struct {
|
||||
// +optional
|
||||
StaleAfter string `json:"staleAfter,omitempty"`
|
||||
// +optional
|
||||
ArchiveAfter string `json:"archiveAfter,omitempty"`
|
||||
}
|
||||
|
||||
// DeadmanSpec controls dead man's switch alerts. Matchers/Timeout/Severity
|
||||
// map straight to TERDUT_DEADMAN_MATCHERS/TERDUT_DEADMAN_TIMEOUT/
|
||||
// TERDUT_DEADMAN_SEVERITY.
|
||||
type DeadmanSpec struct {
|
||||
// +optional
|
||||
Matchers string `json:"matchers,omitempty"`
|
||||
// +optional
|
||||
Timeout string `json:"timeout,omitempty"`
|
||||
// +optional
|
||||
Severity string `json:"severity,omitempty"`
|
||||
}
|
||||
|
||||
// NotifySpec controls push notifications via ntfy. Empty ntfyURL disables
|
||||
// notifications entirely (matches the chart's own default).
|
||||
type NotifySpec struct {
|
||||
// +optional
|
||||
NtfyURL string `json:"ntfyURL,omitempty"`
|
||||
// +optional
|
||||
FallbackTopic string `json:"fallbackTopic,omitempty"`
|
||||
// +optional
|
||||
RepeatEvery string `json:"repeatEvery,omitempty"`
|
||||
// tokenSecretRef is an optional bearer token for an access-controlled
|
||||
// ntfy. Leave unset for an open ntfy.
|
||||
// +optional
|
||||
TokenSecretRef *SecretKeyRef `json:"tokenSecretRef,omitempty"`
|
||||
}
|
||||
|
||||
// OIDCSpec controls single sign-on. Fields the chart also exposes but
|
||||
// DESIGN.md's spec doesn't (usernameClaim, emailClaim, groupsClaim,
|
||||
// trustEmail) use terdut-server's own defaults
|
||||
// (preferred_username/email/groups/false) rather than being added here
|
||||
// speculatively.
|
||||
type OIDCSpec struct {
|
||||
// +optional
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
// +optional
|
||||
Issuer string `json:"issuer,omitempty"`
|
||||
// +optional
|
||||
ClientID string `json:"clientID,omitempty"`
|
||||
// +optional
|
||||
ClientSecretRef *SecretKeyRef `json:"clientSecretRef,omitempty"`
|
||||
// +kubebuilder:default="SSO"
|
||||
// +optional
|
||||
Name string `json:"name,omitempty"`
|
||||
// +kubebuilder:default="openid profile email"
|
||||
// +optional
|
||||
Scopes string `json:"scopes,omitempty"`
|
||||
// +optional
|
||||
AllowedGroups []string `json:"allowedGroups,omitempty"`
|
||||
// +optional
|
||||
AdminGroup string `json:"adminGroup,omitempty"`
|
||||
// +kubebuilder:default="12h"
|
||||
// +optional
|
||||
SessionMaxAge string `json:"sessionMaxAge,omitempty"`
|
||||
}
|
||||
|
||||
// AllowedTeamsNamespaces gates which namespaces a TerdutTeam may resolve a
|
||||
// cross-namespace serverRef into this TerdutServer from (DESIGN.md §4.6).
|
||||
// Same-namespace TerdutTeams are always allowed, regardless of this field.
|
||||
@@ -51,41 +192,49 @@ type AllowedTeams struct {
|
||||
|
||||
// TerdutServerSpec defines the desired state of TerdutServer.
|
||||
//
|
||||
// Narrowed to Stage 1 (ROADMAP.md): this covers only what the bootstrap/
|
||||
// credentials flow (DESIGN.md §6) needs against an already-running,
|
||||
// already-bootstrapped terdut-server. The fields that would have the
|
||||
// controller manage a Deployment/Service/database — image, replicas,
|
||||
// networking, database — are deferred to Stage 5 and deliberately absent
|
||||
// here, not an oversight; adding them later is additive, not a breaking
|
||||
// change to this shape.
|
||||
// The operator creates and owns every TerdutServer it manages (DESIGN.md
|
||||
// §1) -- there is no bring-your-own-install path. This is the full shape
|
||||
// from DESIGN.md §4.1: Deployment, Service, database, bootstrap and
|
||||
// credentials are all built together (ROADMAP.md Stage 1), not staged
|
||||
// separately the way an earlier version of this design did.
|
||||
type TerdutServerSpec struct {
|
||||
// endpoint is the base URL of an already-running terdut-server this
|
||||
// TerdutServer represents, e.g. "http://terdut.oncall.svc:8080". This
|
||||
// stage never creates or manages a Deployment/Service for it — the
|
||||
// server is expected to already exist, deployed some other way (its own
|
||||
// Helm chart, by hand).
|
||||
// +required
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
Endpoint string `json:"endpoint"`
|
||||
Image ImageSpec `json:"image"`
|
||||
|
||||
// credentialsSecretRef names a Secret, in the operator's own namespace,
|
||||
// that a human has already created by minting an instance-scoped service
|
||||
// account with their own admin session (POST /api/service-accounts,
|
||||
// DESIGN.md §6) and placing its raw key under the given key. Set, and
|
||||
// the Secret found: the controller adopts it outright and skips
|
||||
// bootstrap entirely — this is the expected path for Stage 1, not a
|
||||
// fallback (DESIGN.md §6 explains why self-registration cannot complete
|
||||
// unauthenticated in the setup this stage actually exercises).
|
||||
//
|
||||
// Unset: the controller has no way to acquire a credential in this
|
||||
// stage (self-registration lands in Stage 5) and reports
|
||||
// Ready: False, reason: CredentialsSecretRefRequired.
|
||||
// replicas. terdut-server is not horizontally-scale-tested; keep this
|
||||
// at its default of 1 unless you've verified otherwise -- the sweeper
|
||||
// and the notifier are unsynchronised singletons.
|
||||
// +kubebuilder:default=1
|
||||
// +optional
|
||||
CredentialsSecretRef *SecretKeyRef `json:"credentialsSecretRef,omitempty"`
|
||||
Replicas int32 `json:"replicas,omitempty"`
|
||||
|
||||
// +required
|
||||
Networking NetworkingSpec `json:"networking"`
|
||||
|
||||
// +required
|
||||
Database DatabaseSpec `json:"database"`
|
||||
|
||||
// +optional
|
||||
Sweeper SweeperSpec `json:"sweeper,omitempty"`
|
||||
|
||||
// +optional
|
||||
Deadman DeadmanSpec `json:"deadman,omitempty"`
|
||||
|
||||
// +optional
|
||||
Notify NotifySpec `json:"notify,omitempty"`
|
||||
|
||||
// +optional
|
||||
OIDC OIDCSpec `json:"oidc,omitempty"`
|
||||
|
||||
// passwordLogin: whether a user may sign in, or sign up, with a
|
||||
// password.
|
||||
// +kubebuilder:default=true
|
||||
// +optional
|
||||
PasswordLogin bool `json:"passwordLogin,omitempty"`
|
||||
|
||||
// allowedTeams gates cross-namespace TerdutTeams (DESIGN.md §4.6).
|
||||
// Unused until TerdutTeam exists (Stage 2); present now so this CRD's
|
||||
// schema doesn't need a breaking change to grow it later.
|
||||
// Unused until TerdutTeam exists (ROADMAP.md Stage 2); present now so
|
||||
// this CRD's schema doesn't need a breaking change to grow it later.
|
||||
// +optional
|
||||
AllowedTeams AllowedTeams `json:"allowedTeams,omitempty"`
|
||||
}
|
||||
@@ -95,29 +244,42 @@ const (
|
||||
// ConditionReady is the standard top-level condition every CRD carries
|
||||
// (DESIGN.md §7).
|
||||
ConditionReady = "Ready"
|
||||
// ConditionDatabaseReady reflects whether the configured database is
|
||||
// usable -- for postgresClusterRef, whether the Zalando CR and its
|
||||
// generated credentials Secret both resolved; for a plain dsn, always
|
||||
// true once set (DESIGN.md §8: "no connectivity check beyond what the
|
||||
// Deployment's own readiness probe already gives").
|
||||
ConditionDatabaseReady = "DatabaseReady"
|
||||
// ConditionBootstrapped reflects whether a working credential has been
|
||||
// acquired — adopted from spec.credentialsSecretRef in this stage.
|
||||
// acquired via self-registration (DESIGN.md §6).
|
||||
ConditionBootstrapped = "Bootstrapped"
|
||||
)
|
||||
|
||||
// Condition reasons this controller sets.
|
||||
const (
|
||||
// ReasonCredentialsSecretRefRequired: spec.credentialsSecretRef is
|
||||
// unset, and self-registration isn't implemented yet (Stage 5) — the
|
||||
// expected, steady-state reason whenever no bring-your-own credential
|
||||
// has been provided.
|
||||
ReasonCredentialsSecretRefRequired = "CredentialsSecretRefRequired"
|
||||
// ReasonCredentialsSecretNotFound: spec.credentialsSecretRef is set but
|
||||
// no such Secret exists (yet) in the operator's own namespace.
|
||||
ReasonCredentialsSecretNotFound = "CredentialsSecretNotFound"
|
||||
// ReasonCredentialsSecretInvalid: the Secret exists but has no data
|
||||
// under the given key, or it's empty.
|
||||
ReasonCredentialsSecretInvalid = "CredentialsSecretInvalid"
|
||||
// ReasonServerUnreachable: GET /api/version against spec.endpoint
|
||||
// failed — a bad endpoint, or the server is down.
|
||||
ReasonServerUnreachable = "ServerUnreachable"
|
||||
// ReasonAdopted: the happy path. A working credential is in hand and the
|
||||
// server answered its version probe.
|
||||
// ReasonWaitingForDeployment: the Deployment this controller created has
|
||||
// no ready replica yet -- bootstrap can't be attempted until it does.
|
||||
ReasonWaitingForDeployment = "WaitingForDeployment"
|
||||
// ReasonPostgresClusterNotFound: spec.database.postgresClusterRef names
|
||||
// no such postgresql.acid.zalan.do object (yet).
|
||||
ReasonPostgresClusterNotFound = "PostgresClusterNotFound"
|
||||
// ReasonPostgresOperatorCRDNotInstalled: spec.database.postgresClusterRef
|
||||
// is set, but the postgresql.acid.zalan.do CRD itself isn't installed in
|
||||
// this cluster (DESIGN.md §8, §9: this operator degrades gracefully
|
||||
// rather than hard-failing when that CRD is absent and BYO DSN is used
|
||||
// instead -- but a TerdutServer that explicitly asks for it still needs
|
||||
// to say clearly that it can't be satisfied).
|
||||
ReasonPostgresOperatorCRDNotInstalled = "PostgresOperatorCRDNotInstalled"
|
||||
// ReasonBootstrapStateLost: a checkpointed admin credential
|
||||
// (DESIGN.md §6) was lost after being used but before the lasting
|
||||
// credential it was for could be persisted -- the one genuinely
|
||||
// pathological case in the self-registration flow. Fail-closed, same
|
||||
// recovery as DESIGN.md §5's webhook-Secret-loss rule: delete and
|
||||
// recreate this TerdutServer.
|
||||
ReasonBootstrapStateLost = "BootstrapStateLost"
|
||||
// ReasonAdopted: the happy path. A working credential is in hand, the
|
||||
// Deployment has a ready replica, and the database (if postgresClusterRef)
|
||||
// resolved.
|
||||
ReasonAdopted = "Adopted"
|
||||
)
|
||||
|
||||
@@ -135,16 +297,23 @@ type TerdutServerStatus struct {
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
|
||||
// credentialsSecretRef mirrors spec.credentialsSecretRef once adopted —
|
||||
// same Secret, same key, always in the operator's own namespace. Set
|
||||
// only once Bootstrapped is True.
|
||||
// serviceName is the Service this controller created for the
|
||||
// Deployment, so other objects can reference it without recomputing the
|
||||
// naming convention.
|
||||
// +optional
|
||||
ServiceName string `json:"serviceName,omitempty"`
|
||||
|
||||
// credentialsSecretRef is the generated instance-scoped credential
|
||||
// (DESIGN.md §6) -- pure output, always in the operator's own
|
||||
// namespace, under a fixed data key ("token"). Set only once
|
||||
// Bootstrapped is True.
|
||||
// +optional
|
||||
CredentialsSecretRef *SecretKeyRef `json:"credentialsSecretRef,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.spec.endpoint`
|
||||
// +kubebuilder:printcolumn:name="Replicas",type=integer,JSONPath=`.spec.replicas`
|
||||
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
|
||||
// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`
|
||||
|
||||
|
||||
@@ -45,6 +45,136 @@ func (in *AllowedTeamsNamespaces) DeepCopy() *AllowedTeamsNamespaces {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DatabaseSpec) DeepCopyInto(out *DatabaseSpec) {
|
||||
*out = *in
|
||||
if in.PasswordSecretRef != nil {
|
||||
in, out := &in.PasswordSecretRef, &out.PasswordSecretRef
|
||||
*out = new(SecretKeyRef)
|
||||
**out = **in
|
||||
}
|
||||
if in.PostgresClusterRef != nil {
|
||||
in, out := &in.PostgresClusterRef, &out.PostgresClusterRef
|
||||
*out = new(PostgresClusterRef)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseSpec.
|
||||
func (in *DatabaseSpec) DeepCopy() *DatabaseSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DatabaseSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DeadmanSpec) DeepCopyInto(out *DeadmanSpec) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeadmanSpec.
|
||||
func (in *DeadmanSpec) DeepCopy() *DeadmanSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DeadmanSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ImageSpec) DeepCopyInto(out *ImageSpec) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageSpec.
|
||||
func (in *ImageSpec) DeepCopy() *ImageSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ImageSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NetworkingSpec) DeepCopyInto(out *NetworkingSpec) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkingSpec.
|
||||
func (in *NetworkingSpec) DeepCopy() *NetworkingSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NetworkingSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NotifySpec) DeepCopyInto(out *NotifySpec) {
|
||||
*out = *in
|
||||
if in.TokenSecretRef != nil {
|
||||
in, out := &in.TokenSecretRef, &out.TokenSecretRef
|
||||
*out = new(SecretKeyRef)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NotifySpec.
|
||||
func (in *NotifySpec) DeepCopy() *NotifySpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NotifySpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *OIDCSpec) DeepCopyInto(out *OIDCSpec) {
|
||||
*out = *in
|
||||
if in.ClientSecretRef != nil {
|
||||
in, out := &in.ClientSecretRef, &out.ClientSecretRef
|
||||
*out = new(SecretKeyRef)
|
||||
**out = **in
|
||||
}
|
||||
if in.AllowedGroups != nil {
|
||||
in, out := &in.AllowedGroups, &out.AllowedGroups
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCSpec.
|
||||
func (in *OIDCSpec) DeepCopy() *OIDCSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(OIDCSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PostgresClusterRef) DeepCopyInto(out *PostgresClusterRef) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PostgresClusterRef.
|
||||
func (in *PostgresClusterRef) DeepCopy() *PostgresClusterRef {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PostgresClusterRef)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SecretKeyRef) DeepCopyInto(out *SecretKeyRef) {
|
||||
*out = *in
|
||||
@@ -60,6 +190,21 @@ func (in *SecretKeyRef) DeepCopy() *SecretKeyRef {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SweeperSpec) DeepCopyInto(out *SweeperSpec) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SweeperSpec.
|
||||
func (in *SweeperSpec) DeepCopy() *SweeperSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SweeperSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TerdutServer) DeepCopyInto(out *TerdutServer) {
|
||||
*out = *in
|
||||
@@ -122,11 +267,13 @@ func (in *TerdutServerList) DeepCopyObject() runtime.Object {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TerdutServerSpec) DeepCopyInto(out *TerdutServerSpec) {
|
||||
*out = *in
|
||||
if in.CredentialsSecretRef != nil {
|
||||
in, out := &in.CredentialsSecretRef, &out.CredentialsSecretRef
|
||||
*out = new(SecretKeyRef)
|
||||
**out = **in
|
||||
}
|
||||
out.Image = in.Image
|
||||
out.Networking = in.Networking
|
||||
in.Database.DeepCopyInto(&out.Database)
|
||||
out.Sweeper = in.Sweeper
|
||||
out.Deadman = in.Deadman
|
||||
in.Notify.DeepCopyInto(&out.Notify)
|
||||
in.OIDC.DeepCopyInto(&out.OIDC)
|
||||
in.AllowedTeams.DeepCopyInto(&out.AllowedTeams)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user