github.com/kyma-project/kyma-environment-broker@v0.0.1/docs/user/03-20-runtime-operations.md (about) 1 # SAP BTP, Kyma runtime operations 2 3 Kyma Environment Broker (KEB) allows you to configure operations that you can run on a SAP BTP, Kyma runtime. Each operation consists of several steps and each step is represented by a separate file. As every step can be re-launched multiple times, for each step, you should determine a behavior in case of a processing failure. It can either: 4 5 - Return an error, which interrupts the entire process, or 6 - Repeat the entire operation after the specified period. 7 8 > **NOTE:** It's important to set lower timeouts for the Kyma installation in the Runtime Provisioner. 9 10 ## Provision 11 12 The provisioning process is executed when the instance is created, or an unsuspension is triggered. 13 Each provisioning step is responsible for a separate part of preparing Kyma runtime. For example, in a step you can provide tokens, credentials, or URLs to integrate SAP BTP, Kyma runtime with external systems. 14 You can find all the provisioning steps in the [provisioning](../../cmd/broker/provisioning.go) file. 15 16 > **NOTE:** The timeout for processing this operation is set to `24h`. 17 18 ## Deprovision 19 20 Each deprovisioning step is responsible for a separate part of cleaning Kyma runtime dependencies. To properly deprovision all the dependencies, you need the data used during the Kyma runtime provisioning. The first step finds the previous operation and copies the data. 21 22 None of the deprovisioning steps should block the entire deprovisioning operation. Use the `RetryOperationWithoutFail` function from the `DeprovisionOperationManager` struct to skip a step in case of a retry timeout. Set a 5-minute, at the most, timeout for retries in a step. 23 Only one step may fail the operation, namely `Check_Runtime_Removal`. It fails the operation in case of a timeout while checking for the Provisioner to remove the shoot. 24 Once the step is successfully executed, it isn't retried (every deprovisioning step is defined in a separate stage). If a step has been skipped due to a retry timeout or error, the [Cron Job](../contributor/06-50-deprovision-retrigger-cronjob.md) tries to deprovision all remaining Kyma runtime dependencies again at a scheduled time. 25 You can find all the deprovisioning steps in the [deprovisioning](../../cmd/broker/deprovisioning.go) file. 26 27 > **NOTE:** The timeout for processing this operation is set to `24h`. 28 29 ## Update 30 31 The update process is triggered by an [OSB API update operation](https://github.com/openservicebrokerapi/servicebroker/blob/master/spec.md#updating-a-service-instance) request. 32 You can find all the updating steps in the [update](../../cmd/broker/update.go) file. 33 34 ## Upgrade cluster 35 36 The upgrade cluster process is triggered by upgrade cluster orchestration. 37 You can find all the upgrading cluster steps in the [upgrade_cluster](../../cmd/broker/upgrade_cluster.go) file. 38 39 ## Upgrade Kyma 40 41 The upgrade Kyma process is triggered by upgrade Kyma orchestration. 42 You can find all the upgrading Kyma steps in the [upgrade_kyma](../../cmd/broker/upgrade_kyma.go) file. 43 44 ## Provide additional steps 45 46 You can configure SAP BTP, Kyma runtime operations by providing additional steps. To add a new step, follow these tutorials: 47 48 <div tabs name="runtime-provisioning-deprovisioning" group="runtime-provisioning-deprovisioning"> 49 <details> 50 <summary label="provisioning"> 51 Provisioning 52 </summary> 53 54 1. Create a new file in [this directory](../../internal/process/provisioning). 55 56 2. Implement the following interface in your provisioning or deprovisioning step: 57 58 ```go 59 type Step interface { 60 Name() string 61 Run(operation internal.Operation, logger logrus.FieldLogger) (internal.Operation, time.Duration, error) 62 } 63 ``` 64 65 - `Name()` method returns the name of the step that is used in logs. 66 - `Run()` method implements the functionality of the step. The method receives operations as an argument to which it can add appropriate overrides or save other used variables. 67 68 69 ```go 70 operation.InputCreator.SetOverrides(COMPONENT_NAME, []*gqlschema.ConfigEntryInput{ 71 { 72 Key: "path.to.key", 73 Value: SOME_VALUE, 74 }, 75 { 76 Key: "path.to.secret", 77 Value: SOME_VALUE, 78 Secret: ptr.Bool(true), 79 }, 80 }) 81 ``` 82 83 If your functionality contains long-term processes, you can store data in the storage. 84 To do this, add the following field to the provisioning operation in which you want to save data: 85 86 ```go 87 type Operation struct { 88 89 // These fields are serialized to JSON and stored in the storage 90 RuntimeVersion RuntimeVersionData `json:"runtime_version"` 91 92 // These fields are not stored in the storage 93 InputCreator ProvisionerInputCreator `json:"-"` 94 } 95 ``` 96 97 By saving data in the storage, you can check if you already have the necessary data and avoid time-consuming processes. You must always return the modified operation from the method. 98 99 See the example of the step implementation: 100 101 ```go 102 package provisioning 103 104 import ( 105 "encoding/json" 106 "net/http" 107 "time" 108 109 "github.com/kyma-incubator/compass/components/provisioner/pkg/gqlschema" 110 "github.com/kyma-incubator/compass/components/kyma-environment-broker/internal" 111 "github.com/kyma-incubator/compass/components/kyma-environment-broker/internal/storage" 112 113 "github.com/sirupsen/logrus" 114 ) 115 116 type HelloWorldStep struct { 117 operationStorage storage.Operations 118 client *http.Client 119 } 120 121 type ExternalBodyResponse struct { 122 data string 123 token string 124 } 125 126 func NewHelloWorldStep(operationStorage storage.Operations, client *http.Client) *HelloWorldStep { 127 return &HelloWorldStep{ 128 operationStorage: operationStorage, 129 client: client, 130 } 131 } 132 133 func (s *HelloWorldStep) Name() string { 134 return "Hello_World" 135 } 136 137 // Your step can be repeated in case any other step fails, even if your step has already done its job 138 func (s *HelloWorldStep) Run(operation internal.Operation, log *logrus.Entry) (internal.Operation, time.Duration, error) { 139 log.Info("Start step") 140 141 // Check whether your step should be run or if its job has been done in the previous iteration 142 // All non-save operation data are empty (e.g. InputCreator overrides) 143 144 // Add your logic here 145 146 // Add a call to an external service (optional) 147 response, err := s.client.Get("http://example.com") 148 if err != nil { 149 // Error during a call to an external service may be temporary so you should return time.Duration 150 // All steps will be repeated in X seconds/minutes 151 return operation, 1 * time.Second, nil 152 } 153 defer response.Body.Close() 154 155 body := ExternalBodyResponse{} 156 err = json.NewDecoder(response.Body).Decode(&body) 157 if err != nil { 158 log.Errorf("error: %s", err) 159 // Handle a process failure by returning an error or time.Duration 160 } 161 162 // If a call or any other action is time-consuming, you can save the result in the operation 163 // If you need an extra field in the Operation structure, add it first 164 // In the following step, you can check beforehand if a given value already exists in the operation 165 operation.HelloWorlds = body.data 166 updatedOperation, err := s.operationStorage.UpdateOperation(operation) 167 if err != nil { 168 log.Errorf("error: %s", err) 169 // Handle a process failure by returning an error or time.Duration 170 } 171 172 // If your step finishes with data which should be added to override used during the Runtime provisioning, 173 // add an extra value to operation.InputCreator, then return the updated version of the Application 174 updatedOperation.InputCreator.SetOverrides("component-name", []*gqlschema.ConfigEntryInput{ 175 { 176 Key: "some.key", 177 Value: body.token, 178 }, 179 }) 180 181 // Return the updated version of the Application 182 return *updatedOperation, 0, nil 183 } 184 ``` 185 186 3. Add the step to the [`/cmd/broker/provisioning.go`](../../cmd/broker/provisioning.go) file: 187 188 ```go 189 provisioningSteps := []struct { 190 stage string 191 step provisioning.Step 192 }{ 193 { 194 stage: "create_runtime", 195 step: provisioning.NewHelloWorldStep(db.Operations(), &http.Client{}), 196 }, 197 } 198 ``` 199 200 Once all the steps in the stage have run successfully, the stage is not retried even if the application is restarted. 201 202 </details> 203 204 <details> 205 <summary label="upgrade"> 206 Upgrade 207 </summary> 208 209 1. Create a new file in [this directory](../../internal/process/upgrade_kyma). 210 211 2. Implement the following interface in your upgrade step: 212 213 ```go 214 type Step interface { 215 Name() string 216 Run(operation internal.UpgradeOperation, logger logrus.FieldLogger) (internal.UpgradeOperation, time.Duration, error) 217 } 218 ``` 219 220 - `Name()` method returns the name of the step that is used in logs. 221 - `Run()` method implements the functionality of the step. The method receives operations as an argument to which it can add appropriate overrides or save other used variables. 222 223 224 If your functionality contains long-term processes, you can store data in the storage. 225 To do this, add this field to the upgrade operation in which you want to save data: 226 227 ```go 228 type UpgradeOperation struct { 229 Operation `json:"-"` 230 231 // add additional data here 232 } 233 ``` 234 235 By saving data in the storage, you can check if you already have the necessary data and avoid time-consuming processes. You should always return the modified operation from the method. 236 237 See the example of the step implementation: 238 239 ```go 240 package upgrade 241 242 import ( 243 "encoding/json" 244 "net/http" 245 "time" 246 247 "github.com/kyma-incubator/compass/components/provisioner/pkg/gqlschema" 248 "github.com/kyma-incubator/compass/components/kyma-environment-broker/internal" 249 "github.com/kyma-incubator/compass/components/kyma-environment-broker/internal/storage" 250 251 "github.com/sirupsen/logrus" 252 ) 253 254 type HelloWorldStep struct { 255 operationStorage storage.Operations 256 client *http.Client 257 } 258 259 type ExternalBodyResponse struct { 260 data string 261 token string 262 } 263 264 func NewHelloWorldStep(operationStorage storage.Operations, client *http.Client) *HelloWorldStep { 265 return &HelloWorldStep{ 266 operationStorage: operationStorage, 267 client: client, 268 } 269 } 270 271 func (s *HelloWorldStep) Name() string { 272 return "Hello_World" 273 } 274 275 // Your step can be repeated in case any other step fails, even if your step has already done its job 276 func (s *HelloWorldStep) Run(operation internal.UpgradeOperation, log *logrus.Entry) (internal.UpgradeOperation, time.Duration, error) { 277 log.Info("Start step") 278 279 // Check whether your step should be run or if its job has been done in the previous iteration 280 // All non-save operation data are empty (e.g. InputCreator overrides) 281 282 // Add your logic here 283 284 // Add a call to an external service (optional) 285 response, err := s.client.Get("http://example.com") 286 if err != nil { 287 // Error during a call to an external service may be temporary so you should return time.Duration 288 // All steps will be repeated in X seconds/minutes 289 return operation, 1 * time.Second, nil 290 } 291 defer response.Body.Close() 292 293 body := ExternalBodyResponse{} 294 err = json.NewDecoder(response.Body).Decode(&body) 295 if err != nil { 296 log.Errorf("error: %s", err) 297 // Handle a process failure by returning an error or time.Duration 298 } 299 300 // If a call or any other action is time-consuming, you can save the result in the operation 301 // If you need an extra field in the UpgradeOperation structure, add it first 302 // in the step below; beforehand, you can check if a given value already exists in the operation 303 operation.HelloWorlds = body.data 304 updatedOperation, err := s.operationStorage.UpdateUpgradeOperation(operation) 305 if err != nil { 306 log.Errorf("error: %s", err) 307 // Handle a process failure by returning an error or time.Duration 308 } 309 310 // If your step finishes with data which should be added to override used during the Runtime upgrade, 311 // add an extra value to operation.InputCreator, then return the updated version of the Application 312 updatedOperation.InputCreator.SetOverrides("component-name", []*gqlschema.ConfigEntryInput{ 313 { 314 Key: "some.key", 315 Value: body.token, 316 }, 317 }) 318 319 // Return the updated version of the Application 320 return *updatedOperation, 0, nil 321 } 322 ``` 323 324 3. Add the step to the [`/cmd/broker/upgrade_cluster.go`](../../cmd/broker/upgrade_cluster.go) or [`/cmd/broker/upgrade_kyma.go`](../../cmd/broker/upgrade_kyma.go) file: 325 326 ```go 327 upgradeSteps := []struct { 328 weight int 329 step upgrade_kyma.Step 330 }{ 331 { 332 weight: 1, 333 step: upgrade_kyma.NewHelloWorldStep(db.Operations(), &http.Client{}), 334 }, 335 } 336 ``` 337 338 </details> 339 </div> 340 341 ## Stages 342 343 An operation defines stages and steps which represent the work you must do. A stage is a grouping unit for steps. A step is a part of a stage. An operation can consist of multiple stages, and a stage can consist of multiple steps. You group steps in a stage when you have some sensitive data which you don't want to store in database. In such a case you temporarily store the sensitive data in the memory and go through the steps. Once all the steps in a stage are successfully executed, the stage is marked as finished and never repeated again, even if the next one fails. If any steps fail at a given stage, the whole stage is repeated from the beginning.