├── .gitignore ├── .travis.yml ├── README.md └── hack └── ci └── marker /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: bash 2 | 3 | script: ./hack/ci/marker 4 | 5 | after_success: echo 'Markdown links are correct' 6 | after_failure: echo 'Incorrect markdown link detected' 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [DEPRECATED] Getting Started 2 | 3 | This project is deprecated. Documentation for the Operator Framework has moved to https://operatorframework.io/ 4 | 5 | - [Overview](#overview) 6 | - [Build an operator using the Operator SDK](#build-an-operator-using-the-operator-sdk) 7 | - [Create a new project](#create-a-new-project) 8 | - [Manager](#manager) 9 | - [Add a new Custom Resource Definition](#add-a-new-custom-resource-definition) 10 | - [Define the Memcached spec and status](#define-the-memcached-spec-and-status) 11 | - [Add a new Controller](#add-a-new-controller) 12 | - [Resources watched by the Controller](#resources-watched-by-the-controller) 13 | - [Reconcile loop](#reconcile-loop) 14 | - [Build and run the operator](#build-and-run-the-operator) 15 | - [1. Run as a Deployment inside the cluster](#1-run-as-a-deployment-inside-the-cluster) 16 | - [2. Run locally outside the cluster](#2-run-locally-outside-the-cluster) 17 | - [Create a Memcached CR](#create-a-memcached-cr) 18 | - [Update the size](#update-the-size) 19 | - [Cleanup](#cleanup) 20 | - [Reference implementation](#reference-implementation) 21 | - [Manage the operator using the Operator Lifecycle Manager](#manage-the-operator-using-the-operator-lifecycle-manager) 22 | - [Generate an operator manifest](#generate-an-operator-manifest) 23 | - [Testing locally](#testing-locally) 24 | - [Promoting operator standards](#promoting-operator-standards) 25 | - [Conclusion](#conclusion) 26 | 27 | ## Overview 28 | 29 | The [Operator Framework][org_operator_framework] ([intro blog post][site_blog_post]) is an open source toolkit to manage Kubernetes native applications, called operators, in an effective, automated, and scalable way. Operators take advantage of Kubernetes's extensibility to deliver the automation advantages of cloud services like provisioning, scaling, and backup/restore while being able to run anywhere that Kubernetes can run. 30 | 31 | This guide shows how to build a simple [memcached][site_memcached] operator and how to manage its lifecycle from install to update to a new version. For that, we will use two center pieces of the framework: 32 | 33 | * **Operator SDK**: Allows your developers to build an operator based on your expertise without requiring knowledge of Kubernetes API complexities. 34 | * **Operator Lifecycle Manager**: Helps you to install, update, and generally manage the lifecycle of all of the operators (and their associated services) running across your clusters. 35 | 36 | ## Build an operator using the Operator SDK 37 | 38 | **BEFORE YOU BEGIN:** links to the Operator SDK repo in this document are pinned to the `master` branch. Make sure you update the link such that it points to the correct Operator SDK repo version, which should match this repo's version or the `operator-sdk version` being used. For example, if you are using `operator-sdk` v0.12.0, update all links from this repo to the SDK repo with `master -> v0.12.0`. Otherwise you may see incorrect information. 39 | 40 | The Operator SDK makes it easier to build Kubernetes native applications, a process that can require deep, application-specific operational knowledge. The SDK not only lowers that barrier, but it also helps reduce the amount of boilerplate code needed for many common management capabilities, such as metering or monitoring. 41 | 42 | This section walks through an example of building a simple memcached operator using tools and libraries provided by the Operator SDK. This walkthrough is not exhaustive; for an in-depth explanation of these steps, see the SDK's [user guide][doc_sdk_user_guide]. 43 | 44 | **Requirements**: Please make sure that the Operator SDK is [installed][doc_sdk_install_instr] on the development machine. Additionally, the Operator Lifecycle Manager must be [installed][doc_olm_install_instr] in the cluster (1.8 or above to support the apps/v1beta2 API group) before running this guide. 45 | 46 | ### Create a new project 47 | 48 | 1. Use the CLI to create a new `memcached-operator` project: 49 | 50 | 51 | ```sh 52 | $ mkdir -p $GOPATH/src/github.com/example-inc/ 53 | $ cd $GOPATH/src/github.com/example-inc/ 54 | $ export GO111MODULE=on 55 | $ operator-sdk new memcached-operator 56 | $ cd memcached-operator 57 | ``` 58 | 59 | This creates the `memcached-operator` project. 60 | 61 | 2. Install dependencies by running `go mod tidy` 62 | 63 | **NOTE:** Learn more about the project directory structure from the SDK [project layout][layout_doc] documentation. 64 | 65 | ### Manager 66 | 67 | The main program for the operator `cmd/manager/main.go` initializes and runs the [Manager][manager_go_doc]. 68 | 69 | The Manager will automatically register the scheme for all custom resources defined under `pkg/apis/...` and run all controllers under `pkg/controller/...`. 70 | 71 | The Manager can restrict the namespace that all controllers will watch for resources: 72 | 73 | ```Go 74 | mgr, err := manager.New(cfg, manager.Options{ 75 | Namespace: namespace, 76 | }) 77 | ``` 78 | 79 | By default this will be the namespace that the operator is running in. To watch all namespaces leave the namespace option empty: 80 | 81 | ```Go 82 | mgr, err := manager.New(cfg, manager.Options{ 83 | Namespace: "", 84 | }) 85 | ``` 86 | 87 | ## Add a new Custom Resource Definition 88 | 89 | Add a new Custom Resource Definition (CRD) API called `Memcached`, with APIVersion `cache.example.com/v1alpha1` and Kind `Memcached`. 90 | 91 | ```sh 92 | $ operator-sdk add api --api-version=cache.example.com/v1alpha1 --kind=Memcached 93 | ``` 94 | 95 | This will scaffold the `Memcached` resource API under `pkg/apis/cache/v1alpha1/...`. 96 | 97 | ### Define the Memcached spec and status 98 | 99 | Modify the spec and status of the `Memcached` Custom Resource (CR) at `pkg/apis/cache/v1alpha1/memcached_types.go`: 100 | 101 | ```Go 102 | type MemcachedSpec struct { 103 | // Size is the size of the memcached deployment 104 | Size int32 `json:"size"` 105 | } 106 | type MemcachedStatus struct { 107 | // Nodes are the names of the memcached pods 108 | Nodes []string `json:"nodes"` 109 | } 110 | ``` 111 | 112 | After modifying the `*_types.go` file always run the following command to update the generated code for that resource type: 113 | 114 | ```sh 115 | $ operator-sdk generate k8s 116 | ``` 117 | 118 | Also run the following command in order to automatically generate the CRDs: 119 | 120 | ```sh 121 | $ operator-sdk generate crds 122 | ``` 123 | 124 | You can see the changes applied in `deploy/crds/cache.example.com_memcacheds_crd.yaml` 125 | 126 | ## Add a new Controller 127 | 128 | Add a new [Controller][controller_go_doc] to the project that will watch and reconcile the `Memcached` resource: 129 | 130 | ```sh 131 | $ operator-sdk add controller --api-version=cache.example.com/v1alpha1 --kind=Memcached 132 | ``` 133 | 134 | This will scaffold a new Controller implementation under `pkg/controller/memcached/...`. 135 | 136 | For this example replace the generated Controller file `pkg/controller/memcached/memcached_controller.go` with the example [`memcached_controller.go`][memcached_controller] implementation. 137 | 138 | The example Controller executes the following reconciliation logic for each `Memcached` CR: 139 | 140 | * Create a memcached Deployment if it doesn't exist 141 | * Ensure that the Deployment size is the same as specified by the `Memcached` CR spec 142 | * Update the `Memcached` CR status with the names of the memcached pods 143 | 144 | The next two subsections explain how the Controller watches resources and how the reconcile loop is triggered. Skip to the [Build](#build-and-run-the-operator) section to see how to build and run the operator. 145 | 146 | ### Resources watched by the Controller 147 | 148 | Inspect the Controller implementation at `pkg/controller/memcached/memcached_controller.go` to see how the Controller watches resources. 149 | 150 | The first watch is for the `Memcached` type as the primary resource. For each Add/Update/Delete event the reconcile loop will be sent a reconcile `Request` (a namespace/name key) for that `Memcached` object: 151 | 152 | ```Go 153 | err := c.Watch( 154 | &source.Kind{Type: &cachev1alpha1.Memcached{}}, 155 | &handler.EnqueueRequestForObject{}, 156 | ) 157 | ``` 158 | 159 | The next watch is for Deployments but the event handler will map each event to a reconcile `Request` for the owner of the Deployment. Which in this case is the `Memcached` object for which the Deployment was created. This allows the controller to watch Deployments as a secondary resource. 160 | 161 | ```Go 162 | err := c.Watch( 163 | &source.Kind{Type: &appsv1.Deployment{}}, 164 | &handler.EnqueueRequestForOwner{ 165 | IsController: true, 166 | OwnerType: &cachev1alpha1.Memcached{}}, 167 | ) 168 | ``` 169 | 170 | ### Reconcile loop 171 | 172 | Every Controller has a Reconciler object with a `Reconcile()` method that implements the reconcile loop. The reconcile loop is passed the [`Request`][request_go_doc] argument which is a Namespace/Name key used to lookup the primary resource object, `Memcached`, from the cache: 173 | 174 | ```Go 175 | func (r *ReconcileMemcached) Reconcile(request reconcile.Request) (reconcile.Result, error) { 176 | // Lookup the Memcached instance for this reconcile request 177 | memcached := &cachev1alpha1.Memcached{} 178 | err := r.client.Get(context.TODO(), request.NamespacedName, memcached) 179 | ... 180 | } 181 | ``` 182 | 183 | For a guide on Reconcilers, Clients, and interacting with resource Events, see the [Client API doc][doc_client_api]. 184 | 185 | ## Build and run the operator 186 | 187 | Before running the operator, the CRD must be registered with the Kubernetes apiserver: 188 | 189 | ```sh 190 | $ kubectl create -f deploy/crds/cache.example.com_memcacheds_crd.yaml 191 | ``` 192 | 193 | Once this is done, there are two ways to run the operator: 194 | 195 | * As a Deployment inside a Kubernetes cluster 196 | * As Go program outside a cluster 197 | 198 | ### 1. Run as a Deployment inside the cluster 199 | 200 | Build the memcached-operator image and push it to your registry. The following example uses https://quay.io as the registry. 201 | 202 | ```sh 203 | $ operator-sdk build quay.io//memcached-operator:v0.0.1 204 | $ sed -i 's|REPLACE_IMAGE|quay.io//memcached-operator:v0.0.1|g' deploy/operator.yaml 205 | $ docker push quay.io//memcached-operator:v0.0.1 206 | ``` 207 | 208 | **Note** 209 | If you are performing these steps on OSX, use the following `sed` command instead: 210 | 211 | ```sh 212 | $ sed -i "" 's|REPLACE_IMAGE|quay.io//memcached-operator:v0.0.1|g' deploy/operator.yaml 213 | ``` 214 | 215 | The above command will replace the string `REPLACE_IMAGE` with the `:` built above. Afterwards, verify that your `operator.yaml` file was updated successfully. 216 | 217 | ```yaml 218 | serviceAccountName: memcached-operator 219 | containers: 220 | - name: memcached-operator 221 | # Replace this with the built image name 222 | image: quay.io//memcached-operator:v0.0.1 223 | command: 224 | - memcached-operator 225 | imagePullPolicy: Always 226 | ``` 227 | 228 | **IMPORTANT:** Ensure that your cluster is able to pull the image pushed to your registry. 229 | 230 | The Deployment manifest is generated at `deploy/operator.yaml`. Be sure to update the deployment image as shown above since the default is just a placeholder. 231 | 232 | Setup RBAC and deploy the memcached-operator: 233 | 234 | ```sh 235 | $ kubectl create -f deploy/service_account.yaml 236 | $ kubectl create -f deploy/role.yaml 237 | $ kubectl create -f deploy/role_binding.yaml 238 | $ kubectl create -f deploy/operator.yaml 239 | ``` 240 | 241 | **NOTE:** To apply the RBAC you need to be logged in `system:admin`. (E.g. By using for OCP: `oc login -u system:admin.`) 242 | 243 | Verify that the `memcached-operator` Deployment is up and running: 244 | 245 | ```sh 246 | $ kubectl get deployment 247 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 248 | memcached-operator 1 1 1 1 1m 249 | ``` 250 | 251 | Verify that the `memcached-operator` pod is up and running: 252 | 253 | ```sh 254 | $ kubectl get pod 255 | NAME READY STATUS RESTARTS AGE 256 | memcached-operator-7d76948766-nrcp7 1/1 Running 0 44s 257 | ``` 258 | 259 | **IMPORTANT:** Ensure that you built and pushed the image, and updated the `operator.yaml` file. 260 | 261 | Verify that the operator is running successfully by checking its logs. 262 | 263 | ```sh 264 | $ kubectl logs memcached-operator-7d76948766-nrcp7 265 | {"level":"info","ts":1580855834.104447,"logger":"cmd","msg":"Operator Version: 0.0.1"} 266 | {"level":"info","ts":1580855834.1044931,"logger":"cmd","msg":"Go Version: go1.13.6"} 267 | {"level":"info","ts":1580855834.104505,"logger":"cmd","msg":"Go OS/Arch: linux/amd64"} 268 | {"level":"info","ts":1580855834.1045163,"logger":"cmd","msg":"Version of operator-sdk: v0.15.1"} 269 | {"level":"info","ts":1580855834.1049826,"logger":"leader","msg":"Trying to become the leader."} 270 | {"level":"info","ts":1580855834.4423697,"logger":"leader","msg":"No pre-existing lock was found."} 271 | {"level":"info","ts":1580855834.447401,"logger":"leader","msg":"Became the leader."} 272 | {"level":"info","ts":1580855834.7494223,"logger":"controller-runtime.metrics","msg":"metrics server is starting to listen","addr":"0.0.0.0:8383"} 273 | {"level":"info","ts":1580855834.7497423,"logger":"cmd","msg":"Registering Components."} 274 | {"level":"info","ts":1580855835.3955405,"logger":"metrics","msg":"Metrics Service object created","Service.Name":"memcached-operator-metrics","Service.Namespace":"default"} 275 | {"level":"info","ts":1580855835.7000446,"logger":"cmd","msg":"Could not create ServiceMonitor object","error":"no ServiceMonitor registered with the API"} 276 | {"level":"info","ts":1580855835.7005095,"logger":"cmd","msg":"Install prometheus-operator in your cluster to create ServiceMonitor objects","error":"no ServiceMonitor registered with the API"} 277 | {"level":"info","ts":1580855835.7007008,"logger":"cmd","msg":"Starting the Cmd."} 278 | {"level":"info","ts":1580855835.7014875,"logger":"controller-runtime.manager","msg":"starting metrics server","path":"/metrics"} 279 | {"level":"info","ts":1580855835.702304,"logger":"controller-runtime.controller","msg":"Starting EventSource","controller":"memcached-controller","source":"kind source: /, Kind="} 280 | {"level":"info","ts":1580855835.803201,"logger":"controller-runtime.controller","msg":"Starting EventSource","controller":"memcached-controller","source":"kind source: /, Kind="} 281 | {"level":"info","ts":1580855835.9041016,"logger":"controller-runtime.controller","msg":"Starting Controller","controller":"memcached-controller"} 282 | {"level":"info","ts":1580855835.9044445,"logger":"controller-runtime.controller","msg":"Starting workers","controller":"memcached-controller","worker count":1} 283 | ``` 284 | 285 | The following error will occur if your cluster was unable to pull the image: 286 | 287 | ```sh 288 | $ kubectl get pod 289 | NAME READY STATUS RESTARTS AGE 290 | memcached-operator-6b5dc697fb-t62cv 0/1 ImagePullBackOff 0 2m 291 | ``` 292 | 293 | Following the logs in the error scenario described above. 294 | 295 | ```sh 296 | $ kubectl logs memcached-operator-6b5dc697fb-t62cv 297 | Error from server (BadRequest): container "memcached-operator" in pod "memcached-operator-6b5dc697fb-t62cv" is waiting to start: image can't be pulled 298 | ``` 299 | 300 | **NOTE:** Just for tests purposes make the image public and setting up the cluster to allow use insecure registry. ( E.g `--insecure-registry 172.30.0.0/16` ) 301 | 302 | ### 2. Run locally outside the cluster 303 | 304 | This method is preferred during development cycle to deploy and test faster. 305 | 306 | Run the operator locally with the default kubernetes config file present at `$HOME/.kube/config`: 307 | 308 | ```sh 309 | $ operator-sdk run local 310 | INFO[0000] Running the operator locally; watching namespace "default" 311 | {"level":"info","ts":1593777657.892013,"logger":"cmd","msg":"Operator Version: 0.0.1"} 312 | {"level":"info","ts":1593777657.892079,"logger":"cmd","msg":"Go Version: go1.14.4"} 313 | {"level":"info","ts":1593777657.892084,"logger":"cmd","msg":"Go OS/Arch: darwin/amd64"} 314 | {"level":"info","ts":1593777657.892087,"logger":"cmd","msg":"Version of operator-sdk: v0.18.2"} 315 | ... 316 | ``` 317 | 318 | You can use a specific kubeconfig via the flag `--kubeconfig=`. 319 | 320 | ## Create a Memcached CR 321 | 322 | Create the example `Memcached` CR that was generated at `deploy/crds/cache.example.com_v1alpha1_memcached_cr.yaml`: 323 | 324 | ```sh 325 | $ cat deploy/crds/cache.example.com_v1alpha1_memcached_cr.yaml 326 | apiVersion: "cache.example.com/v1alpha1" 327 | kind: "Memcached" 328 | metadata: 329 | name: "example-memcached" 330 | spec: 331 | size: 3 332 | 333 | $ kubectl apply -f deploy/crds/cache.example.com_v1alpha1_memcached_cr.yaml 334 | ``` 335 | 336 | Ensure that the `memcached-operator` creates the deployment for the CR: 337 | 338 | ```sh 339 | $ kubectl get deployment 340 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 341 | memcached-operator 1 1 1 1 2m 342 | example-memcached 3 3 3 3 1m 343 | ``` 344 | 345 | Check the pods and CR status to confirm the status is updated with the memcached pod names: 346 | 347 | ```sh 348 | $ kubectl get pods 349 | NAME READY STATUS RESTARTS AGE 350 | example-memcached-6fd7c98d8-7dqdr 1/1 Running 0 1m 351 | example-memcached-6fd7c98d8-g5k7v 1/1 Running 0 1m 352 | example-memcached-6fd7c98d8-m7vn7 1/1 Running 0 1m 353 | memcached-operator-7cc7cfdf86-vvjqk 1/1 Running 0 2m 354 | ``` 355 | 356 | ```sh 357 | $ kubectl get memcached/example-memcached -o yaml 358 | apiVersion: cache.example.com/v1alpha1 359 | kind: Memcached 360 | metadata: 361 | clusterName: "" 362 | creationTimestamp: 2018-03-31T22:51:08Z 363 | generation: 0 364 | name: example-memcached 365 | namespace: default 366 | resourceVersion: "245453" 367 | selfLink: /apis/cache.example.com/v1alpha1/namespaces/default/memcacheds/example-memcached 368 | uid: 0026cc97-3536-11e8-bd83-0800274106a1 369 | spec: 370 | size: 3 371 | status: 372 | nodes: 373 | - example-memcached-6fd7c98d8-7dqdr 374 | - example-memcached-6fd7c98d8-g5k7v 375 | - example-memcached-6fd7c98d8-m7vn7 376 | ``` 377 | 378 | ### Update the size 379 | 380 | Change the `spec.size` field in the memcached CR from 3 to 4 and apply the change: 381 | 382 | ```sh 383 | $ cat deploy/crds/cache.example.com_v1alpha1_memcached_cr.yaml 384 | apiVersion: "cache.example.com/v1alpha1" 385 | kind: "Memcached" 386 | metadata: 387 | name: "example-memcached" 388 | spec: 389 | size: 4 390 | 391 | $ kubectl apply -f deploy/crds/cache.example.com_v1alpha1_memcached_cr.yaml 392 | ``` 393 | 394 | Confirm that the operator changes the deployment size: 395 | 396 | ```sh 397 | $ kubectl get deployment 398 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 399 | example-memcached 4 4 4 4 5m 400 | ``` 401 | 402 | ### Cleanup 403 | 404 | Delete the operator and its related resources: 405 | 406 | ```sh 407 | $ kubectl delete -f deploy/crds/cache.example.com_v1alpha1_memcached_cr.yaml 408 | $ kubectl delete -f deploy/operator.yaml 409 | $ kubectl delete -f deploy/role_binding.yaml 410 | $ kubectl delete -f deploy/role.yaml 411 | $ kubectl delete -f deploy/service_account.yaml 412 | $ kubectl delete -f deploy/crds/cache.example.com_memcacheds_crd.yaml 413 | ``` 414 | 415 | ## Reference implementation 416 | 417 | The above walkthrough follows a similar implementation process to the one used to produce the `memcached-operator` in the SDK [samples repo][repo_sdk_samples_memcached]. 418 | 419 | ## Manage the operator using the Operator Lifecycle Manager 420 | 421 | > NOTE: This section of the Getting Started Guide is out-of-date. 422 | > We're working on some improvements to Operator SDK to streamline 423 | > the experience of using OLM. For further information see, for example, this enhancement [proposal][sdk-integration-with-olm-doc]. 424 | > In the meantime, you might find the following documentation helpful: 425 | 426 | The previous section has covered manually running an operator. In the next sections, we will explore using the [Operator Lifecycle Manager][operator_lifecycle_manager] (OLM) which is what enables a more robust deployment model for operators being run in production environments. 427 | 428 | OLM helps you to install, update, and generally manage the lifecycle of all of the operators (and their associated services) on a Kubernetes cluster. It runs as an Kubernetes extension and lets you use `kubectl` for all the lifecycle management functions without any additional tools. 429 | 430 | **NOTE:** Various public, OLM-ready operator projects are available at [operatorhub.io][operator-hub-io]. 431 | 432 | ### Generate an operator manifest 433 | 434 | The first step to leveraging OLM is to create a [Cluster Service Version][csv_design_doc] (CSV) manifest. An operator manifest describes how to display, create and manage the application, in this case memcached, as a whole. It is required for OLM to function. 435 | 436 | The Operator SDK CLI can generate CSV manifests via the following command: 437 | 438 | ```console 439 | $ operator-sdk generate csv --csv-version 0.0.1 --update-crds 440 | ``` 441 | 442 | Several fields must be updated after generating the CSV. See the CSV generation doc for a list of [required fields][csv-fields], and the memcached-operator [CSV][memcached_csv] for an example of a complete CSV. 443 | 444 | **NOTE:** You are able to preview and validate your CSV manifest syntax in the [operatorhub.io CSV Preview][operator-hub-io-preview] tool. 445 | 446 | ### Testing locally 447 | 448 | The next step is to ensure your project deploys correctly with OLM and runs as expected. Follow this [testing guide][testing-operators] to deploy and test your operator. 449 | 450 | **NOTE:** Also, check out some of the new OLM integrations in operator-sdk: 451 | 452 | - [`operator-sdk olm`][sdk-olm-cli] to install and manage an OLM installation in your cluster. 453 | - [`operator-sdk run --olm`][sdk-run-cli] to run your operator using the CSV generated by `operator-sdk generate csv`. 454 | - [`operator-sdk bundle`][sdk-bundle-cli] to create and validate operator bundle images. 455 | 456 | ### Promoting operator standards 457 | 458 | We recommend running `operator-sdk scorecard` against your operator to see whether your operator's OLM integration follows best practices. For further information on running the scorecard and results, see the [scorecard documentation][scorecard-doc]. 459 | 460 | **NOTE:** the scorecard is undergoing changes to give informative and helpful feedback. The original scorecard functionality will still be available while and after changes are made. 461 | 462 | ## Conclusion 463 | 464 | Hopefully, this guide was an effective demonstration of the value of the Operator Framework for building and managing operators. There is much more that we left out in the interest of brevity. The Operator Framework and its components are open source, so please feel encouraged to jump into each individually and learn what else you can do. If you want to discuss your experience, have questions, or want to get involved, join the Operator Framework [mailing list][mailing_list]. 465 | 466 | 467 | 468 | [operator_group_doc]: https://github.com/operator-framework/operator-lifecycle-manager/blob/master/doc/design/operatorgroups.md 469 | [csv_design_doc]: https://github.com/operator-framework/operator-lifecycle-manager/blob/master/doc/design/building-your-csv.md 470 | [csv_generation_doc]: https://github.com/operator-framework/operator-sdk/blob/master/doc/user/olm-catalog/generating-a-csv.md 471 | [org_operator_framework]: https://github.com/operator-framework/ 472 | [site_blog_post]: https://coreos.com/blog/introducing-operator-framework 473 | [operator_sdk]: https://github.com/operator-framework/operator-sdk 474 | [operator_lifecycle_manager]: https://github.com/operator-framework/operator-lifecycle-manager 475 | [site_memcached]: https://memcached.org/ 476 | [doc_sdk_user_guide]: https://sdk.operatorframework.io/docs/contribution-guidelines/developer-guide/ 477 | [doc_sdk_install_instr]: https://sdk.operatorframework.io/docs/install-operator-sdk/ 478 | [doc_olm_install_instr]: https://github.com/operator-framework/operator-lifecycle-manager/blob/master/doc/install/install.md 479 | [layout_doc]: https://sdk.operatorframework.io/docs/golang/references/project-layout/ 480 | [manager_go_doc]: https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/manager#Manager 481 | [controller_go_doc]: https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg#hdr-Controller 482 | [memcached_controller]: https://github.com/operator-framework/operator-sdk/blob/master/example/memcached-operator/memcached_controller.go.tmpl 483 | [request_go_doc]: https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/reconcile#Request 484 | [result_go_doc]: https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/reconcile#Result 485 | [doc_client_api]: https://sdk.operatorframework.io/docs/golang/references/client/ 486 | [repo_sdk_samples_memcached]: https://github.com/operator-framework/operator-sdk-samples/tree/master/go/memcached-operator/ 487 | [mailing_list]: https://groups.google.com/forum/#!forum/operator-framework 488 | [memcached_csv]: https://github.com/operator-framework/operator-sdk/blob/master/test/test-framework/deploy/olm-catalog/memcached-operator/0.0.3/memcached-operator.v0.0.3.clusterserviceversion.yaml 489 | [testing-operators]: https://github.com/operator-framework/community-operators/blob/master/docs/testing-operators.md 490 | [sdk-integration-with-olm-doc]: https://github.com/operator-framework/operator-sdk/blob/master/proposals/sdk-integration-with-olm.md 491 | [sdk-olm-cli]: https://sdk.operatorframework.io/docs/cli/operator-sdk_olm/ 492 | [sdk-run-cli]: https://sdk.operatorframework.io/docs/cli/operator-sdk_run/ 493 | [sdk-bundle-cli]: https://sdk.operatorframework.io/docs/cli/operator-sdk_bundle/ 494 | [operator-hub-io]: https://operatorhub.io/ 495 | [operator-hub-io-preview]: https://operatorhub.io/preview 496 | [scorecard-doc]: https://sdk.operatorframework.io/docs/scorecard/ 497 | [csv-fields]: https://sdk.operatorframework.io/docs/olm-integration/generating-a-csv/ 498 | -------------------------------------------------------------------------------- /hack/ci/marker: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/operator-framework/getting-started/3e2ee0a8b5f53b9556f633180f132ddba294d5cb/hack/ci/marker --------------------------------------------------------------------------------