plan

package
v0.8.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 17, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Index

Constants

View Source
const (
	TargetAuthAPIKeys = "confluent_cloud_api_keys"
	TargetAuthMTLS    = "mtls"
	TargetAuthOAuth   = "oauth"
)

Target auth method tokens. Customer-facing values written into `plan-inputs.yaml` and rendered back in the AuthDecision JSON.

View Source
const (
	SourceAuthSCRAM  = "scram"
	SourceAuthIAM    = "iam"
	SourceAuthMTLS   = "mtls"
	SourceAuthUnauth = "unauth"
)

Source-auth tokens. Stable strings — these appear in the rendered Plan (AuthDecision.SourceAuths) and key the auth_mapping table in plan-config.yaml. Don't rename without bumping a schema doc.

View Source
const (
	DiscoveredClientAuthIAM             = "IAM"
	DiscoveredClientAuthSASLSCRAM       = "SASL_SCRAM"
	DiscoveredClientAuthTLS             = "TLS"
	DiscoveredClientAuthUnauthenticated = "UNAUTHENTICATED"
	DiscoveredClientAuthUnknown         = "UNKNOWN"
)

DiscoveredClientAuth* mirrors the literal strings that `kcp scan client-inventory` writes into `DiscoveredClient.Auth` (see `cmd/scan/client_inventory/kafka_trace_line_parser.go`).

**Heads-up:** `types.AuthTypeIAM` in `internal/types/types.go` resolves to `"SASL/IAM"` — a DIFFERENT string used elsewhere. Use these constants when comparing against the persisted client inventory, not the `types.AuthType*` constants.

View Source
const (
	DowntimeZero                      = "zero"
	DowntimeSecondsPerService         = "seconds_per_service"
	DowntimeMinutesPerService         = "minutes_per_service"
	DowntimeScheduledWindowSequential = "scheduled_window_sequential"
	DowntimeScheduledWindowAllAtOnce  = "scheduled_window_all_at_once"
	DowntimeLetConfluentChoose        = "let_confluent_choose"
	DowntimeUnsetFallback             = DowntimeLetConfluentChoose
)

downtime_tolerance enum values. Stable identifiers — used as YAML input tokens AND as keys in the style mapping.

View Source
const (
	PrereqNotStarted            = "not_started"
	PrereqStatusInProgressInput = "in_progress"
	PrereqStatusCompleteInput   = "complete"
)

Gateway prereq status enum values.

View Source
const (
	EffortSignalIDIAMClientCount           = "iam_to_scram_client_count"
	EffortSignalIDMM2CheckpointTopics      = "mm2_checkpoint_topic_count"
	EffortSignalIDSelfManagedConnectFleets = "self_managed_connect_fleet_count"
	EffortSignalIDGlueSerializerMigration  = "glue_serializer_migration_count"
)

Stable Effort Signal IDs. Matches the spec row order.

View Source
const (
	RedFlagIDSchemalessSource          = "schemaless_source"
	RedFlagIDKafkaVersionBelowCLFloor  = "kafka_version_below_cl_floor"
	RedFlagIDIAMAuthEnabled            = "iam_auth_enabled"
	RedFlagIDGlueSRInUse               = "glue_sr_in_use"
	RedFlagIDPartitionApproachingCap   = "partition_approaching_cap"
	RedFlagIDMSKConnectPresent         = "msk_connect_present"
	RedFlagIDSelfManagedConnectPresent = "self_managed_connect_present"
	RedFlagIDMultiRegionSource         = "multi_region_source"
	RedFlagIDZeroACLsWithIAM           = "zero_acls_with_iam"
	RedFlagIDClientInventoryGap        = "client_inventory_gap"
	RedFlagIDMSKExpressBrokerTier      = "msk_express_broker_tier"
	RedFlagIDTieredStorageInUse        = "tiered_storage_in_use"
	RedFlagIDEOSInUse                  = "eos_in_use"
	RedFlagIDKafkaStreamsInUse         = "kafka_streams_in_use"
	RedFlagIDBroadTopicPatternMatch    = "broad_topic_pattern_match"
)

