diff --git a/go.mod b/go.mod index 331d322..1aa9583 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.22.4 require ( github.com/alecthomas/kong v0.9.0 github.com/apple/pkl-go v0.8.0 - github.com/crossplane/crossplane-runtime v1.15.1 - github.com/crossplane/function-sdk-go v0.2.0 + github.com/crossplane/crossplane-runtime v1.16.0 + github.com/crossplane/function-sdk-go v0.3.0-rc.0.0.20240628013706-bc16c875629d github.com/google/go-cmp v0.6.0 google.golang.org/protobuf v1.34.2 k8s.io/apimachinery v0.30.2 diff --git a/go.sum b/go.sum index bbf05b6..01cefac 100644 --- a/go.sum +++ b/go.sum @@ -25,8 +25,12 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crossplane/crossplane-runtime v1.15.1 h1:g1h75tNYOQT152IUNxs8ZgSsRFQKrZN9z69KefMujXs= github.com/crossplane/crossplane-runtime v1.15.1/go.mod h1:kRcJjJQmBFrR2n/KhwL8wYS7xNfq3D8eK4JliEScOHI= +github.com/crossplane/crossplane-runtime v1.16.0 h1:lz+l0wEB3qowdTmN7t0PZkfuNSvfOoEhQrEYFbYqMow= +github.com/crossplane/crossplane-runtime v1.16.0/go.mod h1:Pz2tdGVMF6KDGzHZOkvKro0nKc8EzK0sb/nSA7pH4Dc= github.com/crossplane/function-sdk-go v0.2.0 h1:4r+dXeGgwOC2XehJlHsHlkdkUsGW8PzkiyPPd2cshQs= github.com/crossplane/function-sdk-go v0.2.0/go.mod h1:AvaWMHeKvzzE0vODLBrU5njOzW6sm61Ou4js9OdBUXM= +github.com/crossplane/function-sdk-go v0.3.0-rc.0.0.20240628013706-bc16c875629d h1:foFcAZ7sBZp6wFEQUL8sAwEejRewztl6GBQ5P50i/sk= +github.com/crossplane/function-sdk-go v0.3.0-rc.0.0.20240628013706-bc16c875629d/go.mod h1:20eEseResKdP2p+uj+Wn2KyJOvC25R3YInj876n4ncg= github.com/crossplane/upjet v1.1.0-rc.0.0.20231227120826-4cb45f9104ac h1:T1MTxsPAE/Cs0/EAGjeC29H9O/rO81yol2/5qGsf888= github.com/crossplane/upjet v1.1.0-rc.0.0.20231227120826-4cb45f9104ac/go.mod h1:t9etxIdYaxgyvFPBToikm5zBHi8RIpX8N4mTH77lQFM= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -213,6 +217,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= diff --git a/pkl/crossplane.contrib.example/compositions/steps/full.pkl b/pkl/crossplane.contrib.example/compositions/steps/full.pkl index 5e69add..4a9dd30 100644 --- a/pkl/crossplane.contrib.example/compositions/steps/full.pkl +++ b/pkl/crossplane.contrib.example/compositions/steps/full.pkl @@ -77,3 +77,28 @@ context { meta = if (request.meta != null) new { ttl = 60.s } else null + +local observedObj: Object? = request.observed.resources.getOrNull("cm-one")?.resource as Object? +local observedObjSyncedCondition: Object.Condition? = observedObj?.status?.conditions?.ifNonNull( + (conds: Listing) -> conds.toList().findOrNull( + (cond: Object.Condition) -> cond.type == "Synced" + ) +) + +conditions { + if (observedObjSyncedCondition != null && observedObjSyncedCondition.message.contains("connect failed: cannot get provider config")) + new { + target = TARGET_COMPOSITE_AND_CLAIM + status = STATUS_CONDITION_FALSE + message = "The ProviderConfig 'default' is missing" + reason = "ProviderConfig" + type = "Healthy" + } + else + new { + target = TARGET_COMPOSITE_AND_CLAIM + status = STATUS_CONDITION_TRUE + reason = "TestsPassed" + type = "Healthy" + } +} diff --git a/pkl/crossplane.contrib/CompositionResponse.pkl b/pkl/crossplane.contrib/CompositionResponse.pkl index 48b9a36..2076e92 100644 --- a/pkl/crossplane.contrib/CompositionResponse.pkl +++ b/pkl/crossplane.contrib/CompositionResponse.pkl @@ -47,6 +47,41 @@ context: Mapping? /// Requirements that must be satisfied for this Function to run successfully. requirements: Requirements? +/// Status Conditions to be applied to the Composite Resource and sometimes the Claim. +conditions: Listing + +class ResponseMeta { + + /// Time-to-live of this response. Deterministic Functions with no side-effects + /// (e.g. simple templating Functions) may specify a TTL. Crossplane may choose + /// to cache responses until the TTL expires. + ttl: Duration +} + +/// A Result of running a Function. +class Result { + /// Severity of this result. + severity: Severity + + /// Human-readable details about the result. + message: String + + hidden const SEVERITY_UNSPECIFIED: Severity = 0 + hidden const SEVERITY_FATAL: Severity = 1 + hidden const SEVERITY_WARNING: Severity = 2 + hidden const SEVERITY_NORMAL: Severity = 3 +} +typealias Severity = Int(isBetween(0,3)) + +class State { + /// The state of the composite resource (XR). + composite: Resource + + /// The state of any composed resources. + resources: Mapping +} + +/// Requirements that must be satisfied for a Function to run successfully. class Requirements { extraResources: Mapping } @@ -71,6 +106,43 @@ class MatchLabels { labels: Mapping } +/// A Status Condition to be applied to the Composite Resource and sometimes the +/// Claim. For detailed information on proper usage of Conditions, please see +/// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties. +class Condition { + /// Type of condition in CamelCase or in foo.example.com/CamelCase. + type: String + /// Status of the condition. + status: Status = STATUS_CONDITION_UNSPECIFIED + /// Reason contains a programmatic identifier indicating the reason for the + /// condition's last transition. Producers of specific condition types may + /// define expected values and meanings for this field, and whether the values + /// are considered a guaranteed API. The value should be a CamelCase string. + /// This field may not be empty. + reason: String + /// Message is a human readable message indicating details about the + /// transition. This may be an empty string. + message: String? + /// The resources this condition targets. + target: Target? + + hidden const STATUS_CONDITION_UNSPECIFIED: Status = 0 + hidden const STATUS_CONDITION_UNKNOWN: Status = 1 + hidden const STATUS_CONDITION_TRUE: Status = 2 + hidden const STATUS_CONDITION_FALSE: Status = 3 + + /// If the target is unspecified, the result targets the composite resource. + hidden const TARGET_UNSPECIFIED: Target = 0 + /// Target the composite resource. Results that target the composite resource + /// should include detailed, advanced information. + hidden const TARGET_COMPOSITE: Target = 1 + /// Target the composite and the claim. Results that target the composite and + /// the claim should include only end-user friendly information. + hidden const TARGET_COMPOSITE_AND_CLAIM: Target = 2 +} +typealias Status = Int(isBetween(0, 3)) +typealias Target = Int(isBetween(0, 2)) + output { renderer = new YamlRenderer { converters {