import (
"encoding/yaml"
"strings"
)
"topology-ocm": {
description: "The OCM topology policy enables multi-cluster-aware Workload deployment via the creation of OCM resources: a Placement and a ManifestWorkReplicaSet that targets the Placement and contains the Workload."
annotations: {}
labels: {
"policydefinition.spectrocloud.com/type": "topology"
}
attributes: {}
type: "policy"
}
template: {
output: {
apiVersion: "cluster.open-cluster-management.io/v1beta1"
kind: "Placement"
metadata: {
labels: {
if parameter.placement.labels != _|_ {
parameter.placement.labels
}
"wl.spectrocloud.com/name": context.workloadName
"wl.spectrocloud.com/component": context.name
}
if parameter.placement.annotations != _|_ {
annotations: parameter.placement.annotations
}
namespace: parameter.hubNamespace
}
spec: {
if parameter.placement.clusterSets != _|_ {
clusterSets: parameter.placement.clusterSets
}
if parameter.placement.numberOfClusters != _|_ {
numberOfClusters: parameter.placement.numberOfClusters
}
if parameter.placement.predicates != _|_ {
predicates: parameter.placement.predicates
}
if parameter.placement.decisionStrategy != _|_ {
decisionStrategy: parameter.placement.decisionStrategy
}
if parameter.placement.prioritizerPolicy != _|_ {
prioritizerPolicy: parameter.placement.prioritizerPolicy
}
if parameter.placement.spreadPolicy != _|_ {
spreadPolicy: parameter.placement.spreadPolicy
}
if parameter.placement.tolerations != _|_ {
tolerations: parameter.placement.tolerations
}
}
}
outputs: {
_lifecycle: parameter.manifestWorkReplicaSet.manifestWorkTemplate.lifecycle
_template: parameter.manifestWorkReplicaSet.manifestWorkTemplate
_spokeNsOrphan: *false | bool
if _lifecycle.spokeNamespace.orphan != _|_ {
_spokeNsOrphan: _lifecycle.spokeNamespace.orphan
}
_workloadOrphan: *false | bool
if _lifecycle.workload.orphan != _|_ {
_workloadOrphan: _lifecycle.workload.orphan
}
_spokeNsCreate: *true | bool
if _lifecycle.spokeNamespace.create != _|_ {
_spokeNsCreate: _lifecycle.spokeNamespace.create
}
_needsOrphaning: _workloadOrphan == true || _spokeNsOrphan == true
_orphanNamespace: _spokeNsOrphan == true || (_spokeNsCreate == true && _workloadOrphan == true)
manifestWorkReplicaSet: {
apiVersion: "work.open-cluster-management.io/v1alpha1"
kind: "ManifestWorkReplicaSet"
metadata: {
labels: {
"wl.spectrocloud.com/name": context.workloadName
"wl.spectrocloud.com/component": context.name
}
namespace: parameter.hubNamespace
}
spec: {
cascadeDeletionPolicy: "Foreground"
manifestWorkTemplate: {
manifestConfigs: [
{
resourceIdentifier: {
group: "spectrocloud.com"
name: context.workloadName
namespace: context.namespace
resource: "workloads"
}
feedbackRules: [
{
type: "JSONPaths"
jsonPaths: [
{
name: "phase"
path: ".status.phase"
},
{
name: "priorityPhases"
path: ".status.priorityPhases[*]"
},
{
name: "conditionReasons"
path: ".status.conditions[*].reason"
},
{
name: "conditionStatuses"
path: ".status.conditions[*].status"
},
{
name: "conditionTypes"
path: ".status.conditions[*].type"
},
{
name: "conditionTransitionTimes"
path: ".status.conditions[*].lastTransitionTime"
},
{
name: "components"
path: ".status.components"
},
{
name: "definitionOutputs"
path: ".status.definitionOutputs[*]"
},
{
name: "objectOutputs"
path: ".status.objectOutputs"
},
]
},
]
},
if _template.manifestConfigs != _|_ for mc in _template.manifestConfigs {
mc
},
]
if _needsOrphaning {
deleteOption: {
propagationPolicy: "SelectivelyOrphan"
selectivelyOrphans: orphaningRules: [
if _orphanNamespace {
{
name: context.namespace
resource: "namespaces"
}
},
if _workloadOrphan == true {
group: "spectrocloud.com"
name: context.workloadName
namespace: context.namespace
resource: "workloads"
},
]
}
}
if _template.executor != _|_ {
executor: _template.executor
}
if _lifecycle.spokeNamespace.create == false {
workload: manifests: [for v in context.workloadYamls {yaml.Unmarshal(v)}]
}
if _lifecycle.spokeNamespace.create == true {
workload: manifests: [
{#ns},
for v in context.workloadYamls {
yaml.Unmarshal(v)
},
]
}
}
placementRefs: [{
name: context.name
rolloutStrategy: parameter.manifestWorkReplicaSet.rolloutStrategy
}]
}
}
}
#ns: {
apiVersion: *"v1" | string
kind: *"Namespace" | string
metadata: {
name: *context.namespace | string
if context.workloadNamespaceAnnotations != _|_ {
annotations: context.workloadNamespaceAnnotations
}
if context.workloadNamespaceLabels != _|_ {
labels: context.workloadNamespaceLabels
}
}
}
#matchExpression: {
key: string
operator: *"In" | "NotIn" | "Exists" | "DoesNotExist"
values?: [...string]
}
#clusterSelector: {
claimSelector?: {
matchExpressions: [...#matchExpression]
}
labelSelector?: {
matchLabels?: [string]: string
matchExpressions?: [...#matchExpression]
}
}
#placement: {
annotations?: [string]: string
labels?: [string]: string
clusterSets?: *["global"] | [...string]
numberOfClusters?: int
predicates?: [...{
requiredClusterSelector: #clusterSelector
}]
decisionStrategy?: {
groupStrategy: {
clustersPerDecisionGroup: string
decisionGroups: [...{
groupName: string
groupClusterSelector: #clusterSelector
}]
}
}
prioritizerPolicy?: {
configurations: [...{
scoreCoordinate: {
addOn?: {
resourceName: string
scoreName: string
}
builtIn?: string
type: *"BuiltIn" | "Addon" | ""
}
weight: int
}]
mode: *"Additive" | "Exact" | ""
}
spreadPolicy?: {
spreadConstraints: [...{
maxSkew: *1 | int
topologyKey: string
topologyKeyType: *"Label" | "Claim"
whenUnsatisfiable: *"ScheduleAnyway" | "DoNotSchedule"
}]
}
tolerations?: [...{
effect?: "NoSelect" | "PreferNoSelect" | "NoSelectIfNew"
key?: string
operator: *"Equal" | "Exists"
value?: string
tolerationSeconds?: int
}]
}
#lifecycle: {
spokeNamespace: {
create: *true | bool
orphan?: *false | bool
}
workload: {
orphan: *false | bool
}
}
#resourceId: {
group?: string
name: string
namespace?: string
resource: string
}
#feedbackRule: {
jsonPaths?: [...{
name: string
path: string
version?: string
}]
type: *"WellKnownStatus" | "JSONPaths"
}
#rolloutConfig: {
maxFailures: *"0" | =~"^((100|[0-9]{1,2})%|[0-9]+)$"
minSuccessTime: *"0" | =~"^(([0-9])+[h|m|s])$"
progressDeadline: *"None" | =~"^(([0-9])+[h|m|s])|None$"
}
#mandatoryDecisionGroup: {
groupIndex: int
groupName: string
}
#manifestWorkReplicaSet: {
manifestWorkTemplate: {
lifecycle: #lifecycle
executor?: {
subject: {
serviceAccount?: {
name: =~"^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)$" & strings.MinRunes(1) & strings.MaxRunes(253)
namespace: =~"^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)$" & strings.MinRunes(1) & strings.MaxRunes(253)
}
type: "ServiceAccount"
}
}
manifestConfigs?: [...{
resourceIdentifier: #resourceId
feedbackRules?: [...#feedbackRule]
updateStrategy?: {
serverSideApply?: {
fieldManager: *"work-agent" | =~"^work-agent(-[a-zA-Z0-9]+)*$"
force: bool
}
type: *"Update" | "CreateOnly" | "ReadOnly" | "ServerSideApply"
}
}]
}
rolloutStrategy: {
all?: {
progressDeadline: *"None" | =~"^(([0-9])+[h|m|s])|None$"
}
progressive?: {
#rolloutConfig
mandatoryDecisionGroups: [...#mandatoryDecisionGroup]
maxConcurrency?: =~"^((100|[0-9]{1,2})%|[0-9]+)$"
}
progressivePerGroup?: {
#rolloutConfig
mandatoryDecisionGroups: [...#mandatoryDecisionGroup]
}
type: *"All" | "Progressive" | "ProgressivePerGroup"
}
}
parameter: {
placement: #placement
manifestWorkReplicaSet: #manifestWorkReplicaSet
hubNamespace: *"managed-cluster-set-global" | string
}
}