Stable Red Flag IDs. Matches the spec's row numbering — kept stable so JSON consumers can branch on the ID without parsing the title.

View Source
const (
	SchemaStrategyUnknown                       = "unknown"
	SchemaStrategyNoSchemas                     = "no_schemas"
	SchemaStrategyAdoptSchemasDuringMigration   = "adopt_schemas_during_migration"
	SchemaStrategyMigrateExistingSchemaRegistry = "migrate_existing_schema_registry"

	SchemaCPEditionEnterprise = "enterprise"
	SchemaCPEditionCommunity  = "community"
)

Plan-input enum tokens for schema migration. Stable customer-facing strings — surfaced in plan-inputs.yaml and the rendered Plan.

View Source
const (
	ConsumerHistoryRequired    = "required"
	ConsumerHistoryNotRequired = "not_required"
	ConsumerHistoryUnknown     = "unknown"

	HistoricalKeepMSKRunning = "keep_msk_running_until_data_expires"
	HistoricalBulkLoadExtern = "bulk_load_historical_via_external_tool"
	HistoricalDeferToAccount = "defer_to_account_team"
)

Plan-input enum tokens for tiered-storage knobs. Stable customer-facing strings; the resolver surfaces them through PlanInputsResolved.ConsumerHistoryRequirement and HistoricalDataStrategy.

Variables

This section is empty.

Functions

func LoadPlanInputs

func LoadPlanInputs(path string) (*types.PlanInputs, error)

LoadPlanInputs reads plan-inputs.yaml. Returns nil *PlanInputs if path is empty (no file supplied is a valid state — the Plan still generates from defaults). Missing fields never fail because no field is required at the YAML schema level.

func RenderJSON

func RenderJSON(p *types.Plan) ([]byte, error)

RenderJSON serialises a Plan to canonical 2-space-indented JSON. Stable key ordering comes for free from struct field order — any map members are encoded by Go's stdlib in sorted key order since Go 1.12.

func RenderMarkdown

func RenderMarkdown(p *types.Plan, cfg *PlanConfig) ([]byte, error)

RenderMarkdown emits the human-readable Plan: Source Environment, Sizing & Cluster Decisions, Actions Needed, and collapsed appendices. `cfg` must be the same PlanConfig the PlanService used to build the plan (product-fact numbers in the Definitions block and the partition cap in the appendix read from it).

func ResolvePlanInputs

func ResolvePlanInputs(in *types.PlanInputs, cfg *PlanConfig) types.PlanInputsResolved

ResolvePlanInputs merges customer-supplied PlanInputs with pinned defaults from PlanConfig. Customer-set fields win; everything else falls back to PlanConfig.PlanInputDefaults. Raw is preserved so downstream consumers can detect which sizing fields the customer explicitly set (HeadroomFraction, SLATarget, SizingPercentile, etc.).

func ResolvePlanInputsForCluster

func ResolvePlanInputsForCluster(in *types.PlanInputs, cfg *PlanConfig, clusterName string) types.PlanInputsResolved

ResolvePlanInputsForCluster layers a cluster-specific override on top of the resolved globals. If `in.Clusters[clusterName]` is set, every non-nil field in it wins over the global. Otherwise the global view is returned unchanged. Caller uses this per-cluster during Plan build so heterogeneous fleets get the right verdicts without one global flag flipping every cluster's tier.

**Hot path note:** when iterating many clusters, prefer `applyClusterOverride` against a pre-resolved global view — this function re-resolves globals on every call.

Types

type AuthMapping added in v0.8.4

type AuthMapping struct {
	Target            string `yaml:"target"`
	GatewayCompatible bool   `yaml:"gateway_compatible"`
	TransparentSwap   bool   `yaml:"transparent_swap"`
	Note              string `yaml:"note"`
	Source            string `yaml:"source"`
	LastVerified      string `yaml:"last_verified"`
}

AuthMapping is one row in the source→target auth lookup table keyed by source-auth token (scram / iam / mtls / unauth). Defaults resolve via this table when the customer doesn't override via `target_auth_method`. Source + LastVerified carry the row's provenance (which Confluent doc the values come from, and when an engineer last checked it).

