github.com/crossplane/upjet@v1.3.0/docs/adding-support-for-management-policies.md (about) 1 <!-- 2 SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io> 3 4 SPDX-License-Identifier: CC-BY-4.0 5 --> 6 7 # Adding Support for Management Policies and initProvider 8 9 ## Regenerating a provider with Management Policies 10 11 Check out the provider repo, e.g., upbound/provider-aws, and go to the project 12 directory on your local machine. 13 14 1. Generate with management policy and update crossplane-runtime dependency: 15 16 ```bash 17 # Consume the latest crossplane-tools: 18 go get github.com/crossplane/crossplane-tools@master 19 go mod tidy 20 # Generate getters/setters for management policies 21 make generate 22 23 # Consume the latest crossplane-runtime: 24 go get github.com/crossplane/crossplane-runtime@master 25 go mod tidy 26 ``` 27 28 1. Introduce a feature flag for `Management Policies`. 29 30 Add the feature flag definition into the `internal/features/features.go` 31 file. 32 33 ```diff 34 diff --git a/internal/features/features.go b/internal/features/features.go 35 index 9c6b1fc8..de261ca4 100644 36 --- a/internal/features/features.go 37 +++ b/internal/features/features.go 38 @@ -12,4 +12,9 @@ const ( 39 // External Secret Stores. See the below design for more details. 40 // https://github.com/crossplane/crossplane/blob/390ddd/design/design-doc-external-secret-stores.md 41 EnableAlphaExternalSecretStores feature.Flag = "EnableAlphaExternalSecretStores" 42 + 43 + // EnableAlphaManagementPolicies enables alpha support for 44 + // Management Policies. See the below design for more details. 45 + // https://github.com/crossplane/crossplane/pull/3531 46 + EnableAlphaManagementPolicies feature.Flag = "EnableAlphaManagementPolicies" 47 ) 48 ``` 49 50 Add the actual flag in `cmd/provider/main.go` file and pass the flag to the 51 workspace store: 52 53 ```diff 54 diff --git a/cmd/provider/main.go b/cmd/provider/main.go 55 index 669b01f9..a60df983 100644 56 --- a/cmd/provider/main.go 57 +++ b/cmd/provider/main.go 58 @@ -48,6 +48,7 @@ func main() { 59 60 namespace = app.Flag("namespace", "Namespace used to set as default scope in default secret store config.").Default("crossplane-system").Envar("POD_NAMESPACE").String() 61 enableExternalSecretStores = app.Flag("enable-external-secret-stores", "Enable support for ExternalSecretStores.").Default("false").Envar("ENABLE_EXTERNAL_SECRET_STORES").Bool() 62 + enableManagementPolicies = app.Flag("enable-management-policies", "Enable support for Management Policies.").Default("false").Envar("ENABLE_MANAGEMENT_POLICIES").Bool() 63 ) 64 65 kingpin.MustParse(app.Parse(os.Args[1:])) 66 @@ -122,6 +123,11 @@ func main() { 67 })), "cannot create default store config") 68 } 69 terraform.WithSharedProviderOptions(terraform.WithNativeProviderPath(*setupConfig.NativeProviderPath), terraform.WithNativeProviderName("registry.terraform.io/"+*setupConfig.NativeProviderSource))) 70 } 71 72 + featureFlags := &feature.Flags{} 73 o := tjcontroller.Options{ 74 Options: xpcontroller.Options{ 75 Logger: log, 76 GlobalRateLimiter: ratelimiter.NewGlobal(*maxReconcileRate), 77 PollInterval: *pollInterval, 78 MaxConcurrentReconciles: *maxReconcileRate, 79 - Features: &feature.Flags{}, 80 + Features: featureFlags, 81 }, 82 83 Provider: config.GetProvider(), 84 - WorkspaceStore: terraform.NewWorkspaceStore(log, terraform.WithDisableInit(len(*setupConfig.NativeProviderPath) != 0), terraform.WithProcessReportInterval(*pollInterval)), 85 + WorkspaceStore: terraform.NewWorkspaceStore(log, terraform.WithDisableInit(len(*setupConfig.NativeProviderPath) != 0), terraform.WithProcessReportInterval(*pollInterval), terraform.WithFeatures(featureFlags)), 86 SetupFn: clients.SelectTerraformSetup(log, setupConfig), 87 EventHandler: eventHandler, 88 } 89 90 + if *enableManagementPolicies { 91 + o.Features.Enable(features.EnableAlphaManagementPolicies) 92 + log.Info("Alpha feature enabled", "flag", features.EnableAlphaManagementPolicies) 93 + } 94 + 95 kingpin.FatalIfError(controller.Setup(mgr, o), "Cannot setup AWS controllers") 96 kingpin.FatalIfError(mgr.Start(ctrl.SetupSignalHandler()), "Cannot start controller manager") 97 } 98 ``` 99 100 > [!NOTE] 101 > If the provider was already updated to support observe-only resources, just 102 add the feature flag to the `workspaceStore`. 103 104 1. Generate with the latest upjet and management policies: 105 106 ```bash 107 # Bump to the latest upjet 108 go get github.com/crossplane/upjet@main 109 go mod tidy 110 ``` 111 112 Enable management policies in the generator by adding 113 `config.WithFeaturesPackage` option: 114 115 ```diff 116 diff --git a/config/provider.go b/config/provider.go 117 index 964883670..1c06a53e2 100644 118 --- a/config/provider.go 119 +++ b/config/provider.go 120 @@ -141,6 +141,7 @@ func GetProvider() *config.Provider { 121 config.WithReferenceInjectors([]config.ReferenceInjector{reference.NewInjector(modulePath)}), 122 config.WithSkipList(skipList), 123 config.WithBasePackages(BasePackages), 124 + config.WithFeaturesPackage("internal/features"), 125 config.WithDefaultResourceOptions( 126 GroupKindOverrides(), 127 KindOverrides(), 128 ``` 129 130 Generate: 131 132 ```bash 133 make generate 134 ``` 135 136 ## Testing: Locally Running the Provider with Management Policies Enabled 137 138 1. Create a fresh Kubernetes cluster. 139 1. Apply all of the provider's CRDs with `kubectl apply -f package/crds`. 140 1. Run the provider with `--enable-management-policies`. 141 142 You can update the `run` target in the Makefile as below 143 144 ```diff 145 diff --git a/Makefile b/Makefile 146 index d529a0d6..84411669 100644 147 --- a/Makefile 148 +++ b/Makefile 149 @@ -111,7 +111,7 @@ submodules: 150 run: go.build 151 @$(INFO) Running Crossplane locally out-of-cluster . . . 152 @# To see other arguments that can be provided, run the command with --help instead 153 - UPBOUND_CONTEXT="local" $(GO_OUT_DIR)/provider --debug 154 + UPBOUND_CONTEXT="local" $(GO_OUT_DIR)/provider --debug --enable-management-policies 155 ``` 156 157 and run with: 158 159 ```shell 160 make run 161 ``` 162 163 1. Create some resources in the provider's management console and try observing 164 them by creating a managed resource with `managementPolicies: ["Observe"]`. 165 166 For example: 167 168 ```yaml 169 apiVersion: rds.aws.upbound.io/v1beta1 170 kind: Instance 171 metadata: 172 name: an-existing-dbinstance 173 spec: 174 managementPolicies: ["Observe"] 175 forProvider: 176 region: us-west-1 177 ``` 178 179 You should see the managed resource is ready & synced: 180 181 ```bash 182 NAME READY SYNCED EXTERNAL-NAME AGE 183 an-existing-dbinstance True True an-existing-dbinstance 3m 184 ``` 185 186 and the `status.atProvider` is updated with the actual state of the resource: 187 188 ```bash 189 kubectl get instance.rds.aws.upbound.io an-existing-dbinstance -o yaml 190 ``` 191 192 > [!NOTE] 193 > You need the `terraform` executable installed on your local machine. 194 195 1. Create a managed resource without `LateInitialize` like 196 `managementPolicies: ["Observe", "Create", "Update", "Delete"]` with 197 `spec.initProvider` fields to see the provider create the resource with 198 combining `spec.initProvider` and `spec.forProvider` fields: 199 200 For example: 201 202 ```yaml 203 apiVersion: dynamodb.aws.upbound.io/v1beta1 204 kind: Table 205 metadata: 206 name: example 207 annotations: 208 meta.upbound.io/example-id: dynamodb/v1beta1/table 209 spec: 210 managementPolicies: ["Observe", "Create", "Update", "Delete"] 211 initProvider: 212 writeCapacity: 20 213 readCapacity: 19 214 forProvider: 215 region: us-west-1 216 attribute: 217 - name: UserId 218 type: S 219 - name: GameTitle 220 type: S 221 - name: TopScore 222 type: "N" 223 billingMode: PROVISIONED 224 globalSecondaryIndex: 225 - hashKey: GameTitle 226 name: GameTitleIndex 227 nonKeyAttributes: 228 - UserId 229 projectionType: INCLUDE 230 rangeKey: TopScore 231 readCapacity: 10 232 writeCapacity: 10 233 hashKey: UserId 234 rangeKey: GameTitle 235 ``` 236 237 You should see the managed resource is ready & synced: 238 239 ```bash 240 NAME READY SYNCED EXTERNAL-NAME AGE 241 example True True example 3m 242 ``` 243 244 and the `status.atProvider` is updated with the actual state of the resource, 245 including the `initProvider` fields: 246 247 ```bash 248 kubectl get tables.dynamodb.aws.upbound.io example -o yaml 249 ``` 250 251 As the late initialization is skipped, the `spec.forProvider` should be the 252 same when we created the resource. 253 254 In the provider console, you should see that the resource was created with 255 the values in the `initProvider` field.