Full ProxySpec/ProxyStatus/Proxy types per the plan: PlacementSpec,
CloudInitSpec, EndpointSpec, HealthCheckSpec, SecretKeySelector, all
defaults, and 7 CEL XValidation rules enforcing mode/provider
immutability, provider/endpoint required-iff-Managed/External, and
cloud-init exactly-one-of inline/secretRef.
Applies the four corrections identified during planning that would
otherwise be silent bugs: MaxLeases as *int32 (so an explicit 0 survives
Go round-trips instead of re-defaulting to 5), HealthCheck's
default={} marker (so nested defaults apply even when the field is
omitted entirely), MinLength=1 on Provider/CloudInit.Inline (so the CEL
has() checks stay simple), and listType=map on Conditions.
Adds pure helpers (EffectivePort, EffectiveHost, HealthCheckOrDefault,
MaxLeasesOrDefault) with table-driven tests, for use by the health
engine, discovery API, and spec-hash computation in later steps.
Patches the scaffolded placeholder controller test's resource literal to
a schema-valid spec so it survives the new CRD validation — the test
itself is rewritten wholesale in Step 4 alongside the real reconciler.
Regenerated deepcopy and the CRD; make test green (envtest confirmed all
7 CEL rules enforced by a real apiserver).
Co-Authored-By: Claude <noreply@anthropic.com>
88 lines
2.8 KiB
Go
88 lines
2.8 KiB
Go
/*
|
|
Copyright 2026.
|
|
|
|
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 v1alpha1
|
|
|
|
// EffectivePort returns the port a client should use to reach the proxy:
|
|
// spec.endpoint.port for External proxies, spec.port for Managed proxies.
|
|
// Falls back to DefaultPort if the relevant field is unset, so callers that
|
|
// bypass CRD structural defaulting (unit tests, fake clients) still get a
|
|
// sane value.
|
|
func (p *Proxy) EffectivePort() int32 {
|
|
if p.Spec.Mode == ModeExternal && p.Spec.Endpoint != nil {
|
|
if p.Spec.Endpoint.Port != 0 {
|
|
return p.Spec.Endpoint.Port
|
|
}
|
|
return DefaultPort
|
|
}
|
|
if p.Spec.Port != 0 {
|
|
return p.Spec.Port
|
|
}
|
|
return DefaultPort
|
|
}
|
|
|
|
// EffectiveHost returns the host a client should use to reach the proxy:
|
|
// spec.endpoint.host for External proxies, status.ip for Managed proxies
|
|
// (populated once the VM is running).
|
|
func (p *Proxy) EffectiveHost() string {
|
|
if p.Spec.Mode == ModeExternal && p.Spec.Endpoint != nil {
|
|
return p.Spec.Endpoint.Host
|
|
}
|
|
return p.Status.IP
|
|
}
|
|
|
|
// HealthCheckOrDefault returns spec.healthCheck with every unset field
|
|
// filled from its default. CRD structural defaulting (the default={} marker
|
|
// on ProxySpec.HealthCheck) already does this for objects that went through
|
|
// the API server; this is for callers that didn't (unit tests, fake
|
|
// clients, or a Proxy constructed directly in Go).
|
|
func (p *Proxy) HealthCheckOrDefault() HealthCheckSpec {
|
|
var hc HealthCheckSpec
|
|
if p.Spec.HealthCheck != nil {
|
|
hc = *p.Spec.HealthCheck
|
|
}
|
|
if hc.ProbeURL == "" {
|
|
hc.ProbeURL = DefaultProbeURL
|
|
}
|
|
if hc.IntervalSeconds == 0 {
|
|
hc.IntervalSeconds = DefaultHealthCheckIntervalSeconds
|
|
}
|
|
if hc.TimeoutSeconds == 0 {
|
|
hc.TimeoutSeconds = DefaultHealthCheckTimeoutSeconds
|
|
}
|
|
if hc.FailureThreshold == 0 {
|
|
hc.FailureThreshold = DefaultFailureThreshold
|
|
}
|
|
if hc.SuccessThreshold == 0 {
|
|
hc.SuccessThreshold = DefaultSuccessThreshold
|
|
}
|
|
if len(hc.ExpectedStatusCodes) == 0 {
|
|
hc.ExpectedStatusCodes = append([]int32(nil), DefaultExpectedStatusCodes...)
|
|
}
|
|
return hc
|
|
}
|
|
|
|
// MaxLeasesOrDefault returns spec.maxLeases, or DefaultMaxLeases if unset.
|
|
// spec.maxLeases is a pointer specifically so an explicit 0 (unleasable) is
|
|
// distinguishable from "unset" and survives Go round-trips; this helper
|
|
// preserves that distinction.
|
|
func (p *Proxy) MaxLeasesOrDefault() int32 {
|
|
if p.Spec.MaxLeases != nil {
|
|
return *p.Spec.MaxLeases
|
|
}
|
|
return DefaultMaxLeases
|
|
}
|