type ClusterLinking

type ClusterLinking struct {
	SourceMinKafkaVersion string `yaml:"source_min_kafka_version"`
	ExpressTierSupported  string `yaml:"express_tier_supported"`
	Source                string `yaml:"source"`
}

type CostReconciliationCfg added in v0.8.4

type CostReconciliationCfg struct {
	UsageFamilies []string `yaml:"usage_families"`
}

CostReconciliationCfg pins the AWS Cost Explorer usage-string families that count as MSK broker spend. Today AWS bills MSK under `Kafka.*` and `Express.*`; new broker tiers will land as new family tokens. Admins extend this list when a new tier ships without waiting for a kcp release.

type EnterpriseCaps

type EnterpriseCaps struct {
	PerECKUIngressMBps   int    `yaml:"per_eCKU_ingress_mbps"`
	PerECKUEgressMBps    int    `yaml:"per_eCKU_egress_mbps"`
	PerECKUPartitionRate int    `yaml:"per_eCKU_partition_rate"`
	PrivateLinkMaxECKU   int    `yaml:"privatelink_max_eCKU"`
	PNIMaxECKU           int    `yaml:"pni_max_eCKU"`
	ACLCountCap          int    `yaml:"acl_count_cap"`
	Source               string `yaml:"source"`
}

type PlanConfig

type PlanConfig struct {
	SchemaVersion            int    `yaml:"schema_version"`
	LastVerified             string `yaml:"last_verified"`
	KCPVersionAtVerification string `yaml:"kcp_version_at_verification"`

	EnterpriseCaps     EnterpriseCaps         `yaml:"enterprise_caps"`
	ClusterLinking     ClusterLinking         `yaml:"cluster_linking"`
	SchemaLinking      SchemaLinking          `yaml:"schema_linking"`
	PlanInputDefaults  PlanInputDefaults      `yaml:"plan_input_defaults"`
	AuthMapping        map[string]AuthMapping `yaml:"auth_mapping"`
	Thresholds         Thresholds             `yaml:"thresholds"`
	CostReconciliation CostReconciliationCfg  `yaml:"cost_reconciliation"`
}

PlanConfig is the deserialized plan-config.yaml. The embedded copy is the default; an admin-supplied override file replaces only the fields it specifies (standard yaml unmarshal semantics — slices replace whole, maps merge by key).

func LoadPlanConfig

func LoadPlanConfig(overridePath string) (*PlanConfig, error)

LoadPlanConfig returns the embedded plan-config.yaml, optionally overridden by the file at overridePath. Pass an empty string to skip the override.

func (*PlanConfig) Validate

func (c *PlanConfig) Validate() error

type PlanInputDefaults

type PlanInputDefaults struct {
	SizingPercentile   string         `yaml:"sizing_percentile"`
	HeadroomFraction   float64        `yaml:"headroom_fraction"`
	SpikyWorkloadRatio float64        `yaml:"spiky_workload_ratio"`
	SLAFloorECKU       map[string]int `yaml:"sla_floor_eCKU"`

	// Customer-declared hard requirements (all default false).
	EnforceSchemasAtTheBroker            bool `yaml:"enforce_schemas_at_the_broker"`
	RequiresHighThroughputRESTProduceAPI bool `yaml:"requires_high_throughput_rest_produce_api"`
	Requires9995SLAWithinSingleZone      bool `yaml:"requires_99_95_sla_within_a_single_zone"`

	// Target cloud + existing VPC connectivity (Dedicated-path networking).
	TargetCloud             string `yaml:"target_cloud"`
	ExistingVPCConnectivity string `yaml:"existing_vpc_connectivity"`

	// Networking triggers (PNI→PrivateLink) — see PlanInputsResolved.
	CCEgressRequired         bool `yaml:"cc_egress_required"`
	ProjectedPNIGatewayCount int  `yaml:"projected_pni_gateway_count"`

	// Cutover defaults.
	DowntimeTolerance            string `yaml:"downtime_tolerance"`
	SubPattern                   string `yaml:"sub_pattern"`
	PreferGateway                bool   `yaml:"prefer_gateway"`
	ConfluentForKubernetesStatus string `yaml:"confluent_for_kubernetes_status"`
	CCGatewayLicenseStatus       string `yaml:"cc_gateway_license_status"`
	IAMPreMigrationStatus        string `yaml:"iam_pre_migration_status"`

	// Auth defaults.
	TargetAuthMethod string `yaml:"target_auth_method"`

	// Schema-migration defaults. Defaults below resolve to
	// `unknown` so first-run plans always ask the customer to declare
	// strategy + reachability + CP version/edition rather than silently
	// picking a path.
	SchemaStrategy       string `yaml:"schema_strategy"`
	ConfluentSRCPVersion string `yaml:"confluent_sr_cp_version,omitempty"`
	ConfluentSRCPEdition string `yaml:"confluent_sr_cp_edition,omitempty"`
}

