├── .dockerignore ├── .drone.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── PROJECT ├── README.md ├── cmd └── manager │ └── main.go ├── config ├── crds │ ├── talos_v1alpha1_talosclusterproviderspec.yaml │ ├── talos_v1alpha1_talosclusterproviderstatus.yaml │ ├── talos_v1alpha1_talosmachineproviderspec.yaml │ └── talos_v1alpha1_talosmachineproviderstatus.yaml ├── default │ ├── kustomization.yaml │ ├── kustomization.yaml-e │ ├── manager_auth_proxy_patch.yaml │ ├── manager_image_patch.yaml │ ├── manager_image_patch.yaml-e │ └── manager_prometheus_metrics_patch.yaml ├── manager │ └── manager.yaml ├── rbac │ ├── auth_proxy_role.yaml │ ├── auth_proxy_role_binding.yaml │ ├── auth_proxy_service.yaml │ ├── rbac_role.yaml │ └── rbac_role_binding.yaml └── samples │ ├── cluster-deployment │ ├── aws │ │ ├── kustomization.yaml │ │ ├── platform-config-cluster.yaml │ │ ├── platform-config-masters.yaml │ │ └── platform-config-workers.yaml │ ├── azure │ │ ├── kustomization.yaml │ │ ├── platform-config-cluster.yaml │ │ ├── platform-config-masters.yaml │ │ └── platform-config-workers.yaml │ ├── base │ │ ├── cluster.yaml │ │ ├── kustomization.yaml │ │ ├── master-0.yaml │ │ ├── master-1.yaml │ │ ├── master-2.yaml │ │ └── worker-machines.yaml │ ├── gce │ │ ├── kustomization.yaml │ │ ├── platform-config-cluster.yaml │ │ ├── platform-config-masters.yaml │ │ └── platform-config-workers.yaml │ └── packet │ │ ├── kustomization.yaml │ │ ├── platform-config-cluster.yaml │ │ ├── platform-config-masters.yaml │ │ └── platform-config-workers.yaml │ └── specs │ ├── talos_v1alpha1_talosclusterproviderspec.yaml │ ├── talos_v1alpha1_talosclusterproviderstatus.yaml │ ├── talos_v1alpha1_talosmachineproviderspec.yaml │ └── talos_v1alpha1_talosmachineproviderstatus.yaml ├── docs ├── AWS.md ├── Azure.md ├── GCE.md └── Packet.md ├── go.mod ├── go.sum ├── hack ├── boilerplate.go.txt └── tools.go └── pkg ├── apis ├── addtoscheme_talos_v1alpha1.go ├── apis.go └── talos │ ├── group.go │ └── v1alpha1 │ ├── doc.go │ ├── register.go │ ├── talosclusterproviderspec_types.go │ ├── talosclusterproviderspec_types_test.go │ ├── talosclusterproviderstatus_types.go │ ├── talosclusterproviderstatus_types_test.go │ ├── talosmachineproviderspec_types.go │ ├── talosmachineproviderspec_types_test.go │ ├── talosmachineproviderstatus_types.go │ ├── talosmachineproviderstatus_types_test.go │ ├── v1alpha1_suite_test.go │ └── zz_generated.deepcopy.go ├── cloud └── talos │ ├── actuators │ ├── cluster │ │ └── actuator.go │ └── machine │ │ └── actuator.go │ ├── provisioners │ ├── aws │ │ └── aws.go │ ├── azure │ │ └── azure.go │ ├── gce │ │ └── gce.go │ ├── packet │ │ └── packet.go │ └── provision.go │ └── utils │ └── utils.go ├── controller └── controller.go └── webhook └── webhook.go /.dockerignore: -------------------------------------------------------------------------------- 1 | bin 2 | .git 3 | vendor 4 | -------------------------------------------------------------------------------- /.drone.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | workspace: 6 | base: /go 7 | path: src/github.com/talos-systems/cluster-api-provider-talos 8 | 9 | services: 10 | - name: docker 11 | image: docker:19.03-dind 12 | entrypoint: 13 | - dockerd 14 | command: 15 | - --dns=8.8.8.8 16 | - --dns=8.8.4.4 17 | - --mtu=1440 18 | - --log-level=error 19 | privileged: true 20 | volumes: 21 | - name: dockersock 22 | path: /var/run 23 | - name: manifests 24 | path: /tmp/manifests 25 | 26 | steps: 27 | - name: fetch 28 | image: docker:git 29 | commands: 30 | - git fetch --tags 31 | when: 32 | event: tag 33 | 34 | - name: test 35 | image: autonomy/build-container:latest 36 | commands: 37 | - sleep 5 38 | - make test 39 | volumes: 40 | - name: dockersock 41 | path: /var/run 42 | depends_on: 43 | - fetch 44 | 45 | - name: gen-manifests 46 | image: autonomy/build-container:latest 47 | commands: 48 | - make manifests 49 | environment: 50 | GOPATH: /go 51 | GOBIN: /go/bin 52 | volumes: 53 | - name: manifests 54 | path: /tmp/manifests 55 | - name: dockersock 56 | path: /var/run 57 | depends_on: 58 | - test 59 | 60 | - name: build 61 | image: autonomy/build-container:latest 62 | pull: always 63 | commands: 64 | - make docker-build 65 | volumes: 66 | - name: dockersock 67 | path: /var/run 68 | depends_on: 69 | - test 70 | 71 | - name: push 72 | image: autonomy/build-container:latest 73 | pull: always 74 | environment: 75 | DOCKER_USERNAME: 76 | from_secret: docker_username 77 | DOCKER_PASSWORD: 78 | from_secret: docker_password 79 | commands: 80 | - make login 81 | - make docker-push 82 | volumes: 83 | - name: dockersock 84 | path: /var/run 85 | when: 86 | event: 87 | exclude: 88 | - pull_request 89 | depends_on: 90 | - build 91 | 92 | - name: release 93 | image: plugins/github-release 94 | settings: 95 | api_key: 96 | from_secret: github_token 97 | draft: true 98 | files: 99 | - /tmp/manifests/* 100 | checksum: 101 | - sha256 102 | - sha512 103 | volumes: 104 | - name: manifests 105 | path: /tmp/manifests 106 | depends_on: 107 | - gen-manifests 108 | - push 109 | when: 110 | event: tag 111 | 112 | volumes: 113 | - name: dockersock 114 | temp: {} 115 | - name: manifests 116 | temp: {} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Binaries for programs and plugins 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | bin 9 | 10 | # Test binary, build with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | 16 | # Kubernetes Generated files - skip generated files, except for vendored files 17 | 18 | vendor 19 | 20 | # editor and IDE paraphernalia 21 | .idea 22 | *.swp 23 | *.swo 24 | *~ 25 | .vscode/ 26 | 27 | provider-components.yaml 28 | cover.out 29 | sha256sum.txt 30 | sha512sum.txt -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG KUBEBUILDER_VERSION=1.0.8 2 | ARG KUSTOMIZE_VERSION=1.0.11 3 | ARG CAPI_VERSION=0.1.9 4 | 5 | FROM golang:1.13.0-alpine as base 6 | RUN apk add --no-cache make curl git 7 | 8 | FROM base AS modules 9 | ENV GO111MODULE on 10 | ENV GOPROXY https://proxy.golang.org 11 | ENV CGO_ENABLED 0 12 | WORKDIR /go/src/github.com/talos-systems/cluster-api-provider-talos 13 | COPY ./go.mod ./ 14 | COPY ./go.sum ./ 15 | RUN go mod download 16 | RUN go mod verify 17 | COPY ./cmd ./cmd 18 | COPY ./config ./config 19 | COPY ./hack ./hack 20 | COPY ./pkg ./pkg 21 | COPY Makefile Makefile 22 | COPY PROJECT PROJECT 23 | RUN go mod vendor 24 | RUN go list -mod=readonly all >/dev/null 25 | RUN ! go mod tidy -v 2>&1 | grep . 26 | 27 | FROM modules AS generate 28 | RUN make generate 29 | 30 | FROM generate AS test 31 | RUN mkdir -p /usr/local/kubebuilder/bin 32 | ARG KUBEBUILDER_VERSION 33 | RUN curl -L https://github.com/kubernetes-sigs/kubebuilder/releases/download/v${KUBEBUILDER_VERSION}/kubebuilder_${KUBEBUILDER_VERSION}_linux_amd64.tar.gz | tar -xvz --strip-components=2 -C /usr/local/kubebuilder/bin 34 | RUN go fmt ./pkg/... ./cmd/... 35 | RUN go vet ./pkg/... ./cmd/... 36 | #TODO: Troubleshoot issues here 37 | #RUN go test ./pkg/... ./cmd/... -coverprofile cover.out 38 | 39 | FROM test AS manifests 40 | ARG IMG 41 | ARG KUSTOMIZE_VERSION 42 | ARG CAPI_VERSION 43 | RUN mkdir -p /tmp/manifests 44 | RUN wget -O /usr/local/bin/kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/v${KUSTOMIZE_VERSION}/kustomize_${KUSTOMIZE_VERSION}_linux_amd64 45 | RUN chmod +x /usr/local/bin/kustomize 46 | RUN go run vendor/sigs.k8s.io/controller-tools/cmd/controller-gen/main.go all 47 | RUN sed -i'' -e 's@image: .*@image: '"${IMG}"'@' ./config/default/manager_image_patch.yaml 48 | RUN sed -i'' -e 's@^- manager_auth_proxy_patch.yaml.*@#&@' config/default/kustomization.yaml 49 | RUN kustomize build config/default/ > /tmp/manifests/provider-components.yaml 50 | RUN echo "---" >> /tmp/manifests/provider-components.yaml 51 | RUN kustomize build https://github.com/kubernetes-sigs/cluster-api//config/default?ref=v${CAPI_VERSION} >> /tmp/manifests/provider-components.yaml 52 | 53 | # Build the manager binary 54 | FROM manifests AS build 55 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager github.com/talos-systems/cluster-api-provider-talos/cmd/manager 56 | 57 | # Copy the controller-manager into a thin image 58 | FROM scratch 59 | WORKDIR / 60 | COPY --from=build /go/src/github.com/talos-systems/cluster-api-provider-talos/manager . 61 | ENTRYPOINT ["/manager"] 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TAG ?= $(shell gitmeta image tag) 2 | REPO ?= autonomy/cluster-api-provider-talos 3 | 4 | all: test docker-build 5 | 6 | # Generate code 7 | generate: 8 | go generate ./pkg/... ./cmd/... 9 | go fmt ./pkg/... ./cmd/... 10 | go vet ./pkg/... ./cmd/... 11 | 12 | # Build manager binary 13 | manager: generate 14 | go build -o bin/manager github.com/talos-systems/cluster-api-provider-talos/cmd/manager 15 | 16 | # Run against the configured Kubernetes cluster in ~/.kube/config 17 | run: generate 18 | go run ./cmd/manager/main.go 19 | 20 | # Run tests 21 | test: 22 | docker build . --target $@ -t $(REPO):test 23 | 24 | # Install CRDs into a cluster 25 | install: manifests 26 | kubectl apply -f config/crds 27 | 28 | # Deploy controller in the configured Kubernetes cluster in ~/.kube/config 29 | deploy: manifests 30 | cat provider-components.yaml | kubectl apply -f - 31 | 32 | # Generate manifests e.g. CRD, RBAC etc. 33 | manifests: 34 | docker build . --target $@ -t $(REPO):manifests --build-arg IMG="$(REPO):$(TAG)" 35 | mkdir -p /tmp/manifests 36 | docker run $(REPO):manifests sh -c 'cat /tmp/manifests/provider-components.yaml' > /tmp/manifests/provider-components.yaml 37 | 38 | # Build the docker image 39 | docker-build: 40 | docker build . -t $(REPO):$(TAG) 41 | 42 | # Push the docker image 43 | docker-push: 44 | @docker tag $(REPO):$(TAG) $(REPO):latest 45 | @docker push $(REPO):$(TAG) 46 | @docker push $(REPO):latest 47 | 48 | .PHONY: login 49 | login: 50 | @docker login --username "$(DOCKER_USERNAME)" --password "$(DOCKER_PASSWORD)" 51 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | version: "1" 2 | domain: cluster.k8s.io 3 | repo: github.com/talos-systems/cluster-api-provider-talos 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Note: This repo will soon be deprecated. Please use one of cluster-api's v1alpha2 infrastructure providers, as well as our new [bootstrap provider](https://github.com/talos-systems/cluster-api-bootstrap-provider-talos). 2 | 3 | # cluster-api-provider-talos 4 | 5 | A [cluster-api](https://github.com/kubernetes-sigs/cluster-api) provider for deploying [Talos](https://github.com/talos-systems/talos) clusters. 6 | 7 | 8 | #### Getting Started Guides: 9 | 10 | - [AWS](docs/AWS.md) 11 | - [Azure](docs/Azure.md) 12 | - [GCE](docs/GCE.md) 13 | - [Packet](docs/Packet.md) -------------------------------------------------------------------------------- /cmd/manager/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "flag" 21 | "fmt" 22 | "os" 23 | 24 | "github.com/talos-systems/cluster-api-provider-talos/pkg/apis" 25 | "github.com/talos-systems/cluster-api-provider-talos/pkg/cloud/talos/actuators/cluster" 26 | "github.com/talos-systems/cluster-api-provider-talos/pkg/cloud/talos/actuators/machine" 27 | clusterapis "sigs.k8s.io/cluster-api/pkg/apis" 28 | "sigs.k8s.io/cluster-api/pkg/apis/cluster/common" 29 | capicluster "sigs.k8s.io/cluster-api/pkg/controller/cluster" 30 | capimachine "sigs.k8s.io/cluster-api/pkg/controller/machine" 31 | "sigs.k8s.io/controller-runtime/pkg/client/config" 32 | "sigs.k8s.io/controller-runtime/pkg/manager" 33 | logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" 34 | "sigs.k8s.io/controller-runtime/pkg/runtime/signals" 35 | ) 36 | 37 | func main() { 38 | cfg := config.GetConfigOrDie() 39 | if cfg == nil { 40 | panic(fmt.Errorf("GetConfigOrDie didn't die")) 41 | } 42 | 43 | flag.Parse() 44 | log := logf.Log.WithName("talos-controller-manager") 45 | logf.SetLogger(logf.ZapLogger(false)) 46 | entryLog := log.WithName("entrypoint") 47 | 48 | // Setup a Manager 49 | mgr, err := manager.New(cfg, manager.Options{}) 50 | if err != nil { 51 | entryLog.Error(err, "unable to set up overall controller manager") 52 | os.Exit(1) 53 | } 54 | 55 | clusterActuator, err := cluster.NewClusterActuator(mgr, cluster.ClusterActuatorParams{}) 56 | if err != nil { 57 | panic(err) 58 | } 59 | machineActuator, err := machine.NewMachineActuator(mgr, machine.MachineActuatorParams{}) 60 | if err != nil { 61 | panic(err) 62 | } 63 | 64 | // Register our cluster deployer (the interface is in clusterctl and we define the Deployer interface on the actuator) 65 | common.RegisterClusterProvisioner("talos", clusterActuator) 66 | 67 | if err := apis.AddToScheme(mgr.GetScheme()); err != nil { 68 | panic(err) 69 | } 70 | 71 | if err := clusterapis.AddToScheme(mgr.GetScheme()); err != nil { 72 | panic(err) 73 | } 74 | 75 | capimachine.AddWithActuator(mgr, machineActuator) 76 | capicluster.AddWithActuator(mgr, clusterActuator) 77 | 78 | if err := mgr.Start(signals.SetupSignalHandler()); err != nil { 79 | entryLog.Error(err, "unable to run manager") 80 | os.Exit(1) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /config/crds/talos_v1alpha1_talosclusterproviderspec.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | controller-tools.k8s.io: "1.0" 7 | name: talosclusterproviderspecs.talos.cluster.k8s.io 8 | spec: 9 | group: talos.cluster.k8s.io 10 | names: 11 | kind: TalosClusterProviderSpec 12 | plural: talosclusterproviderspecs 13 | scope: Namespaced 14 | validation: 15 | openAPIV3Schema: 16 | properties: 17 | apiVersion: 18 | description: 'APIVersion defines the versioned schema of this representation 19 | of an object. Servers should convert recognized schemas to the latest 20 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' 21 | type: string 22 | kind: 23 | description: 'Kind is a string value representing the REST resource this 24 | object represents. Servers may infer this from the endpoint the client 25 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' 26 | type: string 27 | masters: 28 | properties: 29 | ips: 30 | items: 31 | type: string 32 | type: array 33 | type: object 34 | metadata: 35 | type: object 36 | status: 37 | type: object 38 | workers: 39 | items: 40 | properties: 41 | name: 42 | type: string 43 | type: object 44 | type: array 45 | version: v1alpha1 46 | status: 47 | acceptedNames: 48 | kind: "" 49 | plural: "" 50 | conditions: [] 51 | storedVersions: [] 52 | -------------------------------------------------------------------------------- /config/crds/talos_v1alpha1_talosclusterproviderstatus.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | controller-tools.k8s.io: "1.0" 7 | name: talosclusterproviderstatuses.talos.cluster.k8s.io 8 | spec: 9 | group: talos.cluster.k8s.io 10 | names: 11 | kind: TalosClusterProviderStatus 12 | plural: talosclusterproviderstatuses 13 | scope: Namespaced 14 | validation: 15 | openAPIV3Schema: 16 | properties: 17 | apiVersion: 18 | description: 'APIVersion defines the versioned schema of this representation 19 | of an object. Servers should convert recognized schemas to the latest 20 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' 21 | type: string 22 | kind: 23 | description: 'Kind is a string value representing the REST resource this 24 | object represents. Servers may infer this from the endpoint the client 25 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' 26 | type: string 27 | metadata: 28 | type: object 29 | spec: 30 | type: object 31 | status: 32 | type: object 33 | version: v1alpha1 34 | status: 35 | acceptedNames: 36 | kind: "" 37 | plural: "" 38 | conditions: [] 39 | storedVersions: [] 40 | -------------------------------------------------------------------------------- /config/crds/talos_v1alpha1_talosmachineproviderspec.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | controller-tools.k8s.io: "1.0" 7 | name: talosmachineproviderspecs.talos.cluster.k8s.io 8 | spec: 9 | group: talos.cluster.k8s.io 10 | names: 11 | kind: TalosMachineProviderSpec 12 | plural: talosmachineproviderspecs 13 | scope: Namespaced 14 | validation: 15 | openAPIV3Schema: 16 | properties: 17 | apiVersion: 18 | description: 'APIVersion defines the versioned schema of this representation 19 | of an object. Servers should convert recognized schemas to the latest 20 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' 21 | type: string 22 | kind: 23 | description: 'Kind is a string value representing the REST resource this 24 | object represents. Servers may infer this from the endpoint the client 25 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' 26 | type: string 27 | metadata: 28 | type: object 29 | platform: 30 | properties: 31 | config: 32 | type: string 33 | type: 34 | type: string 35 | type: object 36 | status: 37 | type: object 38 | version: v1alpha1 39 | status: 40 | acceptedNames: 41 | kind: "" 42 | plural: "" 43 | conditions: [] 44 | storedVersions: [] 45 | -------------------------------------------------------------------------------- /config/crds/talos_v1alpha1_talosmachineproviderstatus.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1beta1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | controller-tools.k8s.io: "1.0" 7 | name: talosmachineproviderstatuses.talos.cluster.k8s.io 8 | spec: 9 | group: talos.cluster.k8s.io 10 | names: 11 | kind: TalosMachineProviderStatus 12 | plural: talosmachineproviderstatuses 13 | scope: Namespaced 14 | validation: 15 | openAPIV3Schema: 16 | properties: 17 | apiVersion: 18 | description: 'APIVersion defines the versioned schema of this representation 19 | of an object. Servers should convert recognized schemas to the latest 20 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' 21 | type: string 22 | kind: 23 | description: 'Kind is a string value representing the REST resource this 24 | object represents. Servers may infer this from the endpoint the client 25 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' 26 | type: string 27 | metadata: 28 | type: object 29 | spec: 30 | type: object 31 | status: 32 | type: object 33 | version: v1alpha1 34 | status: 35 | acceptedNames: 36 | kind: "" 37 | plural: "" 38 | conditions: [] 39 | storedVersions: [] 40 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: cluster-api-provider-talos-system 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | namePrefix: cluster-api-provider-talos- 10 | 11 | # Labels to add to all resources and selectors. 12 | #commonLabels: 13 | # someName: someValue 14 | 15 | # Each entry in this list must resolve to an existing 16 | # resource definition in YAML. These are the resource 17 | # files that kustomize reads, modifies and emits as a 18 | # YAML string, with resources separated by document 19 | # markers ("---"). 20 | resources: 21 | - ../rbac/rbac_role.yaml 22 | - ../rbac/rbac_role_binding.yaml 23 | - ../manager/manager.yaml 24 | # Comment the following 3 lines if you want to disable 25 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy) 26 | # which protects your /metrics endpoint. 27 | # - ../rbac/auth_proxy_service.yaml 28 | # - ../rbac/auth_proxy_role.yaml 29 | # - ../rbac/auth_proxy_role_binding.yaml 30 | 31 | patches: 32 | - manager_image_patch.yaml 33 | # Protect the /metrics endpoint by putting it behind auth. 34 | # Only one of manager_auth_proxy_patch.yaml and 35 | # manager_prometheus_metrics_patch.yaml should be enabled. 36 | # - manager_auth_proxy_patch.yaml 37 | # If you want your controller-manager to expose the /metrics 38 | # endpoint w/o any authn/z, uncomment the following line and 39 | # comment manager_auth_proxy_patch.yaml. 40 | # Only one of manager_auth_proxy_patch.yaml and 41 | # manager_prometheus_metrics_patch.yaml should be enabled. 42 | #- manager_prometheus_metrics_patch.yaml 43 | 44 | vars: 45 | - name: WEBHOOK_SECRET_NAME 46 | objref: 47 | kind: Secret 48 | name: webhook-server-secret 49 | apiVersion: v1 50 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml-e: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: cluster-api-provider-talos-system 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | namePrefix: cluster-api-provider-talos- 10 | 11 | # Labels to add to all resources and selectors. 12 | #commonLabels: 13 | # someName: someValue 14 | 15 | # Each entry in this list must resolve to an existing 16 | # resource definition in YAML. These are the resource 17 | # files that kustomize reads, modifies and emits as a 18 | # YAML string, with resources separated by document 19 | # markers ("---"). 20 | resources: 21 | - ../rbac/rbac_role.yaml 22 | - ../rbac/rbac_role_binding.yaml 23 | - ../manager/manager.yaml 24 | # Comment the following 3 lines if you want to disable 25 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy) 26 | # which protects your /metrics endpoint. 27 | # - ../rbac/auth_proxy_service.yaml 28 | # - ../rbac/auth_proxy_role.yaml 29 | # - ../rbac/auth_proxy_role_binding.yaml 30 | 31 | patches: 32 | - manager_image_patch.yaml 33 | # Protect the /metrics endpoint by putting it behind auth. 34 | # Only one of manager_auth_proxy_patch.yaml and 35 | # manager_prometheus_metrics_patch.yaml should be enabled. 36 | # - manager_auth_proxy_patch.yaml 37 | # If you want your controller-manager to expose the /metrics 38 | # endpoint w/o any authn/z, uncomment the following line and 39 | # comment manager_auth_proxy_patch.yaml. 40 | # Only one of manager_auth_proxy_patch.yaml and 41 | # manager_prometheus_metrics_patch.yaml should be enabled. 42 | #- manager_prometheus_metrics_patch.yaml 43 | 44 | vars: 45 | - name: WEBHOOK_SECRET_NAME 46 | objref: 47 | kind: Secret 48 | name: webhook-server-secret 49 | apiVersion: v1 50 | -------------------------------------------------------------------------------- /config/default/manager_auth_proxy_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch inject a sidecar container which is a HTTP proxy for the controller manager, 2 | # it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 3 | apiVersion: apps/v1 4 | kind: StatefulSet 5 | metadata: 6 | name: controller-manager 7 | namespace: system 8 | spec: 9 | template: 10 | spec: 11 | containers: 12 | - name: kube-rbac-proxy 13 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.4.0 14 | args: 15 | - "--secure-listen-address=0.0.0.0:8443" 16 | - "--upstream=http://127.0.0.1:8080/" 17 | - "--logtostderr=true" 18 | - "--v=10" 19 | ports: 20 | - containerPort: 8443 21 | name: https 22 | - name: manager 23 | args: 24 | - "--metrics-addr=127.0.0.1:8080" 25 | -------------------------------------------------------------------------------- /config/default/manager_image_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | # Change the value of image field below to your controller image URL 11 | - image: rsmitty/cluster-api-provider-talos 12 | name: manager 13 | -------------------------------------------------------------------------------- /config/default/manager_image_patch.yaml-e: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | # Change the value of image field below to your controller image URL 11 | - image: rsmitty/cluster-api-provider-talos 12 | name: manager 13 | -------------------------------------------------------------------------------- /config/default/manager_prometheus_metrics_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch enables Prometheus scraping for the manager pod. 2 | apiVersion: apps/v1 3 | kind: StatefulSet 4 | metadata: 5 | name: controller-manager 6 | namespace: system 7 | spec: 8 | template: 9 | metadata: 10 | annotations: 11 | prometheus.io/scrape: 'true' 12 | spec: 13 | containers: 14 | # Expose the prometheus metrics on default port 15 | - name: manager 16 | ports: 17 | - containerPort: 8080 18 | name: metrics 19 | protocol: TCP 20 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | controller-tools.k8s.io: "1.0" 7 | name: system 8 | --- 9 | apiVersion: v1 10 | kind: Service 11 | metadata: 12 | name: controller-manager-service 13 | namespace: system 14 | labels: 15 | control-plane: controller-manager 16 | controller-tools.k8s.io: "1.0" 17 | spec: 18 | selector: 19 | control-plane: controller-manager 20 | controller-tools.k8s.io: "1.0" 21 | ports: 22 | - port: 443 23 | --- 24 | apiVersion: apps/v1 25 | kind: StatefulSet 26 | metadata: 27 | name: controller-manager 28 | namespace: system 29 | labels: 30 | control-plane: controller-manager 31 | controller-tools.k8s.io: "1.0" 32 | spec: 33 | selector: 34 | matchLabels: 35 | control-plane: controller-manager 36 | controller-tools.k8s.io: "1.0" 37 | serviceName: controller-manager-service 38 | template: 39 | metadata: 40 | labels: 41 | control-plane: controller-manager 42 | controller-tools.k8s.io: "1.0" 43 | spec: 44 | containers: 45 | - command: 46 | - /manager 47 | image: controller:latest 48 | imagePullPolicy: Always 49 | name: manager 50 | env: 51 | - name: POD_NAMESPACE 52 | valueFrom: 53 | fieldRef: 54 | fieldPath: metadata.namespace 55 | - name: NODE_NAME 56 | valueFrom: 57 | fieldRef: 58 | fieldPath: spec.nodeName 59 | - name: AZURE_AUTH_LOCATION 60 | value: /.azure/service-account.json 61 | - name: GOOGLE_APPLICATION_CREDENTIALS 62 | value: /.gce/service-account.json 63 | - name: PACKET_AUTH_TOKEN 64 | value: "{{PACKET_AUTH_TOKEN}}" 65 | resources: 66 | limits: 67 | cpu: 1000m 68 | memory: 1000Mi 69 | requests: 70 | cpu: 100m 71 | memory: 100Mi 72 | ports: 73 | - containerPort: 9876 74 | name: webhook-server 75 | protocol: TCP 76 | volumeMounts: 77 | - mountPath: /tmp/cert 78 | name: cert 79 | readOnly: true 80 | - mountPath: /.azure 81 | name: azure-credentials 82 | - name: gce-credentials 83 | mountPath: /.gce 84 | - name: aws-credentials 85 | mountPath: /.aws 86 | - name: certs 87 | mountPath: /etc/ssl/certs 88 | terminationGracePeriodSeconds: 10 89 | volumes: 90 | - name: cert 91 | secret: 92 | defaultMode: 420 93 | secretName: webhook-server-secret 94 | - name: azure-credentials 95 | secret: 96 | secretName: azure-credentials 97 | - name: gce-credentials 98 | secret: 99 | secretName: gce-credentials 100 | - name: aws-credentials 101 | secret: 102 | secretName: aws-credentials 103 | - name: certs 104 | hostPath: 105 | path: /etc/ssl/certs 106 | --- 107 | apiVersion: v1 108 | kind: Secret 109 | metadata: 110 | name: webhook-server-secret 111 | namespace: system 112 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: proxy-role 5 | rules: 6 | - apiGroups: ["authentication.k8s.io"] 7 | resources: 8 | - tokenreviews 9 | verbs: ["create"] 10 | - apiGroups: ["authorization.k8s.io"] 11 | resources: 12 | - subjectaccessreviews 13 | verbs: ["create"] 14 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: proxy-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: proxy-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: default 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | annotations: 5 | prometheus.io/port: "8443" 6 | prometheus.io/scheme: https 7 | prometheus.io/scrape: "true" 8 | labels: 9 | control-plane: controller-manager 10 | controller-tools.k8s.io: "1.0" 11 | name: controller-manager-metrics-service 12 | namespace: system 13 | spec: 14 | ports: 15 | - name: https 16 | port: 8443 17 | targetPort: https 18 | selector: 19 | control-plane: controller-manager 20 | controller-tools.k8s.io: "1.0" 21 | -------------------------------------------------------------------------------- /config/rbac/rbac_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | name: manager-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - create 16 | - update 17 | - patch 18 | - delete 19 | - apiGroups: 20 | - cluster.k8s.io 21 | resources: 22 | - machines 23 | - machines/status 24 | - machinedeployments 25 | - machinedeployments/status 26 | - machinesets 27 | - machinesets/status 28 | - machineclasses 29 | verbs: 30 | - get 31 | - list 32 | - watch 33 | - create 34 | - update 35 | - patch 36 | - delete 37 | - apiGroups: 38 | - cluster.k8s.io 39 | resources: 40 | - clusters 41 | - clusters/status 42 | verbs: 43 | - get 44 | - list 45 | - watch 46 | - create 47 | - update 48 | - patch 49 | - delete 50 | - apiGroups: 51 | - "" 52 | resources: 53 | - nodes 54 | - events 55 | verbs: 56 | - get 57 | - list 58 | - watch 59 | - create 60 | - update 61 | - patch 62 | - delete 63 | - apiGroups: 64 | - admissionregistration.k8s.io 65 | resources: 66 | - mutatingwebhookconfigurations 67 | - validatingwebhookconfigurations 68 | verbs: 69 | - get 70 | - list 71 | - watch 72 | - create 73 | - update 74 | - patch 75 | - delete 76 | - apiGroups: 77 | - "" 78 | resources: 79 | - secrets 80 | verbs: 81 | - get 82 | - list 83 | - watch 84 | - create 85 | - update 86 | - patch 87 | - delete 88 | - apiGroups: 89 | - "" 90 | resources: 91 | - services 92 | verbs: 93 | - get 94 | - list 95 | - watch 96 | - create 97 | - update 98 | - patch 99 | - delete 100 | -------------------------------------------------------------------------------- /config/rbac/rbac_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | creationTimestamp: null 5 | name: manager-rolebinding 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: ClusterRole 9 | name: manager-role 10 | subjects: 11 | - kind: ServiceAccount 12 | name: default 13 | namespace: system 14 | -------------------------------------------------------------------------------- /config/samples/cluster-deployment/aws/kustomization.yaml: -------------------------------------------------------------------------------- 1 | bases: 2 | - ../base/ 3 | 4 | patchesJson6902: 5 | ##Add master static IPs to cluster.yaml 6 | - target: 7 | group: cluster.k8s.io 8 | version: v1alpha1 9 | kind: Cluster 10 | name: talos-test-cluster 11 | path: platform-config-cluster.yaml 12 | 13 | ##Patch each master with gce config 14 | - target: 15 | group: cluster.k8s.io 16 | version: v1alpha1 17 | kind: Machine 18 | name: talos-test-cluster-master-0 19 | path: platform-config-masters.yaml 20 | - target: 21 | group: cluster.k8s.io 22 | version: v1alpha1 23 | kind: Machine 24 | name: talos-test-cluster-master-1 25 | path: platform-config-masters.yaml 26 | - target: 27 | group: cluster.k8s.io 28 | version: v1alpha1 29 | kind: Machine 30 | name: talos-test-cluster-master-2 31 | path: platform-config-masters.yaml 32 | 33 | ##Patch workers with gce config 34 | - target: 35 | group: cluster.k8s.io 36 | version: v1alpha1 37 | kind: MachineDeployment 38 | name: talos-test-cluster-workers 39 | path: platform-config-workers.yaml -------------------------------------------------------------------------------- /config/samples/cluster-deployment/aws/platform-config-cluster.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /spec/providerSpec/value/platform 3 | value: 4 | config: |- 5 | region: "us-west-2" 6 | type: aws -------------------------------------------------------------------------------- /config/samples/cluster-deployment/aws/platform-config-masters.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /spec/providerSpec/value/platform 3 | value: 4 | config: |- 5 | region: "us-west-2" 6 | instances: 7 | type: "t2.micro" 8 | ami: "{{AWS_AMI}}" 9 | keypair: "{{AWS_KEY_NAME}}" 10 | disks: 11 | size: 10 12 | type: aws -------------------------------------------------------------------------------- /config/samples/cluster-deployment/aws/platform-config-workers.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /spec/template/spec/providerSpec/value/platform 3 | value: 4 | config: |- 5 | region: "us-west-2" 6 | instances: 7 | type: "t2.micro" 8 | ami: "{{AWS_AMI}}" 9 | keypair: "{{AWS_KEY_NAME}}" 10 | disks: 11 | size: 10 12 | type: aws -------------------------------------------------------------------------------- /config/samples/cluster-deployment/azure/kustomization.yaml: -------------------------------------------------------------------------------- 1 | bases: 2 | - ../base/ 3 | 4 | patchesJson6902: 5 | ##Add master static IPs to cluster.yaml 6 | - target: 7 | group: cluster.k8s.io 8 | version: v1alpha1 9 | kind: Cluster 10 | name: talos-test-cluster 11 | path: platform-config-cluster.yaml 12 | 13 | ##Patch each master with gce config 14 | - target: 15 | group: cluster.k8s.io 16 | version: v1alpha1 17 | kind: Machine 18 | name: talos-test-cluster-master-0 19 | path: platform-config-masters.yaml 20 | - target: 21 | group: cluster.k8s.io 22 | version: v1alpha1 23 | kind: Machine 24 | name: talos-test-cluster-master-1 25 | path: platform-config-masters.yaml 26 | - target: 27 | group: cluster.k8s.io 28 | version: v1alpha1 29 | kind: Machine 30 | name: talos-test-cluster-master-2 31 | path: platform-config-masters.yaml 32 | 33 | ##Patch workers with gce config 34 | - target: 35 | group: cluster.k8s.io 36 | version: v1alpha1 37 | kind: MachineDeployment 38 | name: talos-test-cluster-workers 39 | path: platform-config-workers.yaml -------------------------------------------------------------------------------- /config/samples/cluster-deployment/azure/platform-config-cluster.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /spec/providerSpec/value/platform 3 | value: 4 | config: |- 5 | location: "centralus" 6 | resourcegroup: "{{RESOURCE_GROUP}}" 7 | type: azure -------------------------------------------------------------------------------- /config/samples/cluster-deployment/azure/platform-config-masters.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /spec/providerSpec/value/platform 3 | value: 4 | config: |- 5 | location: "centralus" 6 | resourcegroup: "{{RESOURCE_GROUP}}" 7 | instances: 8 | type: "Standard_D2_v3" 9 | image: "{{IMAGE_RESOURCE_ID}}" 10 | network: "{{NETWORK}}" 11 | subnet: "{{SUBNET}}" 12 | disks: 13 | size: 10 14 | type: azure -------------------------------------------------------------------------------- /config/samples/cluster-deployment/azure/platform-config-workers.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /spec/template/spec/providerSpec/value/platform 3 | value: 4 | config: |- 5 | location: "centralus" 6 | resourcegroup: "{{RESOURCE_GROUP}}" 7 | instances: 8 | type: "Standard_D2_v3" 9 | image: "{{IMAGE_RESOURCE_ID}}" 10 | network: "{{NETWORK}}" 11 | subnet: "{{SUBNET}}" 12 | disks: 13 | size: 10 14 | type: azure -------------------------------------------------------------------------------- /config/samples/cluster-deployment/base/cluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "cluster.k8s.io/v1alpha1" 2 | kind: Cluster 3 | metadata: 4 | name: talos-test-cluster 5 | annotations: 6 | spec: 7 | clusterNetwork: 8 | services: 9 | cidrBlocks: ["10.96.0.0/12"] 10 | pods: 11 | cidrBlocks: ["192.168.0.0/16"] 12 | serviceDomain: "cluster.local" 13 | providerSpec: 14 | value: 15 | apiVersion: "talosproviderconfig/v1alpha1" 16 | kind: "TalosClusterProviderSpec" 17 | platform: {} 18 | controlplane: 19 | count: 3 20 | -------------------------------------------------------------------------------- /config/samples/cluster-deployment/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - cluster.yaml 3 | - master-0.yaml 4 | - master-1.yaml 5 | - master-2.yaml 6 | - worker-machines.yaml -------------------------------------------------------------------------------- /config/samples/cluster-deployment/base/master-0.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "cluster.k8s.io/v1alpha1" 2 | kind: Machine 3 | metadata: 4 | name: talos-test-cluster-master-0 5 | labels: 6 | cluster.k8s.io/cluster-name: talos-test-cluster 7 | set: master 8 | spec: 9 | providerSpec: 10 | value: 11 | apiVersion: "talosproviderconfig/v1alpha1" 12 | kind: "TalosMachineProviderSpec" 13 | platform: {} 14 | -------------------------------------------------------------------------------- /config/samples/cluster-deployment/base/master-1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "cluster.k8s.io/v1alpha1" 2 | kind: Machine 3 | metadata: 4 | name: talos-test-cluster-master-1 5 | labels: 6 | cluster.k8s.io/cluster-name: talos-test-cluster 7 | set: master 8 | spec: 9 | providerSpec: 10 | value: 11 | apiVersion: "talosproviderconfig/v1alpha1" 12 | kind: "TalosMachineProviderSpec" 13 | platform: {} -------------------------------------------------------------------------------- /config/samples/cluster-deployment/base/master-2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "cluster.k8s.io/v1alpha1" 2 | kind: Machine 3 | metadata: 4 | name: talos-test-cluster-master-2 5 | labels: 6 | cluster.k8s.io/cluster-name: talos-test-cluster 7 | set: master 8 | spec: 9 | providerSpec: 10 | value: 11 | apiVersion: "talosproviderconfig/v1alpha1" 12 | kind: "TalosMachineProviderSpec" 13 | platform: {} 14 | -------------------------------------------------------------------------------- /config/samples/cluster-deployment/base/worker-machines.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "cluster.k8s.io/v1alpha1" 2 | kind: MachineDeployment 3 | metadata: 4 | name: talos-test-cluster-workers 5 | labels: 6 | cluster.k8s.io/cluster-name: talos-test-cluster 7 | set: worker 8 | spec: 9 | replicas: 3 10 | selector: 11 | matchLabels: 12 | cluster.k8s.io/cluster-name: talos-test-cluster 13 | set: worker 14 | template: 15 | metadata: 16 | labels: 17 | cluster.k8s.io/cluster-name: talos-test-cluster 18 | set: worker 19 | spec: 20 | providerSpec: 21 | value: 22 | apiVersion: "talosproviderconfig/v1alpha1" 23 | kind: "TalosMachineProviderSpec" 24 | platform: {} 25 | -------------------------------------------------------------------------------- /config/samples/cluster-deployment/gce/kustomization.yaml: -------------------------------------------------------------------------------- 1 | bases: 2 | - ../base/ 3 | 4 | patchesJson6902: 5 | ##Add master static IPs to cluster.yaml 6 | - target: 7 | group: cluster.k8s.io 8 | version: v1alpha1 9 | kind: Cluster 10 | name: talos-test-cluster 11 | path: platform-config-cluster.yaml 12 | 13 | ##Patch each master with gce config 14 | - target: 15 | group: cluster.k8s.io 16 | version: v1alpha1 17 | kind: Machine 18 | name: talos-test-cluster-master-0 19 | path: platform-config-masters.yaml 20 | - target: 21 | group: cluster.k8s.io 22 | version: v1alpha1 23 | kind: Machine 24 | name: talos-test-cluster-master-1 25 | path: platform-config-masters.yaml 26 | - target: 27 | group: cluster.k8s.io 28 | version: v1alpha1 29 | kind: Machine 30 | name: talos-test-cluster-master-2 31 | path: platform-config-masters.yaml 32 | 33 | ##Patch workers with gce config 34 | - target: 35 | group: cluster.k8s.io 36 | version: v1alpha1 37 | kind: MachineDeployment 38 | name: talos-test-cluster-workers 39 | path: platform-config-workers.yaml -------------------------------------------------------------------------------- /config/samples/cluster-deployment/gce/platform-config-cluster.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /spec/providerSpec/value/platform 3 | value: 4 | config: |- 5 | region: "us-central1" 6 | project: "{{PROJECT_NAME}}" 7 | type: "gce" -------------------------------------------------------------------------------- /config/samples/cluster-deployment/gce/platform-config-masters.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /spec/providerSpec/value/platform 3 | value: 4 | type: "gce" 5 | config: | 6 | zone: "us-central1-c" 7 | project: "{{PROJECT_NAME}}" 8 | instances: 9 | type: "n1-standard-1" 10 | image: "https://www.googleapis.com/compute/v1/projects/{{PROJECT_NAME}}/global/images/{{TALOS_IMAGE_NAME}}" 11 | disks: 12 | size: 10 -------------------------------------------------------------------------------- /config/samples/cluster-deployment/gce/platform-config-workers.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /spec/template/spec/providerSpec/value/platform 3 | value: 4 | type: "gce" 5 | config: | 6 | zone: "us-central1-c" 7 | project: "{{PROJECT_NAME}}" 8 | instances: 9 | type: "n1-standard-1" 10 | image: "https://www.googleapis.com/compute/v1/projects/{{PROJECT_NAME}}/global/images/{{TALOS_IMAGE_NAME}}" 11 | disks: 12 | size: 10 -------------------------------------------------------------------------------- /config/samples/cluster-deployment/packet/kustomization.yaml: -------------------------------------------------------------------------------- 1 | bases: 2 | - ../base/ 3 | 4 | patchesJson6902: 5 | ##Add master static IPs to cluster.yaml 6 | - target: 7 | group: cluster.k8s.io 8 | version: v1alpha1 9 | kind: Cluster 10 | name: talos-test-cluster 11 | path: platform-config-cluster.yaml 12 | 13 | ##Patch each master with packet config 14 | - target: 15 | group: cluster.k8s.io 16 | version: v1alpha1 17 | kind: Machine 18 | name: talos-test-cluster-master-0 19 | path: platform-config-masters.yaml 20 | - target: 21 | group: cluster.k8s.io 22 | version: v1alpha1 23 | kind: Machine 24 | name: talos-test-cluster-master-1 25 | path: platform-config-masters.yaml 26 | - target: 27 | group: cluster.k8s.io 28 | version: v1alpha1 29 | kind: Machine 30 | name: talos-test-cluster-master-2 31 | path: platform-config-masters.yaml 32 | 33 | ##Patch workers with packet config 34 | - target: 35 | group: cluster.k8s.io 36 | version: v1alpha1 37 | kind: MachineDeployment 38 | name: talos-test-cluster-workers 39 | path: platform-config-workers.yaml -------------------------------------------------------------------------------- /config/samples/cluster-deployment/packet/platform-config-cluster.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /spec/providerSpec/value/platform 3 | value: 4 | config: |- 5 | projectid: {{PROJECT_ID}} 6 | ipblock: {{IP_BLOCK}} 7 | type: packet -------------------------------------------------------------------------------- /config/samples/cluster-deployment/packet/platform-config-masters.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /spec/providerSpec/value/platform 3 | value: 4 | config: |- 5 | projectid: {{PROJECT_ID}} 6 | instances: 7 | plan: "t1.small.x86" 8 | facility: "sjc1" 9 | pxeurl: "http://{{PXE_SERVER}}/boot.ipxe" 10 | install: 11 | wipe: false 12 | force: true 13 | boot: 14 | device: /dev/sda 15 | size: 1024000000 16 | kernel: http://{{PXE_SERVER}}/assets/talos/nightly/vmlinuz 17 | initramfs: http://{{PXE_SERVER}}/assets/talos/nightly/initramfs.xz 18 | root: 19 | device: /dev/sda 20 | size: 2048000000 21 | rootfs: http://{{PXE_SERVER}}/assets/talos/nightly/rootfs.tar.gz 22 | data: 23 | device: /dev/sda 24 | size: 4096000000 25 | type: packet -------------------------------------------------------------------------------- /config/samples/cluster-deployment/packet/platform-config-workers.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /spec/template/spec/providerSpec/value/platform 3 | value: 4 | config: |- 5 | projectid: {{PROJECT_ID}} 6 | instances: 7 | plan: "t1.small.x86" 8 | facility: "sjc1" 9 | pxeurl: "http://{{PXE_SERVER}}/boot.ipxe" 10 | install: 11 | wipe: false 12 | force: true 13 | boot: 14 | device: /dev/sda 15 | size: 1024000000 16 | kernel: http://{{PXE_SERVER}}/assets/talos/nightly/vmlinuz 17 | initramfs: http://{{PXE_SERVER}}/assets/talos/nightly/initramfs.xz 18 | root: 19 | device: /dev/sda 20 | size: 2048000000 21 | rootfs: http://{{PXE_SERVER}}/assets/talos/nightly/rootfs.tar.gz 22 | data: 23 | device: /dev/sda 24 | size: 4096000000 25 | type: packet -------------------------------------------------------------------------------- /config/samples/specs/talos_v1alpha1_talosclusterproviderspec.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: talos.cluster.k8s.io/v1alpha1 2 | kind: TalosClusterProviderSpec 3 | metadata: 4 | labels: 5 | controller-tools.k8s.io: "1.0" 6 | name: talosclusterproviderspec-sample 7 | spec: 8 | # Add fields here 9 | foo: bar 10 | -------------------------------------------------------------------------------- /config/samples/specs/talos_v1alpha1_talosclusterproviderstatus.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: talos.cluster.k8s.io/v1alpha1 2 | kind: TalosClusterProviderStatus 3 | metadata: 4 | labels: 5 | controller-tools.k8s.io: "1.0" 6 | name: talosclusterproviderstatus-sample 7 | spec: 8 | # Add fields here 9 | foo: bar 10 | -------------------------------------------------------------------------------- /config/samples/specs/talos_v1alpha1_talosmachineproviderspec.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: talos.cluster.k8s.io/v1alpha1 2 | kind: TalosMachineProviderSpec 3 | metadata: 4 | labels: 5 | controller-tools.k8s.io: "1.0" 6 | name: talosmachineproviderspec-sample 7 | spec: 8 | # Add fields here 9 | foo: bar 10 | -------------------------------------------------------------------------------- /config/samples/specs/talos_v1alpha1_talosmachineproviderstatus.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: talos.cluster.k8s.io/v1alpha1 2 | kind: TalosMachineProviderStatus 3 | metadata: 4 | labels: 5 | controller-tools.k8s.io: "1.0" 6 | name: talosmachineproviderstatus-sample 7 | spec: 8 | # Add fields here 9 | foo: bar 10 | -------------------------------------------------------------------------------- /docs/AWS.md: -------------------------------------------------------------------------------- 1 | # cluster-api-provider-talos on AWS 2 | 3 | This guide will detail how to deploy the Talos provider into an existing Kubernetes cluster, as well as how to configure it to create Clusters and Machines in AWS. 4 | 5 | #### Prepare bootstrap cluster 6 | 7 | In your cluster that you'll be using to create other clusters, you must prepare a few bits. 8 | 9 | - Git clone this repo. 10 | 11 | - Create a namespace for our provider with `kubectl create ns cluster-api-provider-talos-system`. 12 | 13 | - In AWS, generate an access key and secret access key. Save these to a file called `credentials` with the following format. General instructions for generating the keys can be found [here](https://aws.amazon.com/blogs/security/how-to-find-update-access-keys-password-mfa-aws-management-console/). 14 | 15 | ``` 16 | [default] 17 | aws_access_key_id = AKI... 18 | aws_secret_access_key = MhM... 19 | ``` 20 | 21 | - Create a secret with the key generated above: `kubectl create secret generic aws-credentials -n cluster-api-provider-talos-system --from-file /path/to/credentials`. 22 | 23 | - Generate the manifests for deploying into the bootstrap cluster with `make manifests` from the `cluster-api-provider-talos` directory. 24 | 25 | - Deploy the generated manifests with `kubectl create -f provider-components.yaml` 26 | 27 | #### Create new clusters 28 | 29 | There are sample kustomize templates in [config/samples/cluster-deployment/aws](../config/samples/cluster-deployment/aws) for deploying clusters. These will be our starting point. 30 | 31 | - Edit `platform-config-cluster.yaml`, `platform-config-master.yaml`, and `platform-config-workers.yaml` with your relevant data. 32 | 33 | - From `config/samples/cluster-deployment/aws` issue `kustomize build | kubectl apply -f -`. External IPs will get created and associated with Control Plane nodes automatically. 34 | 35 | - The talos config for your master can be found with `kubectl get cm -n cluster-api-provider-talos-system talos-test-cluster-master-0 -o jsonpath='{.data.talosconfig}'`. -------------------------------------------------------------------------------- /docs/Azure.md: -------------------------------------------------------------------------------- 1 | # cluster-api-provider-talos on Azure 2 | 3 | This guide will detail how to deploy the Talos provider into an existing Kubernetes cluster, as well as how to configure it to create Clusters and Machines in Azure. 4 | 5 | #### Prereqs 6 | 7 | This guide assumes you have several prereqs created in Azure. These are: 8 | 9 | - Resource group 10 | - Virtual network 11 | - Subnet in the virtual network 12 | - A security group for the subnet that allows ports 443, 50000, 50001 13 | - Storage account for image upload 14 | - The Azure CLI configured and talking to your Azure account 15 | 16 | #### Import Image 17 | 18 | To import the image, you must download a .tar.gz talos release, add it to Google storage, and import it as an image. 19 | 20 | - Download the `talos-azure.tar.gz` image from our [Github releases](https://github.com/talos-systems/talos/releases) and extract it to get `disk.vhd`. 21 | 22 | - Follow the [Azure instructions](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/upload-vhd) on importing the vhd to cloud storage and creating an image. 23 | 24 | 25 | #### Prepare bootstrap cluster 26 | 27 | In your cluster that you'll be using to create other clusters, you must prepare a few bits. 28 | 29 | - Git clone this repo. 30 | 31 | - Create a namespace for our provider with `kubectl create ns cluster-api-provider-talos-system`. 32 | 33 | - In Azure, create service account keys and write them to a file called `service-account-azure.json` with the command `az ad sp create-for-rbac --sdk-auth > /path/to/service-account-azure.json`. 34 | 35 | - Create a secret with the key generated above: `kubectl create secret generic azure-credentials -n cluster-api-provider-talos-system --from-file /path/to/service-account-azure.json`. 36 | 37 | - Generate the manifests for deploying into the bootstrap cluster with `make manifests` from the `cluster-api-provider-talos` directory. 38 | 39 | - Deploy the generated manifests with `kubectl create -f provider-components.yaml` 40 | 41 | #### Create new clusters 42 | 43 | There are sample kustomize templates in [config/samples/cluster-deployment/azure](../config/samples/cluster-deployment/azure) for deploying clusters. These will be our starting point. 44 | 45 | - Edit `platform-config-cluster.yaml`, `platform-config-master.yaml`, and `platform-config-workers.yaml` with your relevant data from the prereqs mentioned above. 46 | 47 | - From `config/samples/cluster-deployment/azure` issue `kustomize build | kubectl apply -f -`. External IPs will get created and associated with Control Plane nodes automatically. 48 | 49 | - The talos config for your master can be found with `kubectl get cm -n cluster-api-provider-talos-system talos-test-cluster-master-0 -o jsonpath='{.data.talosconfig}'`. -------------------------------------------------------------------------------- /docs/GCE.md: -------------------------------------------------------------------------------- 1 | # cluster-api-provider-talos on GCE 2 | 3 | This guide will detail how to deploy the Talos provider into an existing Kubernetes cluster, as well as how to configure it to create Clusters and Machines in GCE. 4 | 5 | 6 | #### Import Image 7 | 8 | To import the image, you must download a .tar.gz talos release, add it to Google storage, and import it as an image. 9 | 10 | - Download the `talos-gce.tar.gz` image from our [Github releases](https://github.com/talos-systems/talos/releases). 11 | 12 | - Follow the [Google instructions](https://cloud.google.com/compute/docs/images/import-existing-image#import_image) on importing an image using cloud storage. 13 | 14 | 15 | #### Prepare bootstrap cluster 16 | 17 | In your cluster that you'll be using to create other clusters, you must prepare a few bits. 18 | 19 | - Git clone this repo. 20 | 21 | - Create a namespace for our provider with `kubectl create ns cluster-api-provider-talos-system`. 22 | 23 | - In GCE, create a service account and generate keys for the account. This will result in a JSON file containing the keys. As of now, this file needs to be named `service-account.json`. General instructions for generating the key can be found [here](https://cloud.google.com/iam/docs/creating-managing-service-account-keys). 24 | 25 | - Create a secret with the key generated above: `kubectl create secret generic gce-credentials -n cluster-api-provider-talos-system --from-file /path/to/service-account.json`. 26 | 27 | - Generate the manifests for deploying into the bootstrap cluster with `make manifests` from the `cluster-api-provider-talos` directory. 28 | 29 | - Deploy the generated manifests with `kubectl create -f provider-components.yaml` 30 | 31 | 32 | #### Create new clusters 33 | 34 | There are sample kustomize templates in [config/samples/cluster-deployment/gce](../config/samples/cluster-deployment/gce) for deploying clusters. These will be our starting point. 35 | 36 | - Edit `platform-config-cluster.yaml`, `platform-config-master.yaml`, and `platform-config-workers.yaml` with your relevant data. 37 | 38 | - From `config/samples/cluster-deployment/gce` issue `kustomize build | kubectl apply -f -`. External IPs will get created and associated with Control Plane nodes automatically. 39 | 40 | - The talos config for your master can be found with `kubectl get cm -n cluster-api-provider-talos-system talos-test-cluster-master-0 -o jsonpath='{.data.talosconfig}'`. -------------------------------------------------------------------------------- /docs/Packet.md: -------------------------------------------------------------------------------- 1 | # cluster-api-provider-talos on Packet 2 | 3 | This guide will detail how to deploy the Talos provider into an existing Kubernetes cluster, as well as how to configure it to create Clusters and Machines in Packet. 4 | 5 | **NOTE: This guide assumes you have a PXE server setup in Packet with the relevant Talos objects already present** 6 | 7 | #### Prepare bootstrap cluster 8 | 9 | In your cluster that you'll be using to create other clusters, you must prepare a few bits. 10 | 11 | - Git clone this repo. 12 | 13 | - Create a namespace for our provider with `kubectl create ns cluster-api-provider-talos-system`. 14 | 15 | - In the Packet console, create an API token for the provider to use. 16 | 17 | - In this repo, edit `config/manager/manager.yaml` and replace `{{PACKET_AUTH_TOKEN}}` with your Packet API token you just generated. 18 | 19 | - Generate the manifests for deploying into the bootstrap cluster with `make manifests` from the `cluster-api-provider-talos` directory. 20 | 21 | - Deploy the generated manifests with `kubectl create -f provider-components.yaml` 22 | 23 | 24 | #### Create new clusters 25 | 26 | There are sample kustomize templates in [config/samples/cluster-deployment/packet](../config/samples/cluster-deployment/packet) for deploying clusters. These will be our starting point. 27 | 28 | - In Packet, create a small subnet of elastic IPs for attaching to your masters. A /30 subnet should work well. Take note of the IPs in this subnet for the next step. General instructions for creating these [here](https://support.packet.com/kb/articles/elastic-ips). 29 | 30 | - Edit `platform-config-cluster.yaml`, `platform-config-master.yaml`, and `platform-config-workers.yaml` with your relevant data, adding the IP block created above to `platform-config-cluster.yaml`. 31 | 32 | - From `config/samples/cluster-deployment/packet` issue `kustomize build | kubectl apply -f -`. 33 | 34 | - The talos config for your master can be found with `kubectl get cm -n cluster-api-provider-talos-system talos-test-cluster-master-0 -o jsonpath='{.data.talosconfig}'`. -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/talos-systems/cluster-api-provider-talos 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/Azure/azure-sdk-for-go v34.0.0+incompatible 7 | github.com/Azure/go-autorest/autorest v0.9.1 8 | github.com/Azure/go-autorest/autorest/azure/auth v0.3.0 9 | github.com/Azure/go-autorest/autorest/to v0.3.0 10 | github.com/Azure/go-autorest/autorest/validation v0.2.0 // indirect 11 | github.com/aws/aws-sdk-go v1.25.8 12 | github.com/onsi/gomega v1.5.0 13 | github.com/packethost/packngo v0.2.0 14 | github.com/talos-systems/talos v0.3.0-alpha.0.0.20191009201711-edc21ea9109e 15 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 16 | google.golang.org/api v0.4.0 17 | gopkg.in/yaml.v2 v2.2.2 18 | k8s.io/api v0.0.0-20190918155943-95b840bb6a1f 19 | k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655 20 | k8s.io/client-go v10.0.0+incompatible 21 | k8s.io/code-generator v0.0.0-20181117043124-c2090bec4d9b 22 | sigs.k8s.io/cluster-api v0.1.9 23 | sigs.k8s.io/controller-runtime v0.1.12 24 | sigs.k8s.io/controller-tools v0.1.11 25 | sigs.k8s.io/testing_frameworks v0.1.1 26 | ) 27 | 28 | replace ( 29 | git.apache.org/thrift.git => github.com/apache/thrift v0.12.0 30 | github.com/Azure/go-autorest => github.com/Azure/go-autorest v12.4.1+incompatible 31 | github.com/kubernetes-incubator/bootkube => github.com/andrewrynhard/bootkube v0.14.1-0.20191009160759-890e418c7b1d 32 | k8s.io/api => k8s.io/api v0.0.0-20190222213804-5cb15d344471 33 | k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.0.0-20190228180357-d002e88f6236 34 | k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20190221213512-86fb29eff628 35 | k8s.io/apiserver => k8s.io/apiserver v0.0.0-20190122042701-b6aa1175dafa 36 | k8s.io/client-go => k8s.io/client-go v10.0.0+incompatible 37 | k8s.io/cri-api => k8s.io/cri-api v0.0.0-20190820110325-95eec93e2395 38 | ) 39 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ -------------------------------------------------------------------------------- /hack/tools.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | // Package tools imports things required by build scripts, to force `go mod` to see them as dependencies 15 | package tools 16 | 17 | import ( 18 | _ "k8s.io/code-generator/cmd/client-gen" 19 | _ "k8s.io/code-generator/cmd/conversion-gen" 20 | _ "k8s.io/code-generator/cmd/deepcopy-gen" 21 | _ "k8s.io/code-generator/cmd/defaulter-gen" 22 | _ "k8s.io/code-generator/cmd/informer-gen" 23 | _ "k8s.io/code-generator/cmd/lister-gen" 24 | _ "k8s.io/code-generator/cmd/register-gen" 25 | _ "k8s.io/code-generator/cmd/set-gen" 26 | _ "sigs.k8s.io/controller-tools/cmd/controller-gen" 27 | _ "sigs.k8s.io/testing_frameworks/integration" 28 | ) 29 | -------------------------------------------------------------------------------- /pkg/apis/addtoscheme_talos_v1alpha1.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package apis 18 | 19 | import ( 20 | "github.com/talos-systems/cluster-api-provider-talos/pkg/apis/talos/v1alpha1" 21 | ) 22 | 23 | func init() { 24 | // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back 25 | AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/apis/apis.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Generate deepcopy for apis 18 | //go:generate go run ../../vendor/k8s.io/code-generator/cmd/deepcopy-gen/main.go -O zz_generated.deepcopy -i ./... -h ../../hack/boilerplate.go.txt 19 | 20 | // Package apis contains Kubernetes API groups. 21 | package apis 22 | 23 | import ( 24 | "k8s.io/apimachinery/pkg/runtime" 25 | ) 26 | 27 | // AddToSchemes may be used to add all resources defined in the project to a Scheme 28 | var AddToSchemes runtime.SchemeBuilder 29 | 30 | // AddToScheme adds all Resources to the Scheme 31 | func AddToScheme(s *runtime.Scheme) error { 32 | return AddToSchemes.AddToScheme(s) 33 | } 34 | -------------------------------------------------------------------------------- /pkg/apis/talos/group.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package talos contains talos API versions 18 | package talos 19 | -------------------------------------------------------------------------------- /pkg/apis/talos/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha1 contains API Schema definitions for the talos v1alpha1 API group 18 | // +k8s:openapi-gen=true 19 | // +k8s:deepcopy-gen=package,register 20 | // +k8s:conversion-gen=github.com/talos-systems/cluster-api-provider-talos/pkg/apis/talos 21 | // +k8s:defaulter-gen=TypeMeta 22 | // +groupName=talos.cluster.k8s.io 23 | package v1alpha1 24 | -------------------------------------------------------------------------------- /pkg/apis/talos/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // NOTE: Boilerplate only. Ignore this file. 18 | 19 | // Package v1alpha1 contains API Schema definitions for the talos v1alpha1 API group 20 | // +k8s:openapi-gen=true 21 | // +k8s:deepcopy-gen=package,register 22 | // +k8s:conversion-gen=github.com/talos-systems/cluster-api-provider-talos/pkg/apis/talos 23 | // +k8s:defaulter-gen=TypeMeta 24 | // +groupName=talos.cluster.k8s.io 25 | package v1alpha1 26 | 27 | import ( 28 | "k8s.io/apimachinery/pkg/runtime/schema" 29 | "sigs.k8s.io/controller-runtime/pkg/runtime/scheme" 30 | ) 31 | 32 | var ( 33 | // SchemeGroupVersion is group version used to register these objects 34 | SchemeGroupVersion = schema.GroupVersion{Group: "talos.cluster.k8s.io", Version: "v1alpha1"} 35 | 36 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 37 | SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} 38 | 39 | // AddToScheme is required by pkg/client/... 40 | AddToScheme = SchemeBuilder.AddToScheme 41 | ) 42 | 43 | // Resource is required by pkg/client/listers/... 44 | func Resource(resource string) schema.GroupResource { 45 | return SchemeGroupVersion.WithResource(resource).GroupResource() 46 | } 47 | -------------------------------------------------------------------------------- /pkg/apis/talos/v1alpha1/talosclusterproviderspec_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | //TalosClusterControlPlaneSpec is the spec that defines info about cluster controlplane 24 | type TalosClusterControlPlaneSpec struct { 25 | Count int `json:"count,omitempty"` 26 | K8sVersion string `json:"k8sversion,omitempty"` 27 | } 28 | 29 | //TalosClusterPlatformSpec defines info about platform configs 30 | type TalosClusterPlatformSpec struct { 31 | Type string `json:"type,omitempty"` 32 | Config string `json:"config,omitempty"` 33 | } 34 | 35 | // TalosClusterProviderSpecStatus defines the observed state of TalosClusterProviderSpec 36 | type TalosClusterProviderSpecStatus struct { 37 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 38 | // Important: Run "make" to regenerate code after modifying this file 39 | } 40 | 41 | // +genclient 42 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 43 | 44 | // TalosClusterProviderSpec is the Schema for the talosclusterproviderspecs API 45 | // +k8s:openapi-gen=true 46 | type TalosClusterProviderSpec struct { 47 | metav1.TypeMeta `json:",inline"` 48 | metav1.ObjectMeta `json:"metadata,omitempty"` 49 | 50 | ControlPlane TalosClusterControlPlaneSpec `json:"controlplane,omitempty"` 51 | Platform TalosClusterPlatformSpec `json:"platform,omitempty"` 52 | Status TalosClusterProviderSpecStatus `json:"status,omitempty"` 53 | } 54 | 55 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 56 | 57 | // TalosClusterProviderSpecList contains a list of TalosClusterProviderSpec 58 | type TalosClusterProviderSpecList struct { 59 | metav1.TypeMeta `json:",inline"` 60 | metav1.ListMeta `json:"metadata,omitempty"` 61 | Items []TalosClusterProviderSpec `json:"items"` 62 | } 63 | 64 | func init() { 65 | SchemeBuilder.Register(&TalosClusterProviderSpec{}, &TalosClusterProviderSpecList{}) 66 | } 67 | -------------------------------------------------------------------------------- /pkg/apis/talos/v1alpha1/talosclusterproviderspec_types_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/onsi/gomega" 23 | "golang.org/x/net/context" 24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | "k8s.io/apimachinery/pkg/types" 26 | ) 27 | 28 | //TODO Add tests? 29 | func TestStorageTalosClusterProviderSpec(t *testing.T) { 30 | key := types.NamespacedName{ 31 | Name: "foo", 32 | Namespace: "default", 33 | } 34 | created := &TalosClusterProviderSpec{ 35 | ObjectMeta: metav1.ObjectMeta{ 36 | Name: "foo", 37 | Namespace: "default", 38 | }} 39 | g := gomega.NewGomegaWithT(t) 40 | 41 | // Test Create 42 | fetched := &TalosClusterProviderSpec{} 43 | g.Expect(c.Create(context.TODO(), created)).NotTo(gomega.HaveOccurred()) 44 | 45 | g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) 46 | g.Expect(fetched).To(gomega.Equal(created)) 47 | 48 | // Test Updating the Labels 49 | updated := fetched.DeepCopy() 50 | updated.Labels = map[string]string{"hello": "world"} 51 | g.Expect(c.Update(context.TODO(), updated)).NotTo(gomega.HaveOccurred()) 52 | 53 | g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) 54 | g.Expect(fetched).To(gomega.Equal(updated)) 55 | 56 | // Test Delete 57 | g.Expect(c.Delete(context.TODO(), fetched)).NotTo(gomega.HaveOccurred()) 58 | g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred()) 59 | } 60 | -------------------------------------------------------------------------------- /pkg/apis/talos/v1alpha1/talosclusterproviderstatus_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! 24 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 25 | 26 | // TalosClusterProviderStatusSpec defines the desired state of TalosClusterProviderStatus 27 | type TalosClusterProviderStatusSpec struct { 28 | // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster 29 | // Important: Run "make" to regenerate code after modifying this file 30 | } 31 | 32 | // TalosClusterProviderStatusStatus defines the observed state of TalosClusterProviderStatus 33 | type TalosClusterProviderStatusStatus struct { 34 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 35 | // Important: Run "make" to regenerate code after modifying this file 36 | } 37 | 38 | // +genclient 39 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 40 | 41 | // TalosClusterProviderStatus is the Schema for the talosclusterproviderstatuses API 42 | // +k8s:openapi-gen=true 43 | type TalosClusterProviderStatus struct { 44 | metav1.TypeMeta `json:",inline"` 45 | metav1.ObjectMeta `json:"metadata,omitempty"` 46 | 47 | Spec TalosClusterProviderStatusSpec `json:"spec,omitempty"` 48 | Status TalosClusterProviderStatusStatus `json:"status,omitempty"` 49 | } 50 | 51 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 52 | 53 | // TalosClusterProviderStatusList contains a list of TalosClusterProviderStatus 54 | type TalosClusterProviderStatusList struct { 55 | metav1.TypeMeta `json:",inline"` 56 | metav1.ListMeta `json:"metadata,omitempty"` 57 | Items []TalosClusterProviderStatus `json:"items"` 58 | } 59 | 60 | func init() { 61 | SchemeBuilder.Register(&TalosClusterProviderStatus{}, &TalosClusterProviderStatusList{}) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/apis/talos/v1alpha1/talosclusterproviderstatus_types_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/onsi/gomega" 23 | "golang.org/x/net/context" 24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | "k8s.io/apimachinery/pkg/types" 26 | ) 27 | 28 | func TestStorageTalosClusterProviderStatus(t *testing.T) { 29 | key := types.NamespacedName{ 30 | Name: "foo", 31 | Namespace: "default", 32 | } 33 | created := &TalosClusterProviderStatus{ 34 | ObjectMeta: metav1.ObjectMeta{ 35 | Name: "foo", 36 | Namespace: "default", 37 | }} 38 | g := gomega.NewGomegaWithT(t) 39 | 40 | // Test Create 41 | fetched := &TalosClusterProviderStatus{} 42 | g.Expect(c.Create(context.TODO(), created)).NotTo(gomega.HaveOccurred()) 43 | 44 | g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) 45 | g.Expect(fetched).To(gomega.Equal(created)) 46 | 47 | // Test Updating the Labels 48 | updated := fetched.DeepCopy() 49 | updated.Labels = map[string]string{"hello": "world"} 50 | g.Expect(c.Update(context.TODO(), updated)).NotTo(gomega.HaveOccurred()) 51 | 52 | g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) 53 | g.Expect(fetched).To(gomega.Equal(updated)) 54 | 55 | // Test Delete 56 | g.Expect(c.Delete(context.TODO(), fetched)).NotTo(gomega.HaveOccurred()) 57 | g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred()) 58 | } 59 | -------------------------------------------------------------------------------- /pkg/apis/talos/v1alpha1/talosmachineproviderspec_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | //TalosMachinePlatformSpec defines info about platform configs 24 | type TalosMachinePlatformSpec struct { 25 | Type string `json:"type,omitempty"` 26 | Config string `json:"config,omitempty"` 27 | } 28 | 29 | // TalosMachineProviderSpecStatus defines the observed state of TalosMachineProviderSpec 30 | type TalosMachineProviderSpecStatus struct { 31 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 32 | // Important: Run "make" to regenerate code after modifying this file 33 | } 34 | 35 | // +genclient 36 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 37 | 38 | // TalosMachineProviderSpec is the Schema for the talosmachineproviderspecs API 39 | // +k8s:openapi-gen=true 40 | type TalosMachineProviderSpec struct { 41 | metav1.TypeMeta `json:",inline"` 42 | metav1.ObjectMeta `json:"metadata,omitempty"` 43 | 44 | Platform TalosMachinePlatformSpec `json:"platform,omitempty"` 45 | Status TalosMachineProviderSpecStatus `json:"status,omitempty"` 46 | } 47 | 48 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 49 | 50 | // TalosMachineProviderSpecList contains a list of TalosMachineProviderSpec 51 | type TalosMachineProviderSpecList struct { 52 | metav1.TypeMeta `json:",inline"` 53 | metav1.ListMeta `json:"metadata,omitempty"` 54 | Items []TalosMachineProviderSpec `json:"items"` 55 | } 56 | 57 | func init() { 58 | SchemeBuilder.Register(&TalosMachineProviderSpec{}, &TalosMachineProviderSpecList{}) 59 | } 60 | -------------------------------------------------------------------------------- /pkg/apis/talos/v1alpha1/talosmachineproviderspec_types_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/onsi/gomega" 23 | "golang.org/x/net/context" 24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | "k8s.io/apimachinery/pkg/types" 26 | ) 27 | 28 | func TestStorageTalosMachineProviderSpec(t *testing.T) { 29 | key := types.NamespacedName{ 30 | Name: "foo", 31 | Namespace: "default", 32 | } 33 | created := &TalosMachineProviderSpec{ 34 | ObjectMeta: metav1.ObjectMeta{ 35 | Name: "foo", 36 | Namespace: "default", 37 | }} 38 | g := gomega.NewGomegaWithT(t) 39 | 40 | // Test Create 41 | fetched := &TalosMachineProviderSpec{} 42 | g.Expect(c.Create(context.TODO(), created)).NotTo(gomega.HaveOccurred()) 43 | 44 | g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) 45 | g.Expect(fetched).To(gomega.Equal(created)) 46 | 47 | // Test Updating the Labels 48 | updated := fetched.DeepCopy() 49 | updated.Labels = map[string]string{"hello": "world"} 50 | g.Expect(c.Update(context.TODO(), updated)).NotTo(gomega.HaveOccurred()) 51 | 52 | g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) 53 | g.Expect(fetched).To(gomega.Equal(updated)) 54 | 55 | // Test Delete 56 | g.Expect(c.Delete(context.TODO(), fetched)).NotTo(gomega.HaveOccurred()) 57 | g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred()) 58 | } 59 | -------------------------------------------------------------------------------- /pkg/apis/talos/v1alpha1/talosmachineproviderstatus_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! 24 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 25 | 26 | // TalosMachineProviderStatusSpec defines the desired state of TalosMachineProviderStatus 27 | type TalosMachineProviderStatusSpec struct { 28 | // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster 29 | // Important: Run "make" to regenerate code after modifying this file 30 | } 31 | 32 | // TalosMachineProviderStatusStatus defines the observed state of TalosMachineProviderStatus 33 | type TalosMachineProviderStatusStatus struct { 34 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 35 | // Important: Run "make" to regenerate code after modifying this file 36 | } 37 | 38 | // +genclient 39 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 40 | 41 | // TalosMachineProviderStatus is the Schema for the talosmachineproviderstatuses API 42 | // +k8s:openapi-gen=true 43 | type TalosMachineProviderStatus struct { 44 | metav1.TypeMeta `json:",inline"` 45 | metav1.ObjectMeta `json:"metadata,omitempty"` 46 | 47 | Spec TalosMachineProviderStatusSpec `json:"spec,omitempty"` 48 | Status TalosMachineProviderStatusStatus `json:"status,omitempty"` 49 | } 50 | 51 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 52 | 53 | // TalosMachineProviderStatusList contains a list of TalosMachineProviderStatus 54 | type TalosMachineProviderStatusList struct { 55 | metav1.TypeMeta `json:",inline"` 56 | metav1.ListMeta `json:"metadata,omitempty"` 57 | Items []TalosMachineProviderStatus `json:"items"` 58 | } 59 | 60 | func init() { 61 | SchemeBuilder.Register(&TalosMachineProviderStatus{}, &TalosMachineProviderStatusList{}) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/apis/talos/v1alpha1/talosmachineproviderstatus_types_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/onsi/gomega" 23 | "golang.org/x/net/context" 24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | "k8s.io/apimachinery/pkg/types" 26 | ) 27 | 28 | func TestStorageTalosMachineProviderStatus(t *testing.T) { 29 | key := types.NamespacedName{ 30 | Name: "foo", 31 | Namespace: "default", 32 | } 33 | created := &TalosMachineProviderStatus{ 34 | ObjectMeta: metav1.ObjectMeta{ 35 | Name: "foo", 36 | Namespace: "default", 37 | }} 38 | g := gomega.NewGomegaWithT(t) 39 | 40 | // Test Create 41 | fetched := &TalosMachineProviderStatus{} 42 | g.Expect(c.Create(context.TODO(), created)).NotTo(gomega.HaveOccurred()) 43 | 44 | g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) 45 | g.Expect(fetched).To(gomega.Equal(created)) 46 | 47 | // Test Updating the Labels 48 | updated := fetched.DeepCopy() 49 | updated.Labels = map[string]string{"hello": "world"} 50 | g.Expect(c.Update(context.TODO(), updated)).NotTo(gomega.HaveOccurred()) 51 | 52 | g.Expect(c.Get(context.TODO(), key, fetched)).NotTo(gomega.HaveOccurred()) 53 | g.Expect(fetched).To(gomega.Equal(updated)) 54 | 55 | // Test Delete 56 | g.Expect(c.Delete(context.TODO(), fetched)).NotTo(gomega.HaveOccurred()) 57 | g.Expect(c.Get(context.TODO(), key, fetched)).To(gomega.HaveOccurred()) 58 | } 59 | -------------------------------------------------------------------------------- /pkg/apis/talos/v1alpha1/v1alpha1_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "log" 21 | "os" 22 | "path/filepath" 23 | "testing" 24 | 25 | "k8s.io/client-go/kubernetes/scheme" 26 | "k8s.io/client-go/rest" 27 | "sigs.k8s.io/controller-runtime/pkg/client" 28 | "sigs.k8s.io/controller-runtime/pkg/envtest" 29 | ) 30 | 31 | var cfg *rest.Config 32 | var c client.Client 33 | 34 | func TestMain(m *testing.M) { 35 | t := &envtest.Environment{ 36 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crds")}, 37 | } 38 | 39 | err := SchemeBuilder.AddToScheme(scheme.Scheme) 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | 44 | if cfg, err = t.Start(); err != nil { 45 | log.Fatal(err) 46 | } 47 | 48 | if c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}); err != nil { 49 | log.Fatal(err) 50 | } 51 | 52 | code := m.Run() 53 | t.Stop() 54 | os.Exit(code) 55 | } 56 | -------------------------------------------------------------------------------- /pkg/apis/talos/v1alpha1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | // +build !ignore_autogenerated 2 | 3 | /* 4 | Copyright 2019 The Kubernetes Authors. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | // Code generated by main. DO NOT EDIT. 19 | 20 | package v1alpha1 21 | 22 | import ( 23 | runtime "k8s.io/apimachinery/pkg/runtime" 24 | ) 25 | 26 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 27 | func (in *TalosClusterControlPlaneSpec) DeepCopyInto(out *TalosClusterControlPlaneSpec) { 28 | *out = *in 29 | return 30 | } 31 | 32 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosClusterControlPlaneSpec. 33 | func (in *TalosClusterControlPlaneSpec) DeepCopy() *TalosClusterControlPlaneSpec { 34 | if in == nil { 35 | return nil 36 | } 37 | out := new(TalosClusterControlPlaneSpec) 38 | in.DeepCopyInto(out) 39 | return out 40 | } 41 | 42 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 43 | func (in *TalosClusterPlatformSpec) DeepCopyInto(out *TalosClusterPlatformSpec) { 44 | *out = *in 45 | return 46 | } 47 | 48 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosClusterPlatformSpec. 49 | func (in *TalosClusterPlatformSpec) DeepCopy() *TalosClusterPlatformSpec { 50 | if in == nil { 51 | return nil 52 | } 53 | out := new(TalosClusterPlatformSpec) 54 | in.DeepCopyInto(out) 55 | return out 56 | } 57 | 58 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 59 | func (in *TalosClusterProviderSpec) DeepCopyInto(out *TalosClusterProviderSpec) { 60 | *out = *in 61 | out.TypeMeta = in.TypeMeta 62 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 63 | out.ControlPlane = in.ControlPlane 64 | out.Platform = in.Platform 65 | out.Status = in.Status 66 | return 67 | } 68 | 69 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosClusterProviderSpec. 70 | func (in *TalosClusterProviderSpec) DeepCopy() *TalosClusterProviderSpec { 71 | if in == nil { 72 | return nil 73 | } 74 | out := new(TalosClusterProviderSpec) 75 | in.DeepCopyInto(out) 76 | return out 77 | } 78 | 79 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 80 | func (in *TalosClusterProviderSpec) DeepCopyObject() runtime.Object { 81 | if c := in.DeepCopy(); c != nil { 82 | return c 83 | } 84 | return nil 85 | } 86 | 87 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 88 | func (in *TalosClusterProviderSpecList) DeepCopyInto(out *TalosClusterProviderSpecList) { 89 | *out = *in 90 | out.TypeMeta = in.TypeMeta 91 | out.ListMeta = in.ListMeta 92 | if in.Items != nil { 93 | in, out := &in.Items, &out.Items 94 | *out = make([]TalosClusterProviderSpec, len(*in)) 95 | for i := range *in { 96 | (*in)[i].DeepCopyInto(&(*out)[i]) 97 | } 98 | } 99 | return 100 | } 101 | 102 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosClusterProviderSpecList. 103 | func (in *TalosClusterProviderSpecList) DeepCopy() *TalosClusterProviderSpecList { 104 | if in == nil { 105 | return nil 106 | } 107 | out := new(TalosClusterProviderSpecList) 108 | in.DeepCopyInto(out) 109 | return out 110 | } 111 | 112 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 113 | func (in *TalosClusterProviderSpecList) DeepCopyObject() runtime.Object { 114 | if c := in.DeepCopy(); c != nil { 115 | return c 116 | } 117 | return nil 118 | } 119 | 120 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 121 | func (in *TalosClusterProviderSpecStatus) DeepCopyInto(out *TalosClusterProviderSpecStatus) { 122 | *out = *in 123 | return 124 | } 125 | 126 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosClusterProviderSpecStatus. 127 | func (in *TalosClusterProviderSpecStatus) DeepCopy() *TalosClusterProviderSpecStatus { 128 | if in == nil { 129 | return nil 130 | } 131 | out := new(TalosClusterProviderSpecStatus) 132 | in.DeepCopyInto(out) 133 | return out 134 | } 135 | 136 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 137 | func (in *TalosClusterProviderStatus) DeepCopyInto(out *TalosClusterProviderStatus) { 138 | *out = *in 139 | out.TypeMeta = in.TypeMeta 140 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 141 | out.Spec = in.Spec 142 | out.Status = in.Status 143 | return 144 | } 145 | 146 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosClusterProviderStatus. 147 | func (in *TalosClusterProviderStatus) DeepCopy() *TalosClusterProviderStatus { 148 | if in == nil { 149 | return nil 150 | } 151 | out := new(TalosClusterProviderStatus) 152 | in.DeepCopyInto(out) 153 | return out 154 | } 155 | 156 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 157 | func (in *TalosClusterProviderStatus) DeepCopyObject() runtime.Object { 158 | if c := in.DeepCopy(); c != nil { 159 | return c 160 | } 161 | return nil 162 | } 163 | 164 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 165 | func (in *TalosClusterProviderStatusList) DeepCopyInto(out *TalosClusterProviderStatusList) { 166 | *out = *in 167 | out.TypeMeta = in.TypeMeta 168 | out.ListMeta = in.ListMeta 169 | if in.Items != nil { 170 | in, out := &in.Items, &out.Items 171 | *out = make([]TalosClusterProviderStatus, len(*in)) 172 | for i := range *in { 173 | (*in)[i].DeepCopyInto(&(*out)[i]) 174 | } 175 | } 176 | return 177 | } 178 | 179 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosClusterProviderStatusList. 180 | func (in *TalosClusterProviderStatusList) DeepCopy() *TalosClusterProviderStatusList { 181 | if in == nil { 182 | return nil 183 | } 184 | out := new(TalosClusterProviderStatusList) 185 | in.DeepCopyInto(out) 186 | return out 187 | } 188 | 189 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 190 | func (in *TalosClusterProviderStatusList) DeepCopyObject() runtime.Object { 191 | if c := in.DeepCopy(); c != nil { 192 | return c 193 | } 194 | return nil 195 | } 196 | 197 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 198 | func (in *TalosClusterProviderStatusSpec) DeepCopyInto(out *TalosClusterProviderStatusSpec) { 199 | *out = *in 200 | return 201 | } 202 | 203 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosClusterProviderStatusSpec. 204 | func (in *TalosClusterProviderStatusSpec) DeepCopy() *TalosClusterProviderStatusSpec { 205 | if in == nil { 206 | return nil 207 | } 208 | out := new(TalosClusterProviderStatusSpec) 209 | in.DeepCopyInto(out) 210 | return out 211 | } 212 | 213 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 214 | func (in *TalosClusterProviderStatusStatus) DeepCopyInto(out *TalosClusterProviderStatusStatus) { 215 | *out = *in 216 | return 217 | } 218 | 219 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosClusterProviderStatusStatus. 220 | func (in *TalosClusterProviderStatusStatus) DeepCopy() *TalosClusterProviderStatusStatus { 221 | if in == nil { 222 | return nil 223 | } 224 | out := new(TalosClusterProviderStatusStatus) 225 | in.DeepCopyInto(out) 226 | return out 227 | } 228 | 229 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 230 | func (in *TalosMachinePlatformSpec) DeepCopyInto(out *TalosMachinePlatformSpec) { 231 | *out = *in 232 | return 233 | } 234 | 235 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosMachinePlatformSpec. 236 | func (in *TalosMachinePlatformSpec) DeepCopy() *TalosMachinePlatformSpec { 237 | if in == nil { 238 | return nil 239 | } 240 | out := new(TalosMachinePlatformSpec) 241 | in.DeepCopyInto(out) 242 | return out 243 | } 244 | 245 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 246 | func (in *TalosMachineProviderSpec) DeepCopyInto(out *TalosMachineProviderSpec) { 247 | *out = *in 248 | out.TypeMeta = in.TypeMeta 249 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 250 | out.Platform = in.Platform 251 | out.Status = in.Status 252 | return 253 | } 254 | 255 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosMachineProviderSpec. 256 | func (in *TalosMachineProviderSpec) DeepCopy() *TalosMachineProviderSpec { 257 | if in == nil { 258 | return nil 259 | } 260 | out := new(TalosMachineProviderSpec) 261 | in.DeepCopyInto(out) 262 | return out 263 | } 264 | 265 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 266 | func (in *TalosMachineProviderSpec) DeepCopyObject() runtime.Object { 267 | if c := in.DeepCopy(); c != nil { 268 | return c 269 | } 270 | return nil 271 | } 272 | 273 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 274 | func (in *TalosMachineProviderSpecList) DeepCopyInto(out *TalosMachineProviderSpecList) { 275 | *out = *in 276 | out.TypeMeta = in.TypeMeta 277 | out.ListMeta = in.ListMeta 278 | if in.Items != nil { 279 | in, out := &in.Items, &out.Items 280 | *out = make([]TalosMachineProviderSpec, len(*in)) 281 | for i := range *in { 282 | (*in)[i].DeepCopyInto(&(*out)[i]) 283 | } 284 | } 285 | return 286 | } 287 | 288 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosMachineProviderSpecList. 289 | func (in *TalosMachineProviderSpecList) DeepCopy() *TalosMachineProviderSpecList { 290 | if in == nil { 291 | return nil 292 | } 293 | out := new(TalosMachineProviderSpecList) 294 | in.DeepCopyInto(out) 295 | return out 296 | } 297 | 298 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 299 | func (in *TalosMachineProviderSpecList) DeepCopyObject() runtime.Object { 300 | if c := in.DeepCopy(); c != nil { 301 | return c 302 | } 303 | return nil 304 | } 305 | 306 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 307 | func (in *TalosMachineProviderSpecStatus) DeepCopyInto(out *TalosMachineProviderSpecStatus) { 308 | *out = *in 309 | return 310 | } 311 | 312 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosMachineProviderSpecStatus. 313 | func (in *TalosMachineProviderSpecStatus) DeepCopy() *TalosMachineProviderSpecStatus { 314 | if in == nil { 315 | return nil 316 | } 317 | out := new(TalosMachineProviderSpecStatus) 318 | in.DeepCopyInto(out) 319 | return out 320 | } 321 | 322 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 323 | func (in *TalosMachineProviderStatus) DeepCopyInto(out *TalosMachineProviderStatus) { 324 | *out = *in 325 | out.TypeMeta = in.TypeMeta 326 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 327 | out.Spec = in.Spec 328 | out.Status = in.Status 329 | return 330 | } 331 | 332 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosMachineProviderStatus. 333 | func (in *TalosMachineProviderStatus) DeepCopy() *TalosMachineProviderStatus { 334 | if in == nil { 335 | return nil 336 | } 337 | out := new(TalosMachineProviderStatus) 338 | in.DeepCopyInto(out) 339 | return out 340 | } 341 | 342 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 343 | func (in *TalosMachineProviderStatus) DeepCopyObject() runtime.Object { 344 | if c := in.DeepCopy(); c != nil { 345 | return c 346 | } 347 | return nil 348 | } 349 | 350 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 351 | func (in *TalosMachineProviderStatusList) DeepCopyInto(out *TalosMachineProviderStatusList) { 352 | *out = *in 353 | out.TypeMeta = in.TypeMeta 354 | out.ListMeta = in.ListMeta 355 | if in.Items != nil { 356 | in, out := &in.Items, &out.Items 357 | *out = make([]TalosMachineProviderStatus, len(*in)) 358 | for i := range *in { 359 | (*in)[i].DeepCopyInto(&(*out)[i]) 360 | } 361 | } 362 | return 363 | } 364 | 365 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosMachineProviderStatusList. 366 | func (in *TalosMachineProviderStatusList) DeepCopy() *TalosMachineProviderStatusList { 367 | if in == nil { 368 | return nil 369 | } 370 | out := new(TalosMachineProviderStatusList) 371 | in.DeepCopyInto(out) 372 | return out 373 | } 374 | 375 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 376 | func (in *TalosMachineProviderStatusList) DeepCopyObject() runtime.Object { 377 | if c := in.DeepCopy(); c != nil { 378 | return c 379 | } 380 | return nil 381 | } 382 | 383 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 384 | func (in *TalosMachineProviderStatusSpec) DeepCopyInto(out *TalosMachineProviderStatusSpec) { 385 | *out = *in 386 | return 387 | } 388 | 389 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosMachineProviderStatusSpec. 390 | func (in *TalosMachineProviderStatusSpec) DeepCopy() *TalosMachineProviderStatusSpec { 391 | if in == nil { 392 | return nil 393 | } 394 | out := new(TalosMachineProviderStatusSpec) 395 | in.DeepCopyInto(out) 396 | return out 397 | } 398 | 399 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 400 | func (in *TalosMachineProviderStatusStatus) DeepCopyInto(out *TalosMachineProviderStatusStatus) { 401 | *out = *in 402 | return 403 | } 404 | 405 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TalosMachineProviderStatusStatus. 406 | func (in *TalosMachineProviderStatusStatus) DeepCopy() *TalosMachineProviderStatusStatus { 407 | if in == nil { 408 | return nil 409 | } 410 | out := new(TalosMachineProviderStatusStatus) 411 | in.DeepCopyInto(out) 412 | return out 413 | } 414 | -------------------------------------------------------------------------------- /pkg/cloud/talos/actuators/cluster/actuator.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package cluster 18 | 19 | import ( 20 | "context" 21 | "encoding/base64" 22 | "errors" 23 | "log" 24 | "strconv" 25 | 26 | "github.com/talos-systems/cluster-api-provider-talos/pkg/cloud/talos/provisioners" 27 | "github.com/talos-systems/cluster-api-provider-talos/pkg/cloud/talos/utils" 28 | "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" 29 | "github.com/talos-systems/talos/pkg/config/types/v1alpha1/generate" 30 | "gopkg.in/yaml.v2" 31 | v1 "k8s.io/api/core/v1" 32 | k8serrors "k8s.io/apimachinery/pkg/api/errors" 33 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 | "k8s.io/client-go/kubernetes" 35 | clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" 36 | "sigs.k8s.io/controller-runtime/pkg/client" 37 | "sigs.k8s.io/controller-runtime/pkg/manager" 38 | ) 39 | 40 | const ( 41 | ClusterAPIProviderTalosNamespace = "cluster-api-provider-talos-system" 42 | ) 43 | 44 | // ClusterActuator is responsible for performing machine reconciliation 45 | //+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete 46 | // Add RBAC rules to access cluster-api resources 47 | //+kubebuilder:rbac:groups=cluster.k8s.io,resources=clusters;clusters/status,verbs=get;list;watch 48 | type ClusterActuator struct { 49 | Clientset *kubernetes.Clientset 50 | controllerClient client.Client 51 | } 52 | 53 | // ClusterActuatorParams holds parameter information for Actuator 54 | type ClusterActuatorParams struct { 55 | } 56 | 57 | type talosConfig struct { 58 | Context string 59 | Contexts map[string]*talosConfigContext 60 | } 61 | 62 | type talosConfigContext struct { 63 | Target string 64 | CA string 65 | Crt string 66 | Key string 67 | } 68 | 69 | // NewClusterActuator creates a new Actuator 70 | func NewClusterActuator(mgr manager.Manager, params ClusterActuatorParams) (*ClusterActuator, error) { 71 | clientset, err := utils.CreateK8sClientSet() 72 | if err != nil { 73 | return nil, err 74 | } 75 | 76 | return &ClusterActuator{Clientset: clientset, controllerClient: mgr.GetClient()}, nil 77 | } 78 | 79 | // Reconcile reconciles a cluster and is invoked by the Cluster Controller 80 | // TODO: This needs to be idempotent. Check if these certs and stuff already exist 81 | func (a *ClusterActuator) Reconcile(cluster *clusterv1.Cluster) error { 82 | log.Printf("Reconciling cluster %v.", cluster.Name) 83 | 84 | spec, err := utils.ClusterProviderFromSpec(cluster.Spec.ProviderSpec) 85 | if err != nil { 86 | return err 87 | } 88 | 89 | //Generate external IPs depending on provisioner 90 | provisioner, err := provisioners.NewProvisioner(spec.Platform.Type) 91 | if err != nil { 92 | return err 93 | } 94 | 95 | masterIPs, err := provisioner.AllocateExternalIPs(cluster, a.Clientset) 96 | if err != nil { 97 | return err 98 | } 99 | 100 | //Create machine config, using IPs allocated above 101 | input, err := generate.NewInput(cluster.ObjectMeta.Name, masterIPs, spec.ControlPlane.K8sVersion) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | err = createMasterConfigMaps(cluster, a.Clientset, input) 107 | if err != nil { 108 | return err 109 | } 110 | err = createWorkerConfigMaps(cluster, a.Clientset, input) 111 | if err != nil { 112 | return err 113 | } 114 | return nil 115 | } 116 | 117 | // Delete deletes a cluster and is invoked by the Cluster Controller 118 | func (a *ClusterActuator) Delete(cluster *clusterv1.Cluster) error { 119 | 120 | //Find all machines associated with this cluster and error if there are any 121 | //Avoids orphaning machines 122 | machines := &clusterv1.MachineList{} 123 | listOptions := &client.ListOptions{} 124 | listOptions.MatchingLabels(map[string]string{"cluster.k8s.io/cluster-name": cluster.ObjectMeta.Name}) 125 | listOptions.InNamespace("") 126 | a.controllerClient.List(context.Background(), listOptions, machines) 127 | if len(machines.Items) > 0 { 128 | return errors.New("machines exist for cluster. failing to delete") 129 | } 130 | 131 | spec, err := utils.ClusterProviderFromSpec(cluster.Spec.ProviderSpec) 132 | if err != nil { 133 | return err 134 | } 135 | 136 | //Clean up external IPs depending on provisioner 137 | provisioner, err := provisioners.NewProvisioner(spec.Platform.Type) 138 | if err != nil { 139 | return err 140 | } 141 | 142 | err = provisioner.DeAllocateExternalIPs(cluster, a.Clientset) 143 | if err != nil { 144 | return err 145 | } 146 | 147 | //Clean up configmaps we create a cluster creation time 148 | err = deleteConfigMaps(cluster, a.Clientset) 149 | if err != nil { 150 | return err 151 | } 152 | 153 | return nil 154 | } 155 | 156 | // createMasterConfigMaps generates certs and creates configmaps that define the userdata for each node 157 | func createMasterConfigMaps(cluster *clusterv1.Cluster, clientset *kubernetes.Clientset, input *generate.Input) error { 158 | 159 | talosConfig := &talosConfig{ 160 | Context: input.ClusterName, 161 | Contexts: map[string]*talosConfigContext{ 162 | input.ClusterName: { 163 | Target: input.MasterIPs[0], 164 | CA: base64.StdEncoding.EncodeToString(input.Certs.OS.Crt), 165 | Crt: base64.StdEncoding.EncodeToString(input.Certs.Admin.Crt), 166 | Key: base64.StdEncoding.EncodeToString(input.Certs.Admin.Key), 167 | }, 168 | }, 169 | } 170 | 171 | talosConfigBytes, err := yaml.Marshal(talosConfig) 172 | if err != nil { 173 | helpers.Fatalf("failed to marshal config: %+v", err) 174 | } 175 | 176 | initData, err := generate.Config(generate.TypeInit, input) 177 | if err != nil { 178 | return err 179 | } 180 | 181 | allData := []string{initData} 182 | 183 | for i := 1; i < len(input.MasterIPs); i++ { 184 | 185 | controlPlaneData, err := generate.Config(generate.TypeControlPlane, input) 186 | if err != nil { 187 | return err 188 | } 189 | allData = append(allData, controlPlaneData) 190 | } 191 | 192 | for index, userdata := range allData { 193 | name := cluster.ObjectMeta.Name + "-master-" + strconv.Itoa(index) 194 | data := map[string]string{"userdata": userdata, "talosconfig": string(talosConfigBytes)} 195 | 196 | if err := createConfigMap(clientset, name, data); err != nil { 197 | return err 198 | } 199 | } 200 | 201 | return nil 202 | } 203 | 204 | // createWorkerConfigMaps creates a configmap for a machineset of workers 205 | func createWorkerConfigMaps(cluster *clusterv1.Cluster, clientset *kubernetes.Clientset, input *generate.Input) error { 206 | workerData, err := generate.Config(generate.TypeJoin, input) 207 | if err != nil { 208 | return err 209 | } 210 | 211 | name := cluster.ObjectMeta.Name + "-workers" 212 | data := map[string]string{"userdata": workerData} 213 | 214 | return createConfigMap(clientset, name, data) 215 | } 216 | 217 | // createConfigMap creates a config map in kubernetes with given data 218 | func createConfigMap(clientset *kubernetes.Clientset, name string, data map[string]string) error { 219 | cm := &v1.ConfigMap{ 220 | TypeMeta: metav1.TypeMeta{ 221 | Kind: "ConfigMap", 222 | APIVersion: "v1", 223 | }, 224 | ObjectMeta: metav1.ObjectMeta{ 225 | Name: name, 226 | Namespace: ClusterAPIProviderTalosNamespace, 227 | }, 228 | Data: data, 229 | } 230 | 231 | _, err := clientset.CoreV1().ConfigMaps(ClusterAPIProviderTalosNamespace).Create(cm) 232 | // Essentially no-op if CMs are already there 233 | if k8serrors.IsAlreadyExists(err) { 234 | return nil 235 | //_, err = clientset.CoreV1().ConfigMaps(ClusterAPIProviderTalosNamespace).Update(cm) 236 | } 237 | 238 | return err 239 | } 240 | 241 | // deleteConfigMaps cleans up all configmaps associated with this cluster 242 | func deleteConfigMaps(cluster *clusterv1.Cluster, clientset *kubernetes.Clientset) error { 243 | // TODO(andrewrynhard): We should add labels to the ConfigMaps and use 244 | // the lables to find the ConfigMaps. 245 | spec, err := utils.ClusterProviderFromSpec(cluster.Spec.ProviderSpec) 246 | if err != nil { 247 | return err 248 | } 249 | 250 | for index := 0; index < spec.ControlPlane.Count; index++ { 251 | name := cluster.ObjectMeta.Name + "-master-" + strconv.Itoa(index) 252 | err = clientset.CoreV1().ConfigMaps(ClusterAPIProviderTalosNamespace).Delete(name, nil) 253 | if err != nil && !k8serrors.IsNotFound(err) { 254 | return err 255 | } 256 | } 257 | 258 | err = clientset.CoreV1().ConfigMaps(ClusterAPIProviderTalosNamespace).Delete(cluster.ObjectMeta.Name+"-workers", nil) 259 | if err != nil && !k8serrors.IsNotFound(err) { 260 | return err 261 | } 262 | 263 | return nil 264 | } 265 | -------------------------------------------------------------------------------- /pkg/cloud/talos/actuators/machine/actuator.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 The Kubernetes authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package machine 18 | 19 | import ( 20 | "context" 21 | "log" 22 | 23 | "github.com/talos-systems/cluster-api-provider-talos/pkg/cloud/talos/provisioners" 24 | "github.com/talos-systems/cluster-api-provider-talos/pkg/cloud/talos/utils" 25 | "k8s.io/client-go/kubernetes" 26 | clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" 27 | "sigs.k8s.io/controller-runtime/pkg/client" 28 | "sigs.k8s.io/controller-runtime/pkg/manager" 29 | ) 30 | 31 | const ( 32 | ProviderName = "talos" 33 | ) 34 | 35 | // Add RBAC rules to access cluster-api resources 36 | //+kubebuilder:rbac:groups=cluster.k8s.io,resources=machines;machines/status;machinedeployments;machinedeployments/status;machinesets;machinesets/status;machineclasses,verbs=get;list;watch;create;update;patch;delete 37 | //+kubebuilder:rbac:groups=cluster.k8s.io,resources=clusters;clusters/status,verbs=get;list;watch;create;update;patch;delete 38 | //+kubebuilder:rbac:groups="",resources=nodes;events,verbs=get;list;watch;create;update;patch;delete 39 | 40 | // MachineActuator is responsible for performing machine reconciliation 41 | type MachineActuator struct { 42 | Clientset *kubernetes.Clientset 43 | controllerClient client.Client 44 | } 45 | 46 | // MachineActuatorParams holds parameter information for Actuator 47 | type MachineActuatorParams struct{} 48 | 49 | // NewMachineActuator creates a new Actuator 50 | func NewMachineActuator(mgr manager.Manager, params MachineActuatorParams) (*MachineActuator, error) { 51 | 52 | clientset, err := utils.CreateK8sClientSet() 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | return &MachineActuator{Clientset: clientset, controllerClient: mgr.GetClient()}, nil 58 | } 59 | 60 | // Create creates a machine and is invoked by the Machine Controller 61 | func (a *MachineActuator) Create(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine) error { 62 | log.Printf("Creating machine %v for cluster %v.", machine.Name, cluster.Name) 63 | 64 | spec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | provisioner, err := provisioners.NewProvisioner(spec.Platform.Type) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | err = provisioner.Create(ctx, cluster, machine, a.Clientset) 75 | if err != nil { 76 | return err 77 | } 78 | 79 | return nil 80 | } 81 | 82 | // Delete deletes a machine and is invoked by the Machine Controller 83 | func (a *MachineActuator) Delete(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine) error { 84 | log.Printf("Deleting machine %v for cluster %v.", machine.Name, cluster.Name) 85 | 86 | spec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 87 | if err != nil { 88 | return err 89 | } 90 | 91 | provisioner, err := provisioners.NewProvisioner(spec.Platform.Type) 92 | if err != nil { 93 | return err 94 | } 95 | 96 | err = provisioner.Delete(ctx, cluster, machine, a.Clientset) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | return nil 102 | } 103 | 104 | // Update updates a machine and is invoked by the Machine Controller 105 | func (a *MachineActuator) Update(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine) error { 106 | log.Printf("Updating machine %v for cluster %v.", machine.Name, cluster.Name) 107 | 108 | spec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 109 | if err != nil { 110 | return err 111 | } 112 | 113 | provisioner, err := provisioners.NewProvisioner(spec.Platform.Type) 114 | if err != nil { 115 | return err 116 | } 117 | 118 | err = provisioner.Update(ctx, cluster, machine, a.Clientset) 119 | if err != nil { 120 | return err 121 | } 122 | 123 | return nil 124 | } 125 | 126 | // Exists tests for the existence of a machine and is invoked by the Machine Controller 127 | func (a *MachineActuator) Exists(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine) (bool, error) { 128 | log.Printf("Checking if machine %v for cluster %v exists.", machine.Name, cluster.Name) 129 | 130 | spec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 131 | if err != nil { 132 | return true, err 133 | } 134 | 135 | provisioner, err := provisioners.NewProvisioner(spec.Platform.Type) 136 | if err != nil { 137 | return true, err 138 | } 139 | 140 | exists, err := provisioner.Exists(ctx, cluster, machine, a.Clientset) 141 | if err != nil { 142 | return true, err 143 | } 144 | 145 | return exists, nil 146 | } 147 | -------------------------------------------------------------------------------- /pkg/cloud/talos/provisioners/aws/aws.go: -------------------------------------------------------------------------------- 1 | package aws 2 | 3 | import ( 4 | "context" 5 | "encoding/base64" 6 | "errors" 7 | "log" 8 | "strconv" 9 | "strings" 10 | "time" 11 | 12 | awspkg "github.com/aws/aws-sdk-go/aws" 13 | "github.com/aws/aws-sdk-go/aws/session" 14 | "github.com/aws/aws-sdk-go/service/ec2" 15 | "github.com/talos-systems/cluster-api-provider-talos/pkg/cloud/talos/utils" 16 | "gopkg.in/yaml.v2" 17 | "k8s.io/client-go/kubernetes" 18 | clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" 19 | ) 20 | 21 | // AWS represents a provider for AWS. 22 | type AWS struct { 23 | } 24 | 25 | // ClusterInfo holds data about desired config in cluster object 26 | type ClusterInfo struct { 27 | Region string 28 | } 29 | 30 | // MachineInfo holds data about desired config in cluster object 31 | type MachineInfo struct { 32 | Region string 33 | Instances InstanceInfo 34 | } 35 | 36 | // InstanceInfo holds data about the instances we'll create 37 | type InstanceInfo struct { 38 | Type string 39 | AMI string 40 | Keypair string 41 | SecurityGroups []string 42 | Disks DiskInfo 43 | } 44 | 45 | // DiskInfo holds disk info data 46 | type DiskInfo struct { 47 | Size int 48 | } 49 | 50 | //NewAWS returns an instance of the AWS provisioner 51 | func NewAWS() (*AWS, error) { 52 | return &AWS{}, nil 53 | } 54 | 55 | // Create creates an instance in AWS. 56 | func (aws *AWS) Create(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) error { 57 | 58 | machineSpec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 59 | if err != nil { 60 | return err 61 | } 62 | 63 | awsConfig := &MachineInfo{} 64 | yaml.Unmarshal([]byte(machineSpec.Platform.Config), awsConfig) 65 | 66 | ec2client, err := client(awsConfig.Region) 67 | if err != nil { 68 | return err 69 | } 70 | 71 | //fetch floating IP if master and userdata based on machine name 72 | natIP := "" 73 | if strings.Contains(machine.ObjectMeta.Name, "master") { 74 | 75 | // Find public ip 76 | address, err := getPublicIPByName(ec2client, machine.ObjectMeta.Name+"-ip") 77 | if err != nil { 78 | return err 79 | } 80 | if address == nil { 81 | return errors.New("IP not ready") 82 | } 83 | natIP = *address.PublicIp 84 | } 85 | 86 | udConfigMap, err := utils.FetchConfigMap(cluster, machine, clientset) 87 | if err != nil { 88 | return err 89 | } 90 | ud := udConfigMap.Data["userdata"] 91 | udb64 := base64.StdEncoding.EncodeToString([]byte(ud)) 92 | 93 | // Create our ec2 instance and wait for it to be running 94 | instanceInput := &ec2.RunInstancesInput{ 95 | ImageId: awspkg.String(awsConfig.Instances.AMI), 96 | InstanceType: awspkg.String(awsConfig.Instances.Type), 97 | MinCount: awspkg.Int64(1), 98 | MaxCount: awspkg.Int64(1), 99 | KeyName: awspkg.String(awsConfig.Instances.Keypair), 100 | UserData: awspkg.String(udb64), 101 | } 102 | 103 | res, err := ec2client.RunInstances(instanceInput) 104 | if err != nil { 105 | return err 106 | } 107 | instanceID := *res.Instances[0].InstanceId 108 | 109 | //Wait for instance to be running, then fetch and associate pre-existing Elastic IP if needed 110 | if natIP != "" { 111 | //TODO: Should probably attempt to call delete if running status fails 112 | err = waitForStatus(instanceID, "running", ec2client) 113 | if err != nil { 114 | return err 115 | } 116 | descAddrInput := &ec2.DescribeAddressesInput{PublicIps: []*string{awspkg.String(natIP)}} 117 | addresses, err := ec2client.DescribeAddresses(descAddrInput) 118 | if err != nil { 119 | return err 120 | } 121 | 122 | assocAddrInput := &ec2.AssociateAddressInput{ 123 | AllocationId: addresses.Addresses[0].AllocationId, 124 | NetworkInterfaceId: res.Instances[0].NetworkInterfaces[0].NetworkInterfaceId, 125 | } 126 | _, err = ec2client.AssociateAddress(assocAddrInput) 127 | if err != nil { 128 | return err 129 | } 130 | } 131 | 132 | // Add tags to the created instance 133 | _, err = ec2client.CreateTags(&ec2.CreateTagsInput{ 134 | Resources: []*string{res.Instances[0].InstanceId}, 135 | Tags: []*ec2.Tag{ 136 | { 137 | Key: awspkg.String("Name"), 138 | Value: awspkg.String(machine.ObjectMeta.Name), 139 | }, 140 | { 141 | Key: awspkg.String("TalosClusterName"), 142 | Value: awspkg.String(cluster.ObjectMeta.Name), 143 | }, 144 | }, 145 | }) 146 | if err != nil { 147 | return err 148 | } 149 | 150 | log.Println("[AWS] Instance created with ID:" + instanceID) 151 | return nil 152 | } 153 | 154 | //Update updates a given AWS instance. 155 | func (aws *AWS) Update(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) error { 156 | return nil 157 | } 158 | 159 | // Delete deletes a AWS instance. 160 | func (aws *AWS) Delete(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) error { 161 | 162 | // Fish out configs and create an ec2 client 163 | machineSpec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 164 | if err != nil { 165 | return err 166 | } 167 | 168 | awsConfig := &MachineInfo{} 169 | yaml.Unmarshal([]byte(machineSpec.Platform.Config), awsConfig) 170 | 171 | ec2client, err := client(awsConfig.Region) 172 | if err != nil { 173 | return err 174 | } 175 | 176 | //Make sure instance exists before attempting delete 177 | exists, err := aws.Exists(ctx, cluster, machine, clientset) 178 | if err != nil { 179 | return err 180 | } 181 | if !exists { 182 | return nil 183 | } 184 | 185 | //Find instance id 186 | id, err := fetchInstanceID(cluster.ObjectMeta.Name, machine.ObjectMeta.Name, ec2client) 187 | if err != nil { 188 | return err 189 | } 190 | 191 | //Stop Instance 192 | input := &ec2.TerminateInstancesInput{ 193 | InstanceIds: []*string{id}, 194 | } 195 | _, err = ec2client.TerminateInstances(input) 196 | if err != nil { 197 | return err 198 | } 199 | return nil 200 | } 201 | 202 | // Exists returns whether or not an instance is present in AWS. 203 | func (aws *AWS) Exists(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) (bool, error) { 204 | // Fish out configs and create an ec2 client 205 | machineSpec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 206 | if err != nil { 207 | return false, err 208 | } 209 | 210 | awsConfig := &MachineInfo{} 211 | yaml.Unmarshal([]byte(machineSpec.Platform.Config), awsConfig) 212 | 213 | ec2client, err := client(awsConfig.Region) 214 | if err != nil { 215 | return false, err 216 | } 217 | 218 | id, err := fetchInstanceID(cluster.ObjectMeta.Name, machine.ObjectMeta.Name, ec2client) 219 | if err != nil { 220 | return false, err 221 | } 222 | 223 | if id == nil { 224 | return false, nil 225 | } 226 | return true, nil 227 | } 228 | 229 | // AllocateExternalIPs creates IPs for the control plane nodes 230 | func (aws *AWS) AllocateExternalIPs(cluster *clusterv1.Cluster, clientset *kubernetes.Clientset) ([]string, error) { 231 | // Fish out configs and create an ec2 client 232 | clusterSpec, err := utils.ClusterProviderFromSpec(cluster.Spec.ProviderSpec) 233 | if err != nil { 234 | return nil, err 235 | } 236 | 237 | awsConfig := &ClusterInfo{} 238 | yaml.Unmarshal([]byte(clusterSpec.Platform.Config), awsConfig) 239 | 240 | ec2client, err := client(awsConfig.Region) 241 | if err != nil { 242 | return nil, err 243 | } 244 | 245 | floatingIPs := []string{} 246 | for i := 0; i < clusterSpec.ControlPlane.Count; i++ { 247 | 248 | // Check if ips already exist and add to list early if so 249 | flip, err := getPublicIPByName(ec2client, cluster.ObjectMeta.Name+"-master-"+strconv.Itoa(i)+"-ip") 250 | if err != nil { 251 | return nil, err 252 | } 253 | if flip != nil { 254 | floatingIPs = append(floatingIPs, *flip.PublicIp) 255 | continue 256 | } 257 | 258 | // Allocate elastic ip 259 | allocRes, err := ec2client.AllocateAddress(&ec2.AllocateAddressInput{ 260 | Domain: awspkg.String("vpc"), 261 | }) 262 | if err != nil { 263 | return nil, err 264 | } 265 | 266 | // Add tags to the created instance 267 | _, err = ec2client.CreateTags(&ec2.CreateTagsInput{ 268 | Resources: []*string{allocRes.AllocationId}, 269 | Tags: []*ec2.Tag{ 270 | { 271 | Key: awspkg.String("Name"), 272 | Value: awspkg.String(cluster.ObjectMeta.Name + "-master-" + strconv.Itoa(i) + "-ip"), 273 | }, 274 | }, 275 | }) 276 | if err != nil { 277 | return nil, err 278 | } 279 | 280 | floatingIPs = append(floatingIPs, *allocRes.PublicIp) 281 | } 282 | 283 | return floatingIPs, nil 284 | } 285 | 286 | // DeAllocateExternalIPs cleans IPs for the control plane nodes 287 | func (aws *AWS) DeAllocateExternalIPs(cluster *clusterv1.Cluster, clientset *kubernetes.Clientset) error { 288 | // Fish out configs and create an ec2 client 289 | clusterSpec, err := utils.ClusterProviderFromSpec(cluster.Spec.ProviderSpec) 290 | if err != nil { 291 | return err 292 | } 293 | 294 | awsConfig := &ClusterInfo{} 295 | yaml.Unmarshal([]byte(clusterSpec.Platform.Config), awsConfig) 296 | 297 | ec2client, err := client(awsConfig.Region) 298 | if err != nil { 299 | return err 300 | } 301 | 302 | for i := 0; i < clusterSpec.ControlPlane.Count; i++ { 303 | 304 | // Check if ips already exist and add to list early if so 305 | flip, err := getPublicIPByName(ec2client, cluster.ObjectMeta.Name+"-master-"+strconv.Itoa(i)+"-ip") 306 | if err != nil { 307 | return err 308 | } 309 | 310 | // Ignore if not found 311 | if flip == nil { 312 | return nil 313 | } 314 | 315 | _, err = ec2client.ReleaseAddress(&ec2.ReleaseAddressInput{ 316 | AllocationId: flip.AllocationId, 317 | }) 318 | if err != nil { 319 | return err 320 | } 321 | } 322 | 323 | return nil 324 | } 325 | 326 | // getPublicIPbyName finds the public IP object from a list of all IP objects 327 | func getPublicIPByName(ec2client *ec2.EC2, name string) (*ec2.Address, error) { 328 | result, err := ec2client.DescribeAddresses(&ec2.DescribeAddressesInput{ 329 | Filters: []*ec2.Filter{ 330 | { 331 | Name: awspkg.String("tag:Name"), 332 | Values: awspkg.StringSlice([]string{name}), 333 | }, 334 | }, 335 | }) 336 | if err != nil { 337 | return nil, err 338 | } 339 | 340 | if len(result.Addresses) > 0 { 341 | return result.Addresses[0], nil 342 | } 343 | 344 | // Not found 345 | return nil, nil 346 | } 347 | 348 | // client generates an ec2 client to use 349 | func client(region string) (*ec2.EC2, error) { 350 | sess, err := session.NewSession(&awspkg.Config{ 351 | Region: awspkg.String(region)}, 352 | ) 353 | if err != nil { 354 | return nil, err 355 | } 356 | 357 | ec2client := ec2.New(sess) 358 | return ec2client, nil 359 | } 360 | 361 | //fetchInstance ID searches AWS for instance name and the cluster name tags that we add during instance creation. Returns the instance ID. 362 | func fetchInstanceID(clusterName string, instanceName string, client *ec2.EC2) (*string, error) { 363 | 364 | instanceFilters := &ec2.DescribeInstancesInput{ 365 | Filters: []*ec2.Filter{ 366 | &ec2.Filter{ 367 | Name: awspkg.String("tag:Name"), 368 | Values: []*string{ 369 | awspkg.String(instanceName), 370 | }, 371 | }, 372 | &ec2.Filter{ 373 | Name: awspkg.String("tag:TalosClusterName"), 374 | Values: []*string{ 375 | awspkg.String(clusterName), 376 | }, 377 | }, 378 | &ec2.Filter{ 379 | Name: awspkg.String("instance-state-name"), 380 | Values: []*string{awspkg.String("running"), awspkg.String("pending"), awspkg.String("stopped")}, 381 | }, 382 | }, 383 | } 384 | res, err := client.DescribeInstances(instanceFilters) 385 | if err != nil { 386 | return nil, err 387 | } 388 | if len(res.Reservations) == 0 { 389 | return nil, nil 390 | } 391 | if len(res.Reservations[0].Instances) > 1 { 392 | return nil, errors.New("[AWS] Multiple instances with same filter info") 393 | } 394 | 395 | return res.Reservations[0].Instances[0].InstanceId, nil 396 | 397 | } 398 | 399 | //waitForStatus polls the AWS api for a certain instance status 400 | //needed for attaching elastic IP after boot 401 | func waitForStatus(instanceID string, desiredState string, client *ec2.EC2) error { 402 | 403 | timeout := time.After(120 * time.Second) 404 | tick := time.Tick(3 * time.Second) 405 | 406 | for { 407 | select { 408 | case <-timeout: 409 | return errors.New("[AWS] Timed out waiting for running instance: " + instanceID) 410 | case <-tick: 411 | instanceInput := &ec2.DescribeInstancesInput{ 412 | InstanceIds: []*string{awspkg.String(instanceID)}, 413 | } 414 | res, err := client.DescribeInstances(instanceInput) 415 | if err != nil { 416 | return err 417 | } 418 | 419 | if *res.Reservations[0].Instances[0].State.Name == desiredState { 420 | return nil 421 | } 422 | } 423 | } 424 | } 425 | -------------------------------------------------------------------------------- /pkg/cloud/talos/provisioners/azure/azure.go: -------------------------------------------------------------------------------- 1 | package azure 2 | 3 | import ( 4 | "context" 5 | "encoding/base64" 6 | "encoding/json" 7 | "errors" 8 | "io/ioutil" 9 | "log" 10 | "net/http" 11 | "os" 12 | "strconv" 13 | "strings" 14 | 15 | "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" 16 | "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-04-01/network" 17 | "github.com/Azure/go-autorest/autorest" 18 | azuresdk "github.com/Azure/go-autorest/autorest/azure" 19 | "github.com/Azure/go-autorest/autorest/azure/auth" 20 | "github.com/Azure/go-autorest/autorest/to" 21 | "github.com/talos-systems/cluster-api-provider-talos/pkg/cloud/talos/utils" 22 | "gopkg.in/yaml.v2" 23 | "k8s.io/client-go/kubernetes" 24 | clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" 25 | ) 26 | 27 | // Az represents a provider for Azure. 28 | type Az struct { 29 | } 30 | 31 | // ClusterInfo holds data about desired config in cluster object 32 | type ClusterInfo struct { 33 | Location string 34 | ResourceGroup string 35 | } 36 | 37 | // MachineInfo holds data about desired config in machine object 38 | type MachineInfo struct { 39 | Location string 40 | ResourceGroup string 41 | Instances InstanceInfo 42 | } 43 | 44 | // InstanceInfo holds data about the instances we'll create 45 | type InstanceInfo struct { 46 | Type string 47 | Image string 48 | Network string 49 | Subnet string 50 | Disks DiskInfo 51 | } 52 | 53 | // DiskInfo holds disk info data 54 | type DiskInfo struct { 55 | Size int 56 | } 57 | 58 | // Session is an object representing session for subscription 59 | type Session struct { 60 | SubscriptionID string 61 | Authorizer autorest.Authorizer 62 | } 63 | 64 | // NewAz returns an instance of the Azure provisioner 65 | func NewAz() (*Az, error) { 66 | return &Az{}, nil 67 | } 68 | 69 | // Create creates an instance in Azure. 70 | func (azure *Az) Create(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) error { 71 | 72 | machineSpec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 73 | if err != nil { 74 | return err 75 | } 76 | 77 | azureConfig := &MachineInfo{} 78 | yaml.Unmarshal([]byte(machineSpec.Platform.Config), azureConfig) 79 | 80 | // Dig out the subnet for our nic 81 | subnet, err := getSubnetByName(ctx, azureConfig) 82 | if err != nil { 83 | return err 84 | } 85 | 86 | // Begin crafting the config for the nic 87 | nicIPConfigProperties := &network.InterfaceIPConfigurationPropertiesFormat{ 88 | PrivateIPAllocationMethod: network.Dynamic, 89 | Subnet: subnet, 90 | } 91 | 92 | // Find the public IP we want to use if necessary 93 | if !strings.Contains(machine.ObjectMeta.Name, "worker") { 94 | publicIPObject, err := getPublicIPByName(ctx, machine.ObjectMeta.Name+"-ip", azureConfig.ResourceGroup) 95 | if err != nil { 96 | return err 97 | } 98 | 99 | // If we don't receive an IP object, the public IP wasn't found 100 | // even though we're expecting it to be there. This logic may 101 | // eventually change once we create IPs for the user. 102 | if publicIPObject == nil { 103 | return errors.New("expected public IP not found") 104 | } 105 | nicIPConfigProperties.PublicIPAddress = publicIPObject 106 | } 107 | 108 | // Create a network interface for our VM to use (with flip attached if necessary) 109 | nic := network.Interface{ 110 | Name: to.StringPtr(machine.ObjectMeta.Name + "-nic"), 111 | Location: to.StringPtr(azureConfig.Location), 112 | InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ 113 | IPConfigurations: &[]network.InterfaceIPConfiguration{ 114 | { 115 | Name: to.StringPtr(machine.ObjectMeta.Name + "-ip-config"), 116 | InterfaceIPConfigurationPropertiesFormat: nicIPConfigProperties, 117 | }, 118 | }, 119 | }, 120 | } 121 | nicClient, err := nicclient() 122 | if err != nil { 123 | return err 124 | } 125 | nicfuture, err := nicClient.CreateOrUpdate(ctx, azureConfig.ResourceGroup, *nic.Name, nic) 126 | if err != nil { 127 | return err 128 | } 129 | err = nicfuture.WaitForCompletionRef(ctx, nicClient.Client) 130 | if err != nil { 131 | return err 132 | } 133 | nicObject, err := nicfuture.Result(*nicClient) 134 | if err != nil { 135 | return err 136 | } 137 | 138 | // Pull down userdata and b64 encode it 139 | udConfigMap, err := utils.FetchConfigMap(cluster, machine, clientset) 140 | if err != nil { 141 | return err 142 | } 143 | ud := udConfigMap.Data["userdata"] 144 | udb64 := base64.StdEncoding.EncodeToString([]byte(ud)) 145 | 146 | // Specify dummy val pass. We don't it anyways but it's required. 147 | user := "talosuser" 148 | password := utils.RandomString(15) 149 | 150 | // Draft and create VM 151 | vm := compute.VirtualMachine{ 152 | Location: to.StringPtr(azureConfig.Location), 153 | VirtualMachineProperties: &compute.VirtualMachineProperties{ 154 | HardwareProfile: &compute.HardwareProfile{ 155 | VMSize: compute.VirtualMachineSizeTypes(azureConfig.Instances.Type), 156 | }, 157 | StorageProfile: &compute.StorageProfile{ 158 | ImageReference: &compute.ImageReference{ID: to.StringPtr(azureConfig.Instances.Image)}, 159 | OsDisk: &compute.OSDisk{ 160 | OsType: compute.Linux, 161 | Name: to.StringPtr(machine.ObjectMeta.Name + "-os-disk"), 162 | CreateOption: compute.DiskCreateOptionTypesFromImage, 163 | DiskSizeGB: to.Int32Ptr(int32(azureConfig.Instances.Disks.Size)), 164 | }, 165 | }, 166 | OsProfile: &compute.OSProfile{ 167 | ComputerName: to.StringPtr(machine.ObjectMeta.Name), 168 | AdminUsername: to.StringPtr(user), 169 | AdminPassword: to.StringPtr(password), 170 | CustomData: to.StringPtr(udb64), 171 | }, 172 | NetworkProfile: &compute.NetworkProfile{ 173 | NetworkInterfaces: &[]compute.NetworkInterfaceReference{ 174 | { 175 | ID: nicObject.ID, 176 | NetworkInterfaceReferenceProperties: &compute.NetworkInterfaceReferenceProperties{ 177 | Primary: to.BoolPtr(true), 178 | }, 179 | }, 180 | }, 181 | }, 182 | }, 183 | } 184 | 185 | vmClient, err := vmclient() 186 | if err != nil { 187 | return err 188 | } 189 | 190 | _, err = vmClient.CreateOrUpdate(ctx, azureConfig.ResourceGroup, machine.ObjectMeta.Name, vm) 191 | if err != nil { 192 | return err 193 | } 194 | 195 | log.Println("[Azure] Instance created: " + machine.ObjectMeta.Name) 196 | 197 | return nil 198 | } 199 | 200 | //Update updates a given Azure instance. 201 | func (azure *Az) Update(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) error { 202 | return nil 203 | } 204 | 205 | // Delete deletes a Azure instance. 206 | func (azure *Az) Delete(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) error { 207 | machineSpec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 208 | if err != nil { 209 | return err 210 | } 211 | 212 | azureConfig := &MachineInfo{} 213 | yaml.Unmarshal([]byte(machineSpec.Platform.Config), azureConfig) 214 | 215 | // Cleanup VM 216 | vmClient, err := vmclient() 217 | if err != nil { 218 | return err 219 | } 220 | vmfuture, err := vmClient.Delete(ctx, azureConfig.ResourceGroup, machine.ObjectMeta.Name) 221 | if err != nil { 222 | return err 223 | } 224 | 225 | // If VM is still deleting, return error so the rest of the cleanup is requeued 226 | // Azure returns a 204 when the VM isn't found 227 | if vmfuture.Response().StatusCode != http.StatusNoContent { 228 | return errors.New("[Azure] Waiting for VM completion to be completed") 229 | } 230 | 231 | // Cleanup os disk 232 | disksClient, err := disksclient() 233 | if err != nil { 234 | return err 235 | } 236 | _, err = disksClient.Delete(ctx, azureConfig.ResourceGroup, machine.ObjectMeta.Name+"-os-disk") 237 | if err != nil { 238 | return err 239 | } 240 | 241 | // Cleanup nic 242 | nicClient, err := nicclient() 243 | if err != nil { 244 | return err 245 | } 246 | _, err = nicClient.Delete(ctx, azureConfig.ResourceGroup, machine.ObjectMeta.Name+"-nic") 247 | if err != nil { 248 | return err 249 | } 250 | 251 | log.Println("[Azure] Instance deleted: " + machine.ObjectMeta.Name) 252 | 253 | return nil 254 | } 255 | 256 | // Exists returns whether or not an instance is present in Azure. 257 | func (azure *Az) Exists(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) (bool, error) { 258 | machineSpec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 259 | if err != nil { 260 | return false, err 261 | } 262 | 263 | azureConfig := &MachineInfo{} 264 | yaml.Unmarshal([]byte(machineSpec.Platform.Config), azureConfig) 265 | 266 | vmClient, err := vmclient() 267 | if err != nil { 268 | return true, err 269 | } 270 | 271 | // If there's an error from retrieving the VM by name, assume it doesn't exist 272 | _, err = getVMByName(ctx, vmClient, azureConfig.ResourceGroup, machine.ObjectMeta.Name) 273 | if err != nil { 274 | return false, nil 275 | } 276 | 277 | return true, nil 278 | } 279 | 280 | // AllocateExternalIPs creates IPs for the control plane nodes 281 | func (azure *Az) AllocateExternalIPs(cluster *clusterv1.Cluster, clientset *kubernetes.Clientset) ([]string, error) { 282 | 283 | clusterSpec, err := utils.ClusterProviderFromSpec(cluster.Spec.ProviderSpec) 284 | if err != nil { 285 | return nil, err 286 | } 287 | 288 | azureConfig := &ClusterInfo{} 289 | yaml.Unmarshal([]byte(clusterSpec.Platform.Config), azureConfig) 290 | client, err := ipclient() 291 | if err != nil { 292 | return nil, err 293 | } 294 | 295 | ctx := context.Background() 296 | floatingIPs := []string{} 297 | for i := 0; i < clusterSpec.ControlPlane.Count; i++ { 298 | 299 | // Check if ips already exist and add to list early if so 300 | flip, err := getPublicIPByName(ctx, cluster.ObjectMeta.Name+"-master-"+strconv.Itoa(i)+"-ip", azureConfig.ResourceGroup) 301 | if err != nil { 302 | return nil, err 303 | } 304 | if flip != nil { 305 | floatingIPs = append(floatingIPs, *flip.PublicIPAddressPropertiesFormat.IPAddress) 306 | continue 307 | } 308 | 309 | // Create IP if needed 310 | result, err := client.CreateOrUpdate( 311 | ctx, 312 | azureConfig.ResourceGroup, 313 | cluster.ObjectMeta.Name+"-master-"+strconv.Itoa(i)+"-ip", 314 | network.PublicIPAddress{ 315 | Sku: &network.PublicIPAddressSku{ 316 | Name: network.PublicIPAddressSkuNameBasic, 317 | }, 318 | PublicIPAddressPropertiesFormat: &network.PublicIPAddressPropertiesFormat{ 319 | PublicIPAllocationMethod: network.Static, 320 | PublicIPAddressVersion: network.IPv4, 321 | }, 322 | Location: to.StringPtr(azureConfig.Location), 323 | }, 324 | ) 325 | 326 | err = result.WaitForCompletionRef(ctx, client.Client) 327 | if err != nil { 328 | return nil, err 329 | } 330 | 331 | ip, err := result.Result(*client) 332 | if err != nil { 333 | return nil, err 334 | } 335 | floatingIPs = append(floatingIPs, *ip.PublicIPAddressPropertiesFormat.IPAddress) 336 | } 337 | return floatingIPs, nil 338 | } 339 | 340 | // DeAllocateExternalIPs cleans IPs for the control plane nodes 341 | func (azure *Az) DeAllocateExternalIPs(cluster *clusterv1.Cluster, clientset *kubernetes.Clientset) error { 342 | 343 | clusterSpec, err := utils.ClusterProviderFromSpec(cluster.Spec.ProviderSpec) 344 | if err != nil { 345 | return err 346 | } 347 | 348 | azureConfig := &ClusterInfo{} 349 | yaml.Unmarshal([]byte(clusterSpec.Platform.Config), azureConfig) 350 | client, err := ipclient() 351 | if err != nil { 352 | return err 353 | } 354 | 355 | ctx := context.Background() 356 | 357 | for i := 0; i < clusterSpec.ControlPlane.Count; i++ { 358 | _, err := client.Delete(ctx, azureConfig.ResourceGroup, cluster.ObjectMeta.Name+"-master-"+strconv.Itoa(i)+"-ip") 359 | if err != nil { 360 | return err 361 | } 362 | } 363 | return nil 364 | } 365 | 366 | // getSubnetByName finds a given subnet (required for input along with network) 367 | func getSubnetByName(ctx context.Context, azureConfig *MachineInfo) (*network.Subnet, error) { 368 | client, err := subnetclient() 369 | if err != nil { 370 | return nil, err 371 | } 372 | 373 | subnet, err := client.Get(ctx, azureConfig.ResourceGroup, azureConfig.Instances.Network, azureConfig.Instances.Subnet, "") 374 | if err != nil { 375 | return nil, err 376 | } 377 | 378 | return &subnet, nil 379 | } 380 | 381 | // getPublicIPbyName finds the public IP object from a list of all IP objects 382 | func getPublicIPByName(ctx context.Context, name string, resourceGroup string) (*network.PublicIPAddress, error) { 383 | client, err := ipclient() 384 | if err != nil { 385 | return nil, err 386 | } 387 | 388 | //Dump all public IPs created and iterate to find our desired IP 389 | list, err := client.ListComplete(ctx, resourceGroup) 390 | if err != nil { 391 | return nil, err 392 | } 393 | for list.NotDone() { 394 | result := list.Value() 395 | if *result.Name == name { 396 | return &result, nil 397 | } 398 | err = list.NextWithContext(ctx) 399 | if err != nil { 400 | return nil, err 401 | } 402 | } 403 | 404 | // IP not found 405 | return nil, nil 406 | } 407 | 408 | // getPublicIPbyIP finds the public IP object from a list of all IP objects 409 | func getPublicIPbyIP(ctx context.Context, ipAddress string, resourceGroup string) (*network.PublicIPAddress, error) { 410 | client, err := ipclient() 411 | if err != nil { 412 | return nil, err 413 | } 414 | 415 | //Dump all public IPs created and iterate to find our desired IP 416 | list, err := client.ListComplete(ctx, resourceGroup) 417 | if err != nil { 418 | return nil, err 419 | } 420 | for list.NotDone() { 421 | result := list.Value() 422 | if *result.PublicIPAddressPropertiesFormat.IPAddress == ipAddress { 423 | return &result, nil 424 | } 425 | err = list.NextWithContext(ctx) 426 | if err != nil { 427 | return nil, err 428 | } 429 | } 430 | 431 | // IP not found 432 | return nil, nil 433 | } 434 | 435 | // getVMByName finds the VM given a machine name 436 | func getVMByName(ctx context.Context, vmClient *compute.VirtualMachinesClient, resourceGroup string, vmName string) (*compute.VirtualMachine, error) { 437 | vm, err := vmClient.Get(ctx, resourceGroup, vmName, "") 438 | if err != nil { 439 | return nil, err 440 | } 441 | 442 | return &vm, nil 443 | } 444 | 445 | // fetchCreds creates a new authorizer and parses out the credentials file. Used by various clients for creation 446 | func fetchCreds() (autorest.Authorizer, map[string]interface{}, error) { 447 | authorizer, err := auth.NewAuthorizerFromFile(azuresdk.PublicCloud.ResourceManagerEndpoint) 448 | if err != nil { 449 | return nil, nil, err 450 | } 451 | 452 | credFile, err := ioutil.ReadFile(os.Getenv("AZURE_AUTH_LOCATION")) 453 | if err != nil { 454 | return nil, nil, err 455 | } 456 | 457 | credMap := make(map[string]interface{}) 458 | err = json.Unmarshal(credFile, &credMap) 459 | if err != nil { 460 | return nil, nil, err 461 | } 462 | return authorizer, credMap, nil 463 | } 464 | 465 | // Creates client for use in disk ops 466 | func disksclient() (*compute.DisksClient, error) { 467 | authorizer, credMap, err := fetchCreds() 468 | if err != nil { 469 | return nil, err 470 | } 471 | disksClient := compute.NewDisksClient(credMap["subscriptionId"].(string)) 472 | disksClient.Authorizer = authorizer 473 | return &disksClient, nil 474 | } 475 | 476 | // Creates client for use in public IP ops 477 | func ipclient() (*network.PublicIPAddressesClient, error) { 478 | authorizer, credMap, err := fetchCreds() 479 | if err != nil { 480 | return nil, err 481 | } 482 | ipClient := network.NewPublicIPAddressesClient(credMap["subscriptionId"].(string)) 483 | ipClient.Authorizer = authorizer 484 | return &ipClient, nil 485 | } 486 | 487 | // Creates client for use in NIC ops 488 | func nicclient() (*network.InterfacesClient, error) { 489 | authorizer, credMap, err := fetchCreds() 490 | if err != nil { 491 | return nil, err 492 | } 493 | nicClient := network.NewInterfacesClient(credMap["subscriptionId"].(string)) 494 | nicClient.Authorizer = authorizer 495 | return &nicClient, nil 496 | } 497 | 498 | // Creates client for use in subnet ops 499 | func subnetclient() (*network.SubnetsClient, error) { 500 | authorizer, credMap, err := fetchCreds() 501 | if err != nil { 502 | return nil, err 503 | } 504 | subnetClient := network.NewSubnetsClient(credMap["subscriptionId"].(string)) 505 | subnetClient.Authorizer = authorizer 506 | return &subnetClient, nil 507 | } 508 | 509 | // Creates client for use in VM ops 510 | func vmclient() (*compute.VirtualMachinesClient, error) { 511 | authorizer, credMap, err := fetchCreds() 512 | if err != nil { 513 | return nil, err 514 | } 515 | vmClient := compute.NewVirtualMachinesClient(credMap["subscriptionId"].(string)) 516 | vmClient.Authorizer = authorizer 517 | return &vmClient, nil 518 | } 519 | -------------------------------------------------------------------------------- /pkg/cloud/talos/provisioners/gce/gce.go: -------------------------------------------------------------------------------- 1 | package gce 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | "time" 9 | 10 | "gopkg.in/yaml.v2" 11 | 12 | "github.com/talos-systems/cluster-api-provider-talos/pkg/cloud/talos/utils" 13 | "google.golang.org/api/compute/v1" 14 | "google.golang.org/api/option" 15 | v1 "k8s.io/api/core/v1" 16 | k8serrors "k8s.io/apimachinery/pkg/api/errors" 17 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 | "k8s.io/client-go/kubernetes" 19 | clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" 20 | ) 21 | 22 | // GCE represents a provider for GCE. 23 | type GCE struct { 24 | } 25 | 26 | // ClusterInfo holds data about desired config in cluster object 27 | type ClusterInfo struct { 28 | Region string 29 | Project string 30 | } 31 | 32 | // MachineInfo holds data about desired config in machine object 33 | type MachineInfo struct { 34 | Zone string 35 | Project string 36 | Instances InstanceInfo 37 | } 38 | 39 | // InstanceInfo holds data about the instances we'll create 40 | type InstanceInfo struct { 41 | Type string 42 | Image string 43 | Disks DiskInfo 44 | } 45 | 46 | // DiskInfo holds disk info data 47 | type DiskInfo struct { 48 | Size int 49 | } 50 | 51 | //NewGCE returns an instance of the GCE provisioner 52 | func NewGCE() (*GCE, error) { 53 | return &GCE{}, nil 54 | } 55 | 56 | // Create creates an instance in GCE. 57 | func (gce *GCE) Create(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) error { 58 | 59 | machineSpec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 60 | if err != nil { 61 | return err 62 | } 63 | 64 | gceConfig := &MachineInfo{} 65 | yaml.Unmarshal([]byte(machineSpec.Platform.Config), gceConfig) 66 | 67 | computeService, err := client(clientset) 68 | if err != nil { 69 | return err 70 | } 71 | 72 | //fetch userdata based on machine name 73 | udConfigMap := &v1.ConfigMap{} 74 | natIP := "" 75 | if strings.Contains(machine.ObjectMeta.Name, "worker") { 76 | udConfigMap, err = clientset.CoreV1().ConfigMaps("cluster-api-provider-talos-system").Get(cluster.ObjectMeta.Name+"-workers", metav1.GetOptions{}) 77 | if k8serrors.IsNotFound(err) { 78 | return err 79 | } 80 | } else { 81 | udConfigMap, err = clientset.CoreV1().ConfigMaps("cluster-api-provider-talos-system").Get(machine.ObjectMeta.Name, metav1.GetOptions{}) 82 | if k8serrors.IsNotFound(err) { 83 | return err 84 | } 85 | 86 | // Parse the region out of zone 87 | zoneSlice := strings.Split(gceConfig.Zone, "-") 88 | regionSlice := zoneSlice[:len(zoneSlice)-1] 89 | region := strings.Join(regionSlice, "-") 90 | 91 | // Find public ip 92 | address, err := getPublicIPByName(computeService, machine.ObjectMeta.Name+"-ip", gceConfig.Project, region) 93 | if err != nil { 94 | return err 95 | } 96 | natIP = address.Address 97 | } 98 | ud := udConfigMap.Data["userdata"] 99 | 100 | //create instance with userdata 101 | _, err = computeService.Instances.Insert(gceConfig.Project, gceConfig.Zone, &compute.Instance{ 102 | Name: machine.ObjectMeta.Name, 103 | MachineType: fmt.Sprintf("zones/%s/machineTypes/%s", gceConfig.Zone, gceConfig.Instances.Type), 104 | CanIpForward: true, 105 | NetworkInterfaces: []*compute.NetworkInterface{ 106 | { 107 | Network: "global/networks/default", 108 | AccessConfigs: []*compute.AccessConfig{ 109 | { 110 | Type: "ONE_TO_ONE_NAT", 111 | Name: "External NAT", 112 | NatIP: natIP, 113 | }, 114 | }, 115 | }, 116 | }, 117 | Disks: []*compute.AttachedDisk{ 118 | { 119 | AutoDelete: true, 120 | Boot: true, 121 | InitializeParams: &compute.AttachedDiskInitializeParams{ 122 | DiskSizeGb: int64(gceConfig.Instances.Disks.Size), 123 | SourceImage: gceConfig.Instances.Image, 124 | }, 125 | }, 126 | }, 127 | Metadata: &compute.Metadata{Items: []*compute.MetadataItems{&compute.MetadataItems{Key: "user-data", Value: &ud}}}, 128 | }, 129 | ).Do() 130 | 131 | if err != nil { 132 | return err 133 | } 134 | 135 | return nil 136 | } 137 | 138 | //Update updates a given GCE instance. 139 | func (gce *GCE) Update(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) error { 140 | 141 | return nil 142 | } 143 | 144 | // Delete deletes a GCE instance. 145 | func (gce *GCE) Delete(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) error { 146 | 147 | // If instance isn't found by name, assume we no longer need to delete 148 | exists, err := gce.Exists(ctx, cluster, machine, clientset) 149 | if err != nil { 150 | return err 151 | } 152 | if !exists { 153 | return nil 154 | } 155 | 156 | machineSpec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 157 | if err != nil { 158 | return err 159 | } 160 | 161 | gceConfig := &MachineInfo{} 162 | yaml.Unmarshal([]byte(machineSpec.Platform.Config), gceConfig) 163 | 164 | computeService, err := client(clientset) 165 | if err != nil { 166 | return err 167 | } 168 | 169 | _, err = computeService.Instances.Delete(gceConfig.Project, gceConfig.Zone, machine.ObjectMeta.Name).Do() 170 | if err != nil { 171 | return err 172 | } 173 | return nil 174 | } 175 | 176 | // Exists returns whether or not an instance is present in GCE. 177 | func (gce *GCE) Exists(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) (bool, error) { 178 | machineSpec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 179 | if err != nil { 180 | return false, err 181 | } 182 | 183 | gceConfig := &MachineInfo{} 184 | yaml.Unmarshal([]byte(machineSpec.Platform.Config), gceConfig) 185 | 186 | computeService, err := client(clientset) 187 | if err != nil { 188 | return true, err 189 | } 190 | 191 | _, err = computeService.Instances.Get(gceConfig.Project, gceConfig.Zone, machine.ObjectMeta.Name).Do() 192 | if err != nil && strings.Contains(err.Error(), "notFound") { 193 | return false, nil 194 | } else if err != nil { 195 | return false, err 196 | } 197 | 198 | return true, nil 199 | } 200 | 201 | // AllocateExternalIPs creates IPs for the control plane nodes 202 | func (gce *GCE) AllocateExternalIPs(cluster *clusterv1.Cluster, clientset *kubernetes.Clientset) ([]string, error) { 203 | clusterSpec, err := utils.ClusterProviderFromSpec(cluster.Spec.ProviderSpec) 204 | if err != nil { 205 | return nil, err 206 | } 207 | 208 | gceConfig := &ClusterInfo{} 209 | yaml.Unmarshal([]byte(clusterSpec.Platform.Config), gceConfig) 210 | 211 | computeService, err := client(clientset) 212 | if err != nil { 213 | return nil, err 214 | } 215 | 216 | floatingIPs := []string{} 217 | for i := 0; i < clusterSpec.ControlPlane.Count; i++ { 218 | 219 | //Check for address existence and return early if we can 220 | address, err := getPublicIPByName(computeService, cluster.ObjectMeta.Name+"-master-"+strconv.Itoa(i)+"-ip", gceConfig.Project, gceConfig.Region) 221 | if err != nil { 222 | return nil, err 223 | } 224 | if address != nil { 225 | floatingIPs = append(floatingIPs, address.Address) 226 | continue 227 | } 228 | 229 | // Insert the address and wait for it to be ready 230 | op, err := computeService.Addresses.Insert(gceConfig.Project, gceConfig.Region, &compute.Address{Name: cluster.ObjectMeta.Name + "-master-" + strconv.Itoa(i) + "-ip"}).Do() 231 | if err != nil { 232 | return nil, err 233 | } 234 | 235 | opStatus := &compute.Operation{} 236 | for opStatus.Status != "DONE" { 237 | opStatus, err = computeService.RegionOperations.Get(gceConfig.Project, gceConfig.Region, op.Name).Do() 238 | time.Sleep(5 * time.Second) 239 | } 240 | 241 | address, err = getPublicIPByName(computeService, cluster.ObjectMeta.Name+"-master-"+strconv.Itoa(i)+"-ip", gceConfig.Project, gceConfig.Region) 242 | if err != nil { 243 | return nil, err 244 | } 245 | floatingIPs = append(floatingIPs, address.Address) 246 | } 247 | 248 | return floatingIPs, nil 249 | } 250 | 251 | // DeAllocateExternalIPs cleans IPs for the control plane nodes 252 | func (gce *GCE) DeAllocateExternalIPs(cluster *clusterv1.Cluster, clientset *kubernetes.Clientset) error { 253 | 254 | clusterSpec, err := utils.ClusterProviderFromSpec(cluster.Spec.ProviderSpec) 255 | if err != nil { 256 | return err 257 | } 258 | 259 | gceConfig := &ClusterInfo{} 260 | yaml.Unmarshal([]byte(clusterSpec.Platform.Config), gceConfig) 261 | 262 | computeService, err := client(clientset) 263 | if err != nil { 264 | return err 265 | } 266 | 267 | for i := 0; i < clusterSpec.ControlPlane.Count; i++ { 268 | _, err := computeService.Addresses.Delete(gceConfig.Project, gceConfig.Region, cluster.ObjectMeta.Name+"-master-"+strconv.Itoa(i)+"-ip").Do() 269 | if err != nil && !strings.Contains(err.Error(), "notFound") { 270 | return err 271 | } 272 | } 273 | 274 | return nil 275 | } 276 | 277 | func getPublicIPByName(computeService *compute.Service, name string, project string, region string) (*compute.Address, error) { 278 | addressList, err := computeService.Addresses.List(project, region).Do() 279 | if err != nil { 280 | return nil, err 281 | } 282 | 283 | for _, address := range addressList.Items { 284 | if address.Name == name { 285 | return address, nil 286 | } 287 | } 288 | 289 | // Nothing found 290 | return nil, nil 291 | 292 | } 293 | 294 | func client(clientset *kubernetes.Clientset) (*compute.Service, error) { 295 | creds, err := clientset.CoreV1().Secrets("cluster-api-provider-talos-system").Get("gce-credentials", metav1.GetOptions{}) 296 | if k8serrors.IsNotFound(err) { 297 | return nil, err 298 | } 299 | 300 | //create client 301 | ctx := context.Background() 302 | return compute.NewService(ctx, option.WithCredentialsJSON(creds.Data["service-account.json"])) 303 | } 304 | -------------------------------------------------------------------------------- /pkg/cloud/talos/provisioners/packet/packet.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "log" 7 | "net" 8 | "strconv" 9 | "strings" 10 | "time" 11 | 12 | "github.com/packethost/packngo" 13 | "github.com/talos-systems/cluster-api-provider-talos/pkg/cloud/talos/utils" 14 | "gopkg.in/yaml.v2" 15 | "k8s.io/client-go/kubernetes" 16 | clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" 17 | ) 18 | 19 | // Packet represents a provider for Packet. 20 | type Packet struct { 21 | client *packngo.Client 22 | } 23 | 24 | // ClusterInfo holds data about desired config in cluster object 25 | type ClusterInfo struct { 26 | ProjectID string 27 | IPBlock string 28 | } 29 | 30 | // MachineInfo holds data about desired config in cluster object 31 | type MachineInfo struct { 32 | ProjectID string 33 | Instances InstanceInfo 34 | } 35 | 36 | // InstanceInfo holds data about the instances we'll create 37 | type InstanceInfo struct { 38 | Plan string 39 | Facility string 40 | PXEUrl string 41 | Install map[string]interface{} 42 | } 43 | 44 | // Userdata holds userdata in struct form 45 | type Userdata struct { 46 | Version string 47 | Security interface{} 48 | Services interface{} 49 | Install map[string]interface{} 50 | Networking *Network 51 | } 52 | 53 | type Network struct { 54 | OS *OS `yaml:"os,omitempty"` 55 | } 56 | 57 | type OS struct { 58 | Devices []map[string]interface{} `yaml:"devices,omitempty"` 59 | } 60 | 61 | //NewPacket returns an instance of the Packet provisioner 62 | func NewPacket() (*Packet, error) { 63 | c, err := packngo.NewClient() 64 | if err != nil { 65 | log.Fatal(err) 66 | } 67 | 68 | return &Packet{client: c}, nil 69 | } 70 | 71 | // Create creates an instance in Packet. 72 | func (packet *Packet) Create(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) error { 73 | clusterSpec, err := utils.ClusterProviderFromSpec(cluster.Spec.ProviderSpec) 74 | if err != nil { 75 | return err 76 | } 77 | 78 | clusterConfig := &ClusterInfo{} 79 | yaml.Unmarshal([]byte(clusterSpec.Platform.Config), clusterConfig) 80 | 81 | machineSpec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 82 | if err != nil { 83 | return err 84 | } 85 | 86 | packetConfig := &MachineInfo{} 87 | yaml.Unmarshal([]byte(machineSpec.Platform.Config), packetConfig) 88 | 89 | // Here we pull down the userdata config map and add the install section to the end if it's defined in the machine 90 | udConfigMap, err := utils.FetchConfigMap(cluster, machine, clientset) 91 | if err != nil { 92 | return err 93 | } 94 | udStruct := &Userdata{} 95 | yaml.Unmarshal([]byte(udConfigMap.Data["userdata"]), udStruct) 96 | 97 | if packetConfig.Instances.Install != nil { 98 | udStruct.Install = packetConfig.Instances.Install 99 | } 100 | 101 | //Add network tweaks for elastic IPs to userdata 102 | var floatingIP string 103 | isMaster := strings.Contains(machine.ObjectMeta.Name, "master") 104 | if isMaster { 105 | nameSlice := strings.Split(machine.ObjectMeta.Name, "-") 106 | indexString := nameSlice[len(nameSlice)-1] 107 | index, err := strconv.Atoi(indexString) 108 | if err != nil { 109 | return err 110 | } 111 | 112 | ipList, err := getIPList(packet.client, clusterConfig.ProjectID, clusterConfig.IPBlock) 113 | if err != nil { 114 | return err 115 | } 116 | 117 | floatingIP = ipList[index] 118 | 119 | //Haxx on haxx b/c dhcp value needs to be a bool so the whole networking.os.devices block needs to be a map of interfaces 120 | if udStruct.Networking == nil { 121 | udStruct.Networking = &Network{OS: &OS{Devices: make([]map[string]interface{}, 0)}} 122 | } 123 | udStruct.Networking.OS.Devices = append(udStruct.Networking.OS.Devices, map[string]interface{}{"interface": "lo", "cidr": floatingIP + "/32"}) 124 | udStruct.Networking.OS.Devices = append(udStruct.Networking.OS.Devices, map[string]interface{}{"interface": "eth0", "dhcp": true}) 125 | } 126 | 127 | udBytes, err := yaml.Marshal(udStruct) 128 | if err != nil { 129 | return err 130 | } 131 | //TODO(rsmitty): Shebang no longer needed once talos alpha 28 is cut. 132 | ud := "#!talos\n" + string(udBytes) 133 | 134 | devCreateReq := &packngo.DeviceCreateRequest{ 135 | Hostname: machine.ObjectMeta.Name, 136 | Plan: packetConfig.Instances.Plan, 137 | Facility: []string{packetConfig.Instances.Facility}, 138 | OS: "custom_ipxe", 139 | BillingCycle: "hourly", 140 | ProjectID: packetConfig.ProjectID, 141 | UserData: ud, 142 | IPXEScriptURL: packetConfig.Instances.PXEUrl, 143 | } 144 | 145 | dev, _, err := packet.client.Devices.Create(devCreateReq) 146 | if err != nil { 147 | return err 148 | } 149 | 150 | //Wait for masters to be active, attach floating ip 151 | if isMaster { 152 | err = packet.waitForStatus(machine, "active") 153 | if err != nil { 154 | return err 155 | } 156 | ipReq := &packngo.AddressStruct{Address: floatingIP + "/32"} 157 | _, _, err = packet.client.DeviceIPs.Assign(dev.ID, ipReq) 158 | if err != nil { 159 | return err 160 | } 161 | } 162 | 163 | log.Println("[Packet] Instance created with id: " + dev.ID) 164 | 165 | return nil 166 | } 167 | 168 | //Update updates a given Packet instance. 169 | func (packet *Packet) Update(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) error { 170 | return nil 171 | } 172 | 173 | // Delete deletes a Packet instance. 174 | func (packet *Packet) Delete(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) error { 175 | 176 | dev, err := packet.fetchDevice(machine) 177 | if err != nil { 178 | return err 179 | } 180 | 181 | if dev != nil { 182 | _, err = packet.client.Devices.Delete(dev.ID) 183 | if err != nil { 184 | return err 185 | } 186 | } 187 | return nil 188 | } 189 | 190 | // Exists returns whether or not an instance is present in AWS. 191 | func (packet *Packet) Exists(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) (bool, error) { 192 | 193 | dev, err := packet.fetchDevice(machine) 194 | if err != nil { 195 | return false, err 196 | } 197 | 198 | if dev == nil { 199 | return false, nil 200 | } 201 | return true, nil 202 | } 203 | 204 | // AllocateExternalIPs creates IPs for the control plane nodes 205 | // Note: This is weird for packet. We still expect the block of IPs to pre-exist and we just list them out. 206 | func (packet *Packet) AllocateExternalIPs(cluster *clusterv1.Cluster, clientset *kubernetes.Clientset) ([]string, error) { 207 | 208 | clusterSpec, err := utils.ClusterProviderFromSpec(cluster.Spec.ProviderSpec) 209 | if err != nil { 210 | return nil, err 211 | } 212 | packetConfig := &ClusterInfo{} 213 | yaml.Unmarshal([]byte(clusterSpec.Platform.Config), packetConfig) 214 | 215 | ipList, err := getIPList(packet.client, packetConfig.ProjectID, packetConfig.IPBlock) 216 | if err != nil { 217 | return nil, err 218 | } 219 | 220 | return ipList[:clusterSpec.ControlPlane.Count], nil 221 | } 222 | 223 | // DeAllocateExternalIPs cleans IPs for the control plane nodes 224 | // Note: Weird for packet. A no-op since we actually expect the block of IPs to be pre-created 225 | func (packet *Packet) DeAllocateExternalIPs(cluster *clusterv1.Cluster, clientset *kubernetes.Clientset) error { 226 | return nil 227 | } 228 | 229 | // Returns a full list of available IPs for a given CIDR block 230 | func getIPList(client *packngo.Client, projectID string, ipBlock string) ([]string, error) { 231 | ipBlocks, _, err := client.ProjectIPs.List(projectID) 232 | if err != nil { 233 | return nil, err 234 | } 235 | var desiredBlock packngo.IPAddressReservation 236 | for _, block := range ipBlocks { 237 | if block.IpAddressCommon.Network+"/"+strconv.Itoa(block.IpAddressCommon.CIDR) == ipBlock { 238 | desiredBlock = block 239 | } 240 | } 241 | 242 | if desiredBlock.ID != "" { 243 | available, _, err := client.ProjectIPs.AvailableAddresses(desiredBlock.ID, &packngo.AvailableRequest{CIDR: 32}) 244 | if err != nil { 245 | return nil, err 246 | } 247 | 248 | ipList := []string{} 249 | for _, addr := range available { 250 | addrParsed, _, err := net.ParseCIDR(addr) 251 | if err != nil { 252 | return nil, err 253 | } 254 | ipList = append(ipList, addrParsed.String()) 255 | } 256 | return ipList, nil 257 | } 258 | 259 | //Not found 260 | return nil, errors.New("[Packet] Unable to find or parse desired IP block") 261 | } 262 | 263 | func (packet *Packet) fetchDevice(machine *clusterv1.Machine) (*packngo.Device, error) { 264 | machineSpec, err := utils.MachineProviderFromSpec(machine.Spec.ProviderSpec) 265 | if err != nil { 266 | return nil, err 267 | } 268 | packetConfig := &MachineInfo{} 269 | yaml.Unmarshal([]byte(machineSpec.Platform.Config), packetConfig) 270 | 271 | devList, _, err := packet.client.Devices.List(packetConfig.ProjectID, &packngo.ListOptions{}) 272 | if err != nil { 273 | return nil, err 274 | } 275 | 276 | for _, dev := range devList { 277 | if dev.Hostname == machine.ObjectMeta.Name { 278 | return &dev, nil 279 | } 280 | } 281 | return nil, nil 282 | } 283 | 284 | //waitForStatus polls the Packet api for a certain instance status 285 | //needed for attaching elastic IP after boot 286 | func (packet *Packet) waitForStatus(machine *clusterv1.Machine, desiredState string) error { 287 | 288 | timeout := time.After(600 * time.Second) 289 | tick := time.Tick(3 * time.Second) 290 | 291 | for { 292 | select { 293 | case <-timeout: 294 | return errors.New("[Packet] Timed out waiting for running instance") 295 | case <-tick: 296 | dev, err := packet.fetchDevice(machine) 297 | if err != nil { 298 | return err 299 | } 300 | if dev.State == desiredState { 301 | return nil 302 | } 303 | } 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /pkg/cloud/talos/provisioners/provision.go: -------------------------------------------------------------------------------- 1 | package provisioners 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/talos-systems/cluster-api-provider-talos/pkg/cloud/talos/provisioners/aws" 8 | "github.com/talos-systems/cluster-api-provider-talos/pkg/cloud/talos/provisioners/azure" 9 | "github.com/talos-systems/cluster-api-provider-talos/pkg/cloud/talos/provisioners/gce" 10 | "github.com/talos-systems/cluster-api-provider-talos/pkg/cloud/talos/provisioners/packet" 11 | "k8s.io/client-go/kubernetes" 12 | clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" 13 | ) 14 | 15 | type Provisioner interface { 16 | Create(context.Context, *clusterv1.Cluster, *clusterv1.Machine, *kubernetes.Clientset) error 17 | Update(context.Context, *clusterv1.Cluster, *clusterv1.Machine, *kubernetes.Clientset) error 18 | 19 | Delete(context.Context, *clusterv1.Cluster, *clusterv1.Machine, *kubernetes.Clientset) error 20 | Exists(context.Context, *clusterv1.Cluster, *clusterv1.Machine, *kubernetes.Clientset) (bool, error) 21 | 22 | AllocateExternalIPs(*clusterv1.Cluster, *kubernetes.Clientset) ([]string, error) 23 | DeAllocateExternalIPs(*clusterv1.Cluster, *kubernetes.Clientset) error 24 | } 25 | 26 | func NewProvisioner(id string) (Provisioner, error) { 27 | switch id { 28 | case "aws": 29 | return aws.NewAWS() 30 | case "azure": 31 | return azure.NewAz() 32 | case "gce": 33 | return gce.NewGCE() 34 | case "packet": 35 | return packet.NewPacket() 36 | } 37 | return nil, errors.New("Unknown provisioner") 38 | } 39 | -------------------------------------------------------------------------------- /pkg/cloud/talos/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "math/rand" 5 | "strings" 6 | 7 | talosv1 "github.com/talos-systems/cluster-api-provider-talos/pkg/apis/talos/v1alpha1" 8 | "gopkg.in/yaml.v2" 9 | v1 "k8s.io/api/core/v1" 10 | k8serrors "k8s.io/apimachinery/pkg/api/errors" 11 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | "k8s.io/client-go/kubernetes" 13 | "k8s.io/client-go/rest" 14 | clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" 15 | ) 16 | 17 | //RandomString simply returns a string of length n 18 | func RandomString(n int) string { 19 | var letter = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy0123456789") 20 | 21 | b := make([]rune, n) 22 | for i := range b { 23 | b[i] = letter[rand.Intn(len(letter))] 24 | } 25 | return string(b) 26 | } 27 | 28 | //ClusterProviderFromSpec parses out and returns provider specific cluster spec 29 | func ClusterProviderFromSpec(providerSpec clusterv1.ProviderSpec) (*talosv1.TalosClusterProviderSpec, error) { 30 | var config talosv1.TalosClusterProviderSpec 31 | if err := yaml.Unmarshal(providerSpec.Value.Raw, &config); err != nil { 32 | return nil, err 33 | } 34 | return &config, nil 35 | } 36 | 37 | //MachineProviderFromSpec parses out and returns provider specific machine spec 38 | func MachineProviderFromSpec(providerSpec clusterv1.ProviderSpec) (*talosv1.TalosMachineProviderSpec, error) { 39 | var config talosv1.TalosMachineProviderSpec 40 | if err := yaml.Unmarshal(providerSpec.Value.Raw, &config); err != nil { 41 | return nil, err 42 | } 43 | return &config, nil 44 | } 45 | 46 | //CreateK8sClientSet returns a kube client to use for calls to the api server 47 | func CreateK8sClientSet() (*kubernetes.Clientset, error) { 48 | // creates the in-cluster config 49 | config, err := rest.InClusterConfig() 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | // creates the clientset 55 | clientset, err := kubernetes.NewForConfig(config) 56 | if err != nil { 57 | return nil, err 58 | } 59 | return clientset, nil 60 | } 61 | 62 | // FetchConfigMap grabs the proper cm from kubernetes depending on whether we're worried about our masters or workers 63 | func FetchConfigMap(cluster *clusterv1.Cluster, machine *clusterv1.Machine, clientset *kubernetes.Clientset) (*v1.ConfigMap, error) { 64 | udConfigMap := &v1.ConfigMap{} 65 | var err error 66 | if strings.Contains(machine.ObjectMeta.Name, "worker") { 67 | udConfigMap, err = clientset.CoreV1().ConfigMaps("cluster-api-provider-talos-system").Get(cluster.ObjectMeta.Name+"-workers", metav1.GetOptions{}) 68 | if k8serrors.IsNotFound(err) { 69 | return nil, err 70 | } 71 | } else { 72 | udConfigMap, err = clientset.CoreV1().ConfigMaps("cluster-api-provider-talos-system").Get(machine.ObjectMeta.Name, metav1.GetOptions{}) 73 | if k8serrors.IsNotFound(err) { 74 | return nil, err 75 | } 76 | 77 | } 78 | 79 | return udConfigMap, nil 80 | } 81 | -------------------------------------------------------------------------------- /pkg/controller/controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "sigs.k8s.io/controller-runtime/pkg/manager" 21 | ) 22 | 23 | // AddToManagerFuncs is a list of functions to add all Controllers to the Manager 24 | var AddToManagerFuncs []func(manager.Manager) error 25 | 26 | // AddToManager adds all Controllers to the Manager 27 | func AddToManager(m manager.Manager) error { 28 | for _, f := range AddToManagerFuncs { 29 | if err := f(m); err != nil { 30 | return err 31 | } 32 | } 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /pkg/webhook/webhook.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package webhook 18 | 19 | import ( 20 | "sigs.k8s.io/controller-runtime/pkg/manager" 21 | ) 22 | 23 | // AddToManagerFuncs is a list of functions to add all Controllers to the Manager 24 | var AddToManagerFuncs []func(manager.Manager) error 25 | 26 | // AddToManager adds all Controllers to the Manager 27 | // +kubebuilder:rbac:groups=admissionregistration.k8s.io,resources=mutatingwebhookconfigurations;validatingwebhookconfigurations,verbs=get;list;watch;create;update;patch;delete 28 | // +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete 29 | // +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete 30 | func AddToManager(m manager.Manager) error { 31 | for _, f := range AddToManagerFuncs { 32 | if err := f(m); err != nil { 33 | return err 34 | } 35 | } 36 | return nil 37 | } 38 | --------------------------------------------------------------------------------