type PlanService

type PlanService struct {
	// contains filtered or unexported fields
}

Function-naming convention across this package:

  • `decide*` — produces a verdict / path from inputs. One decision per cluster (sizing, cluster type, networking, auth) or one per fleet (cutover, schema). Output is the rendered §section's primary recommendation.

  • `detect*` — enumerates findings or signals from `state` / `plan` (red flags, effort signals, tiered storage, cost reconciliation, all OQ detectors). Output is a list/section, not a single verdict. Some detectors also apply small input cascades (e.g. `detectTieredStorage` defaults `HistoricalDataStrategy`) — that's deliberate: the cascade is scoped to the section's findings, not a standalone decision.

  • `compute*` — pure numeric / structural transforms with no verdict (e.g. `computeClusterSizing`, `computeCutoverOverrides`). Output is intermediate data the renderer + decide* consume.

PlanService orchestrates the deterministic plan-generation pipeline. Each Build step is a pure function so the test surface is the orchestration, not its parts.

func NewPlanService

func NewPlanService(cfg *PlanConfig, now func() time.Time) *PlanService

NewPlanService wires a PlanConfig into a PlanService. now is overridable for deterministic tests; pass nil for time.Now.

func (*PlanService) Build

func (s *PlanService) Build(state types.ProcessedState, inputs types.PlanInputsResolved, stateFilePath string) (*types.Plan, error)

Build produces a Plan from a ProcessedState and resolved plan-inputs. Each step is a pure function so the test surface is the orchestration, not its parts.

type SchemaLinking added in v0.8.4

type SchemaLinking struct {
	MinCPVersion      string `yaml:"min_cp_version"`
	RequiresCPEdition string `yaml:"requires_cp_edition"`
	Source            string `yaml:"source"`
	LastVerified      string `yaml:"last_verified"`
}

SchemaLinking pins the version + edition floor that source Confluent SR must clear for the Schema Linking path. Customers below these floors get the `defer_to_account_team` verdict (REST API export / import is technically possible but kcp doesn't drive it). The values come from the [Schema Linking on CP docs](https://docs.confluent.io/platform/current/schema-registry/schema-linking-cp.html); last-verified date below tracks when an engineer cross-checked them.

type Thresholds added in v0.8.4

type Thresholds struct {
	// StaleStateDays — emit the state-file-stale OQ when the state
	// snapshot is older than this many days.
	StaleStateDays int `yaml:"stale_state_days"`
	// PNIGatewayBreakeven — projected PNI gateway count at or above
	// which the recommendation flips from PNI to PrivateLink.
	PNIGatewayBreakeven int `yaml:"pni_gateway_breakeven"`
	// PartitionApproachingFraction — Red Flag row 5 fires when
	// user-topic partitions exceed this fraction of the cluster's
	// sized eCKU capacity (per_eCKU_partition_rate * FinalECKU).
	// 0.30 means "fire at 30% of sized capacity"; pre-fix this was
	// hardcoded in red_flags.go.
	PartitionApproachingFraction float64 `yaml:"partition_approaching_fraction"`
}

Thresholds collects numeric cutoffs that the rule engine and the renderer need but that aren't customer-facing knobs — pulled out so an admin can tune them without code edits if a tenant has a genuinely different operating envelope.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL