├── .editorconfig ├── .github └── workflows │ ├── 001-go-ubuntu-latest-make-test-format-lint.yaml │ ├── 090-go-release-ubuntu-latest-go-releaser.yaml │ └── README.md ├── .gitignore ├── .golangci.yaml ├── .goreleaser.yaml ├── Dockerfile.goreleaser ├── LICENSE ├── Makefile ├── README.md ├── RELEASING.md ├── bin └── .gitkeep ├── buf.gen.yaml ├── client └── README.md ├── cmd ├── cells │ ├── allocate │ │ └── allocate.go │ ├── cells.go │ ├── free │ │ └── free.go │ ├── start │ │ └── start.go │ └── stop │ │ └── stop.go ├── cmd.go ├── discovery │ ├── discover.go │ └── discover_test.go ├── health │ ├── check.go │ └── check_test.go ├── observe │ ├── observe.go │ └── observe_test.go ├── oci │ ├── create │ │ └── create.go │ ├── delete │ │ └── delete.go │ ├── kill │ │ └── kill.go │ ├── oci.go │ ├── start │ │ └── start.go │ └── state │ │ └── state.go ├── pki │ ├── create │ │ ├── create.go │ │ └── create_test.go │ └── pki.go ├── root │ └── root.go └── version │ ├── version.go │ └── version_test.go ├── compile.go ├── docs ├── aurae_pki.md └── subcommands.md ├── go.mod ├── go.sum ├── main.go └── pkg ├── cli ├── output_format.go ├── output_format_test.go ├── printer │ ├── interface.go │ ├── json.go │ └── yaml.go └── testsuite │ └── suite.go ├── client └── client.go ├── config ├── auth.go ├── config.go └── system.go ├── discovery └── discovery.go ├── health └── health.go ├── observe └── observe.go └── pki ├── pki.go └── pki_test.go /.editorconfig: -------------------------------------------------------------------------------- 1 | ; https://editorconfig.org/ 2 | 3 | root = true 4 | 5 | [*] 6 | insert_final_newline = true 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | indent_style = space 10 | indent_size = 2 11 | 12 | [{Makefile,go.mod,go.sum,*.go,.gitmodules}] 13 | indent_style = tab 14 | indent_size = 4 15 | 16 | [*.md] 17 | indent_size = 4 18 | trim_trailing_whitespace = false 19 | 20 | eclint_indent_style = unset 21 | 22 | [Dockerfile] 23 | indent_size = 4 24 | -------------------------------------------------------------------------------- /.github/workflows/001-go-ubuntu-latest-make-test-format-lint.yaml: -------------------------------------------------------------------------------- 1 | name: Test and Format 2 | on: 3 | push: 4 | branches: main 5 | pull_request: 6 | branches: main 7 | 8 | jobs: 9 | tests: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - uses: bufbuild/buf-setup-action@v1 15 | with: 16 | github_token: ${{ github.token }} 17 | 18 | - uses: actions/setup-go@v3 19 | with: 20 | go-version: '>=1.20.1' 21 | cache: true 22 | 23 | - name: Run tests 24 | run: make test 25 | 26 | format: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@v3 30 | 31 | - uses: actions/setup-go@v3 32 | with: 33 | go-version: '>=1.20.1' 34 | cache: true 35 | 36 | - name: Run formatting 37 | run: make check-format 38 | 39 | lint: 40 | runs-on: ubuntu-latest 41 | steps: 42 | - uses: actions/checkout@v3 43 | 44 | - uses: bufbuild/buf-setup-action@v1 45 | with: 46 | github_token: ${{ github.token }} 47 | 48 | - uses: actions/setup-go@v3 49 | with: 50 | go-version: '>=1.20.1' 51 | 52 | - name: Make proto 53 | run: make proto 54 | 55 | - name: Lint 56 | uses: golangci/golangci-lint-action@v3 57 | 58 | editorconfig: 59 | runs-on: ubuntu-latest 60 | steps: 61 | - uses: actions/checkout@v3 62 | 63 | - uses: editorconfig-checker/action-editorconfig-checker@main 64 | 65 | - run: editorconfig-checker 66 | -------------------------------------------------------------------------------- /.github/workflows/090-go-release-ubuntu-latest-go-releaser.yaml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: 5 | # run only against tags 6 | tags: 7 | - '*' 8 | 9 | permissions: 10 | contents: write 11 | packages: write 12 | 13 | jobs: 14 | goreleaser: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | fetch-depth: 0 20 | 21 | - run: git fetch --force --tags 22 | 23 | - uses: actions/setup-go@v3 24 | with: 25 | go-version: '>=1.20.1' 26 | cache: true 27 | 28 | - uses: goreleaser/goreleaser-action@v4 29 | with: 30 | distribution: goreleaser 31 | version: "~> v1.14" 32 | args: release --rm-dist 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | -------------------------------------------------------------------------------- /.github/workflows/README.md: -------------------------------------------------------------------------------- 1 | # GitHub Workflows 2 | 3 | Please use the following naming convention such that we can easily map failed builds back to YAML files here in the repository. 4 | 5 | ``` 6 | $number-$friendlyname-$environment-$testcommands 7 | ``` 8 | 9 | Where **number** is just a unique identifier to easily map failed builds to YAML files. 10 | Where **friendlyname** is a good descriptor to describe what is being checked (avoid words like "build" or "main" as they are too generic) 11 | Where **environment** describes the environment it is running in, such as `alpine:latest` or `armv7`. 12 | Where **testcommands** are the commands that a user can replicate on their computer! Do NOT test commands that can not be easily replicated! 13 | 14 | ``` 15 | 007-linter-alpine-make-lint.yaml 16 | ``` 17 | 18 | A new linter running in alpine that tests the command `make lint` and **007** can easily be mapped backed to the file. 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/* 2 | .idea* 3 | vendor/* 4 | dist/ 5 | .vscode/ 6 | pkg/api/ 7 | ae 8 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | issues: 2 | exclude-dirs: 3 | - pkg/api/ 4 | 5 | linters: 6 | disable: 7 | - typecheck 8 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - go mod tidy 4 | - go mod vendor 5 | - go mod download 6 | - go test -v ./... 7 | #- go generate ./... 8 | builds: 9 | - env: 10 | - CGO_ENABLED=0 11 | goos: 12 | - linux 13 | - windows 14 | - darwin 15 | goarch: 16 | - amd64 17 | - arm 18 | - arm64 19 | - "386" 20 | goarm: 21 | - "6" 22 | - "7" 23 | ignore: 24 | - goos: darwin 25 | goarch: "386" 26 | ldflags: 27 | - -s -w -X 'main.Version={{ .Version }}' -X 'main.AuthorName="The Aurae Authors"' -X 'main.AuthorEmail="info@aurae.io"' -X 'main.Copyright=Copyright (c) 2023' -X 'main.License=Apache2' -X 'main.Name=ae' 28 | 29 | archives: 30 | - format: tar.gz 31 | # this name template makes the OS and Arch compatible with the results of uname. 32 | name_template: >- 33 | {{ .ProjectName }}_ 34 | {{- tolower .Os }}_ 35 | {{- if eq .Arch "amd64" }}x86_64 36 | {{- else if eq .Arch "386" }}i386 37 | {{- else }}{{ .Arch }}{{ end }} 38 | {{- if .Arm }}v{{ .Arm }}{{ end }} 39 | # use zip for windows archives 40 | format_overrides: 41 | - goos: windows 42 | format: zip 43 | checksum: 44 | name_template: 'checksums.txt' 45 | snapshot: 46 | name_template: "{{ incpatch .Version }}-next" 47 | changelog: 48 | sort: asc 49 | filters: 50 | exclude: 51 | - '^docs:' 52 | - '^test:' 53 | 54 | # TODO: Build docker images with `ae` binaries generated by goreleaser. 55 | # 56 | # dockers: 57 | # - image_templates: 58 | # - "foo/bar:{{ .Version }}-amd64" 59 | # - "aurae-runtime/ae:latest-amd64" 60 | # - "aurae-runtime/ae:{{ .Tag }}-amd64" 61 | # - "aurae-runtime/ae:v{{ .Major }}-amd64" 62 | # - "ghcr.io/aurae-runtime/ae:latest-amd64" 63 | # use: buildx 64 | # dockerfile: Dockerfile.goreleaser 65 | # skip_push: true 66 | # build_flag_templates: 67 | # - "--platform=linux/amd64" 68 | # - "--pull" 69 | # - "--label=org.opencontainers.image.created={{.Date}}" 70 | # - "--label=org.opencontainers.image.title={{.ProjectName}}" 71 | # - "--label=org.opencontainers.image.revision={{.FullCommit}}" 72 | # - "--label=org.opencontainers.image.version={{.Version}}" 73 | # - image_templates: 74 | # - "aurae-runtime/ae:latest-arm64v8" 75 | # - "aurae-runtime/ae:{{ .Tag }}-arm64v8" 76 | # - "aurae-runtime/ae:v{{ .Major }}-arm64v8" 77 | # - "ghcr.io/aurae-runtime/ae:latest-arm64v8" 78 | # use: buildx 79 | # goarch: arm64 80 | # dockerfile: Dockerfile 81 | # skip_push: true 82 | # build_flag_templates: 83 | # - "--platform=linux/arm64/v8" 84 | # - "--pull" 85 | # - "--label=org.opencontainers.image.created={{.Date}}" 86 | # - "--label=org.opencontainers.image.title={{.ProjectName}}" 87 | # - "--label=org.opencontainers.image.revision={{.FullCommit}}" 88 | # - "--label=org.opencontainers.image.version={{.Version}}" 89 | # docker_manifests: 90 | # - name_template: aurae-runtime/ae:{{ .Version }} 91 | # image_templates: 92 | # - aurae-runtime/ae:{{ .Version }}-amd64 93 | # - aurae-runtime/ae:{{ .Version }}-arm64v8 94 | -------------------------------------------------------------------------------- /Dockerfile.goreleaser: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | COPY ae /ae 3 | ENTRYPOINT ["/ae"] 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------- # 2 | # Apache 2.0 License Copyright © 2023 The Aurae Authors # 3 | # # 4 | # +--------------------------------------------+ # 5 | # | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | # 6 | # | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | # 7 | # | ███████║██║ ██║██████╔╝███████║█████╗ | # 8 | # | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | # 9 | # | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | # 10 | # | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | # 11 | # +--------------------------------------------+ # 12 | # # 13 | # Distributed Systems Runtime # 14 | # # 15 | # ---------------------------------------------------------------------------- # 16 | # # 17 | # Licensed under the Apache License, Version 2.0 (the "License"); # 18 | # you may not use this file except in compliance with the License. # 19 | # You may obtain a copy of the License at # 20 | # # 21 | # http://www.apache.org/licenses/LICENSE-2.0 # 22 | # # 23 | # Unless required by applicable law or agreed to in writing, software # 24 | # distributed under the License is distributed on an "AS IS" BASIS, # 25 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 26 | # See the License for the specific language governing permissions and # 27 | # limitations under the License. # 28 | # # 29 | # ---------------------------------------------------------------------------- # 30 | 31 | GORELEASER_FLAGS ?= --snapshot --rm-dist 32 | all: compile check-format lint test 33 | 34 | # Variables and Settings 35 | version ?= 0.0.1 36 | target ?= ae 37 | org ?= aurae-runtime 38 | authorname ?= The Aurae Authors 39 | authoremail ?= info@aurae.io 40 | license ?= Apache2 41 | year ?= 2023 42 | copyright ?= Copyright (c) $(year) 43 | 44 | COMMIT := $(shell git rev-parse HEAD) 45 | DATE := $(shell date +%Y-%m-%d) 46 | PKG_LDFLAGS := github.com/prometheus/common/version 47 | 48 | compile: mod proto ## Compile for the local architecture ⚙ 49 | @echo "Compiling..." 50 | go build -ldflags "\ 51 | -X 'main.Version=$(version)' \ 52 | -X 'main.AuthorName=$(authorname)' \ 53 | -X 'main.AuthorEmail=$(authoremail)' \ 54 | -X 'main.Copyright=$(copyright)' \ 55 | -X 'main.License=$(license)' \ 56 | -X 'main.Name=$(target)' \ 57 | -X '${PKG_LDFLAGS}.Version=$(version)' \ 58 | -X '${PKG_LDFLAGS}.BuildDate=$(DATE)' \ 59 | -X '${PKG_LDFLAGS}.Revision=$(COMMIT)'" \ 60 | -o bin/$(target) . 61 | 62 | .PHONY: goreleaser 63 | goreleaser: ## Run goreleaser directly at the pinned version 🛠 64 | go run github.com/goreleaser/goreleaser@v1.14 $(GORELEASER_FLAGS) 65 | 66 | .PHONY: mod 67 | mod: proto ## Go mod things 68 | go mod tidy 69 | go mod vendor 70 | go mod download 71 | 72 | .PHONY: install 73 | install: compile test ## Install the program to /usr/bin 🎉 74 | @echo "Installing..." 75 | sudo cp bin/$(target) /usr/local/bin/$(target) 76 | 77 | .PHONY: test 78 | test: compile ## 🤓 Run go tests 79 | @echo "Testing..." 80 | go test -v ./... 81 | 82 | .PHONY: clean 83 | clean: ## Clean your artifacts 🧼 84 | @echo "Cleaning..." 85 | rm -rvf dist/* 86 | rm -rvf release/* 87 | rm -rvf pkg/api/ 88 | 89 | .PHONY: help 90 | help: ## Show help messages for make targets 91 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(firstword $(MAKEFILE_LIST)) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}' 92 | 93 | .PHONY: format 94 | format: ## Format the code using gofmt 95 | @echo "Formatting..." 96 | @gofmt -s -w $(shell find . -name '*.go' -not -path "./vendor/*") 97 | 98 | .PHONY: check-format 99 | check-format: ## Used by CI to check if code is formatted 100 | @gofmt -l $(shell find . -name '*.go' -not -path "./vendor/*") | grep ".*" ; if [ $$? -eq 0 ]; then exit 1; fi 101 | 102 | .PHONY: lint 103 | lint: ## Runs the linter 104 | golangci-lint run 105 | 106 | .PHONY: check-editorconfig 107 | check-editorconfig: ## Use to check if the codebase follows editorconfig rules 108 | @docker run --rm --volume=$(shell PWD):/check mstruebing/editorconfig-checker 109 | 110 | # TODO: run this conditionally. see aurae-runtime/aurae for how to do this. 111 | .PHONY: proto 112 | proto: 113 | @buf --version >/dev/null 2>&1 || (echo "Error: buf is not installed. Please install from https://docs.buf.build/installation"; exit 1) 114 | buf generate -v https://github.com/aurae-runtime/aurae.git#branch=main,subdir=api 115 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | ae 3 |

UNIX inspired CLI Client for Aurae

4 | 5 | 6 |
7 | 8 | 9 | go version 10 | 11 | 12 | 13 | 14 | license 15 | 16 | 17 | 18 |
19 | 20 |
21 | 22 | 23 | workflow status 24 | 25 | 26 | 27 | 28 | workflow status 29 | 30 | 31 | 32 |
33 | 34 | --- 35 | 36 | > The project welcomes new contributors and developers. Check out the **[Getting Involved](https://github.com/aurae-runtime/community#getting-involved)** section and join the Discord. It is mandatory to sign the **[CLA](https://cla.nivenly.org/)** to contribute. 37 | 38 | 39 | 40 |

📑 Table of Contents

41 | 42 |
43 | Table of Contents 44 |
    45 |
  1. • About The Project
  2. 46 |
  3. • Quickstart
  4. 47 |
  5. • Usage
  6. 48 |
  7. • Philosophy
  8. 49 |
  9. • Contribute
  10. 50 |
51 |
52 | 53 |   54 | 55 | 56 | ## About The Project 57 | 58 | `ae` is a UNIX inspired CLI client for **[Aurae](https://github.com/aurae-runtime/aurae)**, written in Go. However, in order to understand what `ae` should and can do, we must first understand `aer`. 59 | 60 | ### What is `aer`? 61 | 62 | [`aer`](https://github.com/aurae-runtime/aurae/tree/main/aer) is a CLI tool based on the Rust client which has the identical scope as a single _[auraed](https://github.com/aurae-runtime/aurae/tree/main/auraed)_ node. 63 | 64 | The tool is aimed at "POWER-USERS" and exists as a rapid way to develop and debug against APIs that change frequently. For example, an [auraed](https://github.com/aurae-runtime/aurae/) developer can make a change to an existing API and test it locally against a single daemon. 65 | 66 | 67 | ### What is `ae`? 68 | 69 | We have created and maintain a CLI tool, `ae` that is based on the Go client and has a broader view than `aer`. 70 | 71 | The purpose will be to use `ae` for clusters of [Aurae](https://github.com/aurae-runtime/aurae) nodes and will probably work similar to `aer`, but not completely! 72 | 73 | The `ae` CLI tool will work for a group of nodes and will probably contain more pragmatic functions that are more important for enterprise operators. 74 | 75 | ### Who is `ae` for? 76 | 77 | The `ae` utility should be familiar to every cloud operator. 78 | 79 | _Typical tasks_ such as: 80 | 81 | * "Rolling out NGINX to production" 82 | * "Counting the numbers of threads of a process" 83 | * "Changing the current database connection count for a service" 84 | * "Swinging traffic from one to another" 85 | 86 | should be possible with `ae`. 87 | 88 | There are more "practical" and "impact-oriented" tasks, and these probably need extra functionality, which they add to the lightweight `aer` tool. 89 | 90 | 91 | ## Quickstart 92 | 93 | > This section is reserved for future installation instructions as well as an example of integration. 94 | 95 | 96 | ## Usage 97 | 98 | There are a number of commands for `ae`. 99 | 100 | These are shown here in _alphabetical_ order. 101 | 102 |
103 | allocate 104 | 105 |   106 | 107 | Resources are reserved and prerequisites can be managed, but it **does not** start. It will not work if the resources are not available. 108 | 109 | ``` 110 | ae allocate 111 | ae allocate cell 112 | ae allocate pod 113 | ``` 114 | 115 |
116 | 117 |
118 | check 119 | 120 |   121 | 122 | Checks the nodes of the cluster and returns the current serving status with the given list of services. 123 | 124 | ``` 125 | ae check | ip > 126 | ``` 127 | 128 |
129 | 130 |
131 | discover 132 | 133 |   134 | 135 | Scans the complete network or cluster of nodes and returns information about it, including the version. 136 | 137 | ``` 138 | ae discover | ip > 139 | ``` 140 | 141 |
142 | 143 |
144 | free 145 | 146 |   147 | 148 | It frees the resources and destroys the prerequisites that were started. It will fail if the resources cannot be freed or do not exist. 149 | 150 | ``` 151 | ae free 152 | ae free cell 153 | ae free pod 154 | ``` 155 | 156 |
157 | 158 |
159 | logs 160 | 161 |   162 | 163 | This option will accept arguments and return or save some kind of logs. 164 | 165 | ``` 166 | ae logs 167 | ``` 168 | 169 |
170 | 171 |
172 | oci 173 | 174 |   175 | 176 | Here the [OCI CLI interface](https://github.com/opencontainers/runtime-tools/blob/master/docs/command-line-interface.md) is implemented with the respective subcommands. 177 | 178 | ``` 179 | ae oci 180 | ae oci create 181 | ae oci delete 182 | ae oci kill 183 | ae oci start 184 | ae oci status 185 | ``` 186 | 187 |
188 | 189 |
190 | start 191 | 192 |   193 | 194 | It will run the resources directly. 195 | 196 | ``` 197 | ae start 198 | ae start executable, exe 199 | ae start container # Note this has an alias: 'ae oci start' 200 | ``` 201 | 202 |
203 | 204 |
205 | stop 206 | 207 |   208 | 209 | It will stop the resources directly. 210 | 211 | ``` 212 | ae stop 213 | ae stop executable, exe 214 | ae stop container # Note this has an alias: 'ae oci kill' 215 | ``` 216 | 217 |
218 | 219 | 220 | ## Philosophy 221 | 222 | ### Less is more 223 | 224 | We do not want `ae` to become a junk drawer. In situations where we are considering bringing in new functionality to the project, we prefer to keep it out. 225 | 226 | For example imagine we were considering bringing in a `--filter` flag or a `--query` flag which would allow for us to filter the output returned by `ae`. In this situation we could very well identify advanced query patterns and libraries that would make it possible to adopt this feature. 227 | 228 | However, the feature comes at a maintenance cost, and frankly already exists in other tools. Instead of adopting a filter syntax and the associated burden that comes along with it, we instead focus on outputting to formats (such as JSON) which can easily be queried by existing tools (such as [jq](https://stedolan.github.io/jq/)). 229 | 230 | ### Just because you can, doesn't mean you should. 231 | 232 | Also known as **"The Jurassic Park"** rule. 233 | 234 | In situations where functionality is made available or "is possible" because of current technical advancements or capabilities, we prefer to focus on the goals of ae instead of supporting optional features "just because we can". 235 | 236 | For example imagine discovering a well written, and well regarded library that takes a Go struct and turns it into many formats which can be printed to the screen such as XML, JSON, YAML, TOML, BSON, and so on.... 237 | 238 | The project currently has established a need for JSON only, and plans to use jq for filtering and querying the data. In this case bringing the additional output types to the project "just because we can" would be a violation of this rule. 239 | 240 | ### No assumptions 241 | 242 | Also known as "no conveniences" or "no magic" policies. 243 | 244 | Assumptions and conveniences can delight a user, until the assumption is wrong in which case they can be catastrophically frustrating. We prefer to stay away from assumptions whenever possible. We prefer to stay away from "magic" and "convenience" style features. 245 | 246 | A famous example of this is bringing a logger to a project which assumes that the logger will be able to take ownership of the -v flag for verbose. There are many situations (docker -v for volume, or grep -v for reverse) where this assumption is flawed and will conflict with the goals of the project and panic at runtime. We do not want ae to turn into the logger where we assume something incorrectly that ends up causing problems for others. 247 | 248 | This will be a delicate principle to consider, as we also will need to hold firm opinions on things. A balance between a strong opinion and false assumptions is often times hard to get right. 249 | 250 | For example the ae project will need to read from a configuration file in order to communicate with a remote server. In one case we could assume that the configuration file is always in a familiar location for convenience. In the other case we do not want to force a user to pass a very long flag every time they run the tool. What do we do? 251 | 252 | We hold the opinion that the file should be in a few well documented locations, and create logic to try the locations if no flag is passed. If a user passes a flag we should remove all assumptions from the program and only consider the input. We prefer clarity over magic. 253 | 254 | ### Remote Servers instead of Local Systems! 255 | 256 | This is an interesting principle to consider. As the functionality of the ae tool grows we will inevitably need to execute logic somewhere. 257 | 258 | We prefer to keep the ae tool as "boring" and "unintelligent" as possible. We do not want the command line tool to do a lot of "work" or "processing" locally. In situations where we need to perform logic against a set of Aurae nodes, we need to find a way to take the logic out of ae for compatibility reasons. 259 | 260 | We do not want to get into a situation where we have multiple clients attempting to perform slightly different tasks locally against a server. Especially when these clients are subject to take long periods of time for their work to complete. 261 | 262 | In the event these types of situations arise, it is a sign that we likely need to deploy a scheduler or server mechanisms somewhere to manage the work on our behalf. 263 | 264 | 265 | ## Contribute 266 | 267 | The **[Aurae](https://github.com/aurae-runtime/aurae)** project is always looking for new members and developers. Here in this repository you can improve `ae`, but be sure to check out the [organisation](https://github.com/aurae-runtime) and the other internal projects. [This](https://github.com/aurae-runtime/community) is always a good starting point. 268 | 269 | ## Code Quality 270 | 271 | ### Linting 272 | 273 | We are using the [golangci-lint](https://golangci-lint.run/) tool to lint the code. You will need to install it for your system, and can find instructions at [this link](https://golangci-lint.run/usage/install/). This tool runs on every pull request, and it must pass before merging is allowed. You can run it locally with `make lint`. 274 | 275 | ### Formatting 276 | 277 | We are using the [gofmt](https://pkg.go.dev/cmd/gofmt) tool to lint the code. This tool runs on every pull request, and it must pass before merging is allowed. You can run it locally with `make format`. 278 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | # Creating an `ae` Release 2 | 3 | With goreleaser, the release process should be fairly straightforward as it does majority of the work. Below is a list of steps that should get the repo prepped for release and then generate a release. 4 | 5 | 1. Once you are happy with the state of `main` for the release cut, create a tag with the version number you wish to release. 6 | 1. Ex. If you are releasing 0.0.2, make a tag for `v0.0.2`. 7 | 2. This will trigger the Github Action for the goreleaser, and once it finishes successfully, you will have a Github Release for the tag. 8 | 3. The release will have a changelog of just all the commits since the last tag. You can edit it as needed. 9 | 4. You can also edit the release to add additional information, a preamble, whatever you need. 10 | 11 | If you wish to run goreleaser locally for testing the flow, you can run `make goreleaser`. If you want to adjust the default parameters you can change them by setting the `GORELEASER_FLAGS` variable. `make GORELEASER_FLAGS=--snapshot goreleaser`. 12 | -------------------------------------------------------------------------------- /bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurae-runtime/ae/dac5deee0c5cb454d56f7b752969b86266f0c599/bin/.gitkeep -------------------------------------------------------------------------------- /buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | plugins: 3 | - plugin: buf.build/protocolbuffers/go:v1.28.1 4 | out: pkg/api 5 | opt: paths=source_relative 6 | - plugin: buf.build/grpc/go:v1.2.0 7 | out: pkg/api 8 | opt: paths=source_relative 9 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # Client Go 2 | 3 | The official Go client of the Aurae Runtime project. 4 | -------------------------------------------------------------------------------- /cmd/cells/allocate/allocate.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package allocate 32 | 33 | import ( 34 | "context" 35 | "io" 36 | 37 | aeCMD "github.com/aurae-runtime/ae/cmd" 38 | "github.com/aurae-runtime/ae/pkg/cli" 39 | "github.com/spf13/cobra" 40 | ) 41 | 42 | type option struct { 43 | aeCMD.Option 44 | outputFormat *cli.OutputFormat 45 | writer io.Writer 46 | } 47 | 48 | func (o *option) Complete(_ []string) error { 49 | return nil 50 | } 51 | 52 | func (o *option) Validate() error { 53 | return nil 54 | } 55 | 56 | func (o *option) Execute(_ context.Context) error { 57 | return o.outputFormat.ToPrinter().Print(o.writer, "allocate called") 58 | } 59 | 60 | func (o *option) SetWriter(writer io.Writer) { 61 | o.writer = writer 62 | } 63 | 64 | func NewCMD(ctx context.Context) *cobra.Command { 65 | o := &option{} 66 | cmd := &cobra.Command{ 67 | Use: "allocate", 68 | Short: "Allocate a cell resource.", 69 | Long: `Allocate a cell resource.`, 70 | RunE: func(cmd *cobra.Command, args []string) error { 71 | return aeCMD.Run(ctx, o, cmd, args) 72 | }, 73 | } 74 | // add flags here 75 | 76 | return cmd 77 | } 78 | -------------------------------------------------------------------------------- /cmd/cells/cells.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package cells 32 | 33 | import ( 34 | "context" 35 | "io" 36 | 37 | aeCMD "github.com/aurae-runtime/ae/cmd" 38 | allocate "github.com/aurae-runtime/ae/cmd/cells/allocate" 39 | free "github.com/aurae-runtime/ae/cmd/cells/free" 40 | start "github.com/aurae-runtime/ae/cmd/cells/start" 41 | stop "github.com/aurae-runtime/ae/cmd/cells/stop" 42 | "github.com/aurae-runtime/ae/pkg/cli" 43 | "github.com/spf13/cobra" 44 | ) 45 | 46 | type option struct { 47 | aeCMD.Option 48 | outputFormat *cli.OutputFormat 49 | writer io.Writer 50 | } 51 | 52 | func (o *option) Complete(_ []string) error { 53 | return nil 54 | } 55 | 56 | func (o *option) Validate() error { 57 | return nil 58 | } 59 | 60 | func (o *option) Execute(_ context.Context) error { 61 | return o.outputFormat.ToPrinter().Print(o.writer, "cells called") 62 | } 63 | 64 | func (o *option) SetWriter(writer io.Writer) { 65 | o.writer = writer 66 | } 67 | 68 | func NewCMD(ctx context.Context) *cobra.Command { 69 | o := &option{} 70 | cmd := &cobra.Command{ 71 | Use: "cells", 72 | Short: "CLI for aurae cells service.", 73 | Long: `CLI for aurae cells service.`, 74 | RunE: func(cmd *cobra.Command, args []string) error { 75 | return aeCMD.Run(ctx, o, cmd, args) 76 | }, 77 | } 78 | // Here you will define your flags and configuration settings. 79 | 80 | // Cobra supports Persistent Flags which will work for this command 81 | // and all subcommands, e.g.: 82 | // startCmd.PersistentFlags().String("foo", "", "A help for foo") 83 | 84 | // Cobra supports local flags which will only run when this command 85 | // is called directly, e.g.: 86 | // startCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 87 | cmd.AddCommand(allocate.NewCMD(ctx)) 88 | cmd.AddCommand(free.NewCMD(ctx)) 89 | cmd.AddCommand(start.NewCMD(ctx)) 90 | cmd.AddCommand(stop.NewCMD(ctx)) 91 | 92 | return cmd 93 | } 94 | -------------------------------------------------------------------------------- /cmd/cells/free/free.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package free 32 | 33 | import ( 34 | "context" 35 | "io" 36 | 37 | aeCMD "github.com/aurae-runtime/ae/cmd" 38 | "github.com/aurae-runtime/ae/pkg/cli" 39 | "github.com/spf13/cobra" 40 | ) 41 | 42 | type option struct { 43 | aeCMD.Option 44 | outputFormat *cli.OutputFormat 45 | writer io.Writer 46 | } 47 | 48 | func (o *option) Complete(_ []string) error { 49 | return nil 50 | } 51 | 52 | func (o *option) Validate() error { 53 | return nil 54 | } 55 | 56 | func (o *option) Execute(_ context.Context) error { 57 | return o.outputFormat.ToPrinter().Print(o.writer, "free called") 58 | } 59 | 60 | func (o *option) SetWriter(writer io.Writer) { 61 | o.writer = writer 62 | } 63 | 64 | func NewCMD(ctx context.Context) *cobra.Command { 65 | o := &option{} 66 | cmd := &cobra.Command{ 67 | Use: "free", 68 | Short: "Free a cell resource.", 69 | Long: `Free a cell resource.`, 70 | RunE: func(cmd *cobra.Command, args []string) error { 71 | return aeCMD.Run(ctx, o, cmd, args) 72 | }, 73 | } 74 | // add flags here 75 | 76 | return cmd 77 | } 78 | -------------------------------------------------------------------------------- /cmd/cells/start/start.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package start 32 | 33 | import ( 34 | "context" 35 | "io" 36 | 37 | aeCMD "github.com/aurae-runtime/ae/cmd" 38 | "github.com/aurae-runtime/ae/pkg/cli" 39 | "github.com/spf13/cobra" 40 | ) 41 | 42 | type option struct { 43 | aeCMD.Option 44 | outputFormat *cli.OutputFormat 45 | writer io.Writer 46 | } 47 | 48 | func (o *option) Complete(_ []string) error { 49 | return nil 50 | } 51 | 52 | func (o *option) Validate() error { 53 | return nil 54 | } 55 | 56 | func (o *option) Execute(_ context.Context) error { 57 | return o.outputFormat.ToPrinter().Print(o.writer, "start called") 58 | } 59 | 60 | func (o *option) SetWriter(writer io.Writer) { 61 | o.writer = writer 62 | } 63 | 64 | func NewCMD(ctx context.Context) *cobra.Command { 65 | o := &option{} 66 | cmd := &cobra.Command{ 67 | Use: "start", 68 | Short: "Start a cell resource.", 69 | Long: `Start a cell resource.`, 70 | RunE: func(cmd *cobra.Command, args []string) error { 71 | return aeCMD.Run(ctx, o, cmd, args) 72 | }, 73 | } 74 | // add flags here 75 | 76 | return cmd 77 | } 78 | -------------------------------------------------------------------------------- /cmd/cells/stop/stop.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package stop 32 | 33 | import ( 34 | "context" 35 | "io" 36 | 37 | aeCMD "github.com/aurae-runtime/ae/cmd" 38 | "github.com/aurae-runtime/ae/pkg/cli" 39 | "github.com/spf13/cobra" 40 | ) 41 | 42 | type option struct { 43 | aeCMD.Option 44 | outputFormat *cli.OutputFormat 45 | writer io.Writer 46 | } 47 | 48 | func (o *option) Complete(_ []string) error { 49 | return nil 50 | } 51 | 52 | func (o *option) Validate() error { 53 | return nil 54 | } 55 | 56 | func (o *option) Execute(_ context.Context) error { 57 | return o.outputFormat.ToPrinter().Print(o.writer, "stop called") 58 | } 59 | 60 | func (o *option) SetWriter(writer io.Writer) { 61 | o.writer = writer 62 | } 63 | 64 | func NewCMD(ctx context.Context) *cobra.Command { 65 | o := &option{} 66 | cmd := &cobra.Command{ 67 | Use: "stop", 68 | Short: "Stop a cell resource.", 69 | Long: `Stop a cell resource.`, 70 | RunE: func(cmd *cobra.Command, args []string) error { 71 | return aeCMD.Run(ctx, o, cmd, args) 72 | }, 73 | } 74 | // add flags here 75 | 76 | return cmd 77 | } 78 | -------------------------------------------------------------------------------- /cmd/cmd.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package aeCMD 32 | 33 | import ( 34 | "context" 35 | "io" 36 | 37 | "github.com/spf13/cobra" 38 | ) 39 | 40 | type Option interface { 41 | // Complete is the method where the option is extracting the data from the 42 | // args and is setting its different attributes. 43 | Complete(args []string) error 44 | // Validate is ensuring that the different value of its attribute are 45 | // coherent (when it makes sense). 46 | Validate() error 47 | // Execute is the method used by the command to actually run the business 48 | // logic. 49 | Execute(context.Context) error 50 | SetWriter(writer io.Writer) 51 | } 52 | 53 | func Run(ctx context.Context, o Option, cmd *cobra.Command, args []string) error { 54 | o.SetWriter(cmd.OutOrStdout()) 55 | if err := o.Complete(args); err != nil { 56 | return err 57 | } 58 | if err := o.Validate(); err != nil { 59 | return err 60 | } 61 | return o.Execute(ctx) 62 | } 63 | -------------------------------------------------------------------------------- /cmd/discovery/discover.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package discovery 32 | 33 | import ( 34 | "context" 35 | "errors" 36 | "fmt" 37 | "io" 38 | "log" 39 | "net" 40 | 41 | "github.com/3th1nk/cidr" 42 | "github.com/aurae-runtime/ae/pkg/cli" 43 | "github.com/aurae-runtime/ae/pkg/cli/printer" 44 | "github.com/aurae-runtime/ae/pkg/client" 45 | "github.com/aurae-runtime/ae/pkg/config" 46 | "github.com/spf13/cobra" 47 | 48 | aeCMD "github.com/aurae-runtime/ae/cmd" 49 | discoveryv0 "github.com/aurae-runtime/ae/pkg/api/v0/discovery" 50 | ) 51 | 52 | type outputDiscoverNode struct { 53 | Available bool 54 | Version string `json:"version"` 55 | } 56 | 57 | type outputDiscover struct { 58 | Nodes map[string]outputDiscoverNode `json:"nodes"` 59 | } 60 | 61 | type option struct { 62 | aeCMD.Option 63 | ctx context.Context 64 | auth *config.Auth 65 | outputFormat *cli.OutputFormat 66 | cidr string 67 | ip string 68 | port uint16 69 | protocol string 70 | verbose bool 71 | writer io.Writer 72 | 73 | output *outputDiscover 74 | } 75 | 76 | func (o *option) Complete(args []string) error { 77 | switch args[0] { 78 | case "cidr": 79 | o.cidr = args[1] 80 | case "ip": 81 | o.ip = args[1] 82 | default: 83 | return errors.New("either 'cidr' or 'ip' must be passed to this command") 84 | } 85 | return nil 86 | } 87 | 88 | func (o *option) Validate() error { 89 | if err := o.outputFormat.Validate(); err != nil { 90 | return err 91 | } 92 | 93 | if len(o.cidr) != 0 { 94 | if _, _, err := net.ParseCIDR(o.cidr); err != nil { 95 | return err 96 | } 97 | } else if len(o.ip) != 0 { 98 | if ip := net.ParseIP(o.ip); ip == nil { 99 | return fmt.Errorf("failed to parse ip %q", o.ip) 100 | } 101 | } else { 102 | return errors.New("either 'cidr' or 'ip' must be passed to this command") 103 | } 104 | 105 | return nil 106 | } 107 | 108 | func (o *option) Execute(ctx context.Context) error { 109 | o.ctx = ctx 110 | 111 | o.output = &outputDiscover{ 112 | Nodes: make(map[string]outputDiscoverNode), 113 | } 114 | 115 | o.protocol = "tcp4" 116 | 117 | if len(o.cidr) != 0 { 118 | c, err := cidr.Parse(o.cidr) 119 | if err != nil { 120 | return err 121 | } 122 | if c.IsIPv6() { 123 | o.protocol = "tcp6" 124 | } 125 | // TODO: use some concurrency magic to run these async 126 | c.Each(o.checkHost) 127 | } else if len(o.ip) != 0 { 128 | if net.ParseIP(o.ip).To4() == nil { 129 | o.protocol = "tcp6" 130 | } 131 | o.checkHost(o.ip) 132 | } 133 | 134 | return o.outputFormat.ToPrinter().Print(o.writer, o.output) 135 | } 136 | 137 | func (o *option) SetWriter(writer io.Writer) { 138 | o.writer = writer 139 | } 140 | 141 | func (o option) checkHost(ip_str string) bool { 142 | if o.verbose { 143 | log.Printf("connecting to %s:%d using protocol %s\n", ip_str, o.port, o.protocol) 144 | } 145 | 146 | c, err := client.New(o.ctx, config.WithAuth(*o.auth), config.WithSystem(config.System{Protocol: o.protocol, Socket: net.JoinHostPort(ip_str, fmt.Sprintf("%d", o.port))})) 147 | if err != nil { 148 | log.Fatalf("failed to create client: %s", err) 149 | return false 150 | } 151 | 152 | d, err := c.Discovery() 153 | if err != nil { 154 | log.Fatalf("failed to dial Discovery service: %s", err) 155 | return false 156 | } 157 | 158 | rsp, err := d.Discover(o.ctx, &discoveryv0.DiscoverRequest{}) 159 | if err != nil { 160 | if o.verbose { 161 | log.Printf("failed to call discover: %s. not an aurae node\n", err) 162 | } 163 | return true 164 | } 165 | 166 | if rsp.Healthy { 167 | o.output.Nodes[ip_str] = outputDiscoverNode{Available: true, Version: rsp.Version} 168 | } else { 169 | o.output.Nodes[ip_str] = outputDiscoverNode{Available: false} 170 | } 171 | return true 172 | } 173 | 174 | func NewCMD(ctx context.Context) *cobra.Command { 175 | o := &option{ 176 | auth: &config.Auth{}, 177 | outputFormat: cli.NewOutputFormat(). 178 | WithDefaultFormat(printer.NewJSON().Format()). 179 | WithPrinter(printer.NewJSON()), 180 | } 181 | cmd := &cobra.Command{ 182 | Use: "discover [cidr |ip ]", 183 | Short: "Scans a node or cluster of nodes for active Aurae Discovery services.", 184 | Args: cobra.MinimumNArgs(2), 185 | RunE: func(cmd *cobra.Command, args []string) error { 186 | return aeCMD.Run(ctx, o, cmd, args) 187 | }, 188 | } 189 | o.auth.AddFlags(cmd) 190 | o.outputFormat.AddFlags(cmd) 191 | cmd.Flags().Uint16Var(&o.port, "port", o.port, "The port to use when trying to connect") 192 | cmd.Flags().BoolVar(&o.verbose, "verbose", o.verbose, "Lots of output") 193 | return cmd 194 | } 195 | -------------------------------------------------------------------------------- /cmd/discovery/discover_test.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/aurae-runtime/ae/pkg/cli" 7 | "github.com/aurae-runtime/ae/pkg/cli/printer" 8 | ) 9 | 10 | func TestComplete(t *testing.T) { 11 | ts := []struct { 12 | args0 string 13 | wanterr bool 14 | }{ 15 | { 16 | "cidr", 17 | false, 18 | }, 19 | { 20 | "ip", 21 | false, 22 | }, 23 | { 24 | "foo", 25 | true, 26 | }, 27 | } 28 | 29 | for _, tt := range ts { 30 | o := &option{} 31 | goterr := o.Complete([]string{tt.args0, "foo"}) 32 | if tt.wanterr && goterr == nil { 33 | t.Fatal("want error, got no error") 34 | } 35 | if !tt.wanterr && goterr != nil { 36 | t.Fatal("want no error, got error") 37 | } 38 | } 39 | } 40 | 41 | func TestValidate(t *testing.T) { 42 | ts := []struct { 43 | name string 44 | outputFormat *cli.OutputFormat 45 | cidr string 46 | ip string 47 | wanterr bool 48 | }{ 49 | { 50 | name: "no output format", 51 | outputFormat: cli.NewOutputFormat(), 52 | wanterr: true, 53 | }, 54 | { 55 | name: "no cidr or ip", 56 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 57 | wanterr: true, 58 | }, 59 | { 60 | name: "invalid cidr", 61 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 62 | cidr: "invalid cidr", 63 | wanterr: true, 64 | }, 65 | { 66 | name: "valid cidr", 67 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 68 | cidr: "192.168.170.0/32", 69 | wanterr: false, 70 | }, 71 | { 72 | name: "invalid ip", 73 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 74 | ip: "invalid ip", 75 | wanterr: true, 76 | }, 77 | { 78 | name: "valid ip", 79 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 80 | ip: "10.0.0.0", 81 | wanterr: false, 82 | }, 83 | } 84 | 85 | for _, tt := range ts { 86 | o := &option{cidr: tt.cidr, ip: tt.ip, outputFormat: tt.outputFormat} 87 | goterr := o.Validate() 88 | if tt.wanterr && goterr == nil { 89 | t.Fatalf("[%s] want error, got no error", tt.name) 90 | } 91 | if !tt.wanterr && goterr != nil { 92 | t.Fatalf("[%s] want no error, got error: %s", tt.name, goterr) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /cmd/health/check.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package health 32 | 33 | import ( 34 | "context" 35 | "errors" 36 | "fmt" 37 | "io" 38 | "log" 39 | "net" 40 | "strings" 41 | 42 | "github.com/3th1nk/cidr" 43 | "github.com/aurae-runtime/ae/pkg/cli" 44 | "github.com/aurae-runtime/ae/pkg/cli/printer" 45 | "github.com/aurae-runtime/ae/pkg/client" 46 | "github.com/aurae-runtime/ae/pkg/config" 47 | "github.com/spf13/cobra" 48 | 49 | aeCMD "github.com/aurae-runtime/ae/cmd" 50 | //healthv1 "github.com/aurae-runtime/ae/pkg/api/grpc/health/v1/health" 51 | healthv1 "google.golang.org/grpc/health/grpc_health_v1" 52 | ) 53 | 54 | type outputCheckNode struct { 55 | // Maps from a service name to a serving status. 56 | Statuses map[string]string `json:"version"` 57 | } 58 | 59 | type outputCheck struct { 60 | Nodes map[string]outputCheckNode `json:"nodes"` 61 | } 62 | 63 | type option struct { 64 | aeCMD.Option 65 | 66 | // TODO: abstract the next batch of fields into a "clusterOption" 67 | ctx context.Context 68 | auth *config.Auth 69 | outputFormat *cli.OutputFormat 70 | cidr string 71 | ip string 72 | port uint16 73 | protocol string 74 | verbose bool 75 | writer io.Writer 76 | 77 | services []string 78 | output *outputCheck 79 | } 80 | 81 | func (o *option) Complete(args []string) error { 82 | if len(args) != 3 { 83 | return errors.New("expected 'cidr' or 'ip' and a list of services passed to this command") 84 | } 85 | switch args[0] { 86 | case "cidr": 87 | o.cidr = args[1] 88 | case "ip": 89 | o.ip = args[1] 90 | default: 91 | return errors.New("either 'cidr' or 'ip' must be passed to this command") 92 | } 93 | 94 | o.services = strings.Split(args[2], ",") 95 | 96 | return nil 97 | } 98 | 99 | func (o *option) Validate() error { 100 | if err := o.outputFormat.Validate(); err != nil { 101 | return err 102 | } 103 | 104 | if len(o.cidr) != 0 { 105 | if _, _, err := net.ParseCIDR(o.cidr); err != nil { 106 | return err 107 | } 108 | } else if len(o.ip) != 0 { 109 | if ip := net.ParseIP(o.ip); ip == nil { 110 | return fmt.Errorf("failed to parse ip %q", o.ip) 111 | } 112 | } else { 113 | return errors.New("either 'cidr' or 'ip' must be passed to this command") 114 | } 115 | 116 | // TODO: check for valid service list. 117 | if len(o.services) == 0 { 118 | return errors.New("expected list of services to be provided") 119 | } 120 | 121 | return nil 122 | } 123 | 124 | func (o *option) Execute(ctx context.Context) error { 125 | o.ctx = ctx 126 | 127 | o.output = &outputCheck{ 128 | Nodes: make(map[string]outputCheckNode), 129 | } 130 | 131 | o.protocol = "tcp4" 132 | 133 | if len(o.cidr) != 0 { 134 | c, err := cidr.Parse(o.cidr) 135 | if err != nil { 136 | return err 137 | } 138 | if c.IsIPv6() { 139 | o.protocol = "tcp6" 140 | } 141 | // TODO: use some concurrency magic to run these async 142 | c.Each(o.checkHost) 143 | } else if len(o.ip) != 0 { 144 | if net.ParseIP(o.ip).To4() == nil { 145 | o.protocol = "tcp6" 146 | } 147 | o.checkHost(o.ip) 148 | } 149 | 150 | return o.outputFormat.ToPrinter().Print(o.writer, o.output) 151 | } 152 | 153 | func (o *option) SetWriter(writer io.Writer) { 154 | o.writer = writer 155 | } 156 | 157 | func (o option) checkHost(ip_str string) bool { 158 | if o.verbose { 159 | log.Printf("connecting to %s:%d using protocol %s\n", ip_str, o.port, o.protocol) 160 | } 161 | 162 | c, err := client.New(o.ctx, config.WithAuth(*o.auth), config.WithSystem(config.System{Protocol: o.protocol, Socket: net.JoinHostPort(ip_str, fmt.Sprintf("%d", o.port))})) 163 | if err != nil { 164 | log.Fatalf("failed to create client: %s", err) 165 | return false 166 | } 167 | 168 | h, err := c.Health() 169 | if err != nil { 170 | log.Fatalf("failed to dial Health service: %s", err) 171 | return false 172 | } 173 | 174 | o.output.Nodes[ip_str] = outputCheckNode{ 175 | Statuses: make(map[string]string), 176 | } 177 | 178 | for _, s := range o.services { 179 | rsp, err := h.Check(o.ctx, &healthv1.HealthCheckRequest{Service: s}) 180 | if err != nil { 181 | if o.verbose { 182 | log.Printf("failed to call discover: %s. not an aurae node\n", err) 183 | } 184 | return true 185 | } 186 | 187 | o.output.Nodes[ip_str].Statuses[s] = rsp.Status.String() 188 | } 189 | return true 190 | } 191 | 192 | func NewCMD(ctx context.Context) *cobra.Command { 193 | o := &option{ 194 | auth: &config.Auth{}, 195 | outputFormat: cli.NewOutputFormat(). 196 | WithDefaultFormat(printer.NewJSON().Format()). 197 | WithPrinter(printer.NewJSON()), 198 | } 199 | cmd := &cobra.Command{ 200 | Use: "check [cidr |ip ] [services]", 201 | Short: "Scans a node or cluster of nodes and checks the health of the given list of services", 202 | Args: cobra.MinimumNArgs(3), 203 | RunE: func(cmd *cobra.Command, args []string) error { 204 | return aeCMD.Run(ctx, o, cmd, args) 205 | }, 206 | } 207 | o.auth.AddFlags(cmd) 208 | o.outputFormat.AddFlags(cmd) 209 | cmd.Flags().Uint16Var(&o.port, "port", o.port, "The port to use when trying to connect") 210 | cmd.Flags().BoolVar(&o.verbose, "verbose", o.verbose, "Lots of output") 211 | return cmd 212 | } 213 | -------------------------------------------------------------------------------- /cmd/health/check_test.go: -------------------------------------------------------------------------------- 1 | package health 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/aurae-runtime/ae/pkg/cli" 7 | "github.com/aurae-runtime/ae/pkg/cli/printer" 8 | ) 9 | 10 | func TestComplete(t *testing.T) { 11 | ts := []struct { 12 | args []string 13 | wanterr bool 14 | }{ 15 | { 16 | args: []string{"cidr", "192.168.0.0/32", "list,of,services"}, 17 | wanterr: false, 18 | }, 19 | { 20 | args: []string{"ip", "10.0.0.0", "list,of,services"}, 21 | wanterr: false, 22 | }, 23 | { 24 | args: []string{"cidr"}, 25 | wanterr: true, 26 | }, 27 | { 28 | args: []string{"cidr", "192.168.0.0/32"}, 29 | wanterr: true, 30 | }, 31 | } 32 | 33 | for _, tt := range ts { 34 | o := &option{} 35 | goterr := o.Complete(tt.args) 36 | if tt.wanterr && goterr == nil { 37 | t.Fatal("want error, got no error") 38 | } 39 | if !tt.wanterr && goterr != nil { 40 | t.Fatalf("want no error, got error %q", goterr) 41 | } 42 | } 43 | } 44 | 45 | func TestValidate(t *testing.T) { 46 | ts := []struct { 47 | name string 48 | outputFormat *cli.OutputFormat 49 | cidr string 50 | ip string 51 | services []string 52 | wanterr bool 53 | }{ 54 | { 55 | name: "no output format", 56 | outputFormat: cli.NewOutputFormat(), 57 | wanterr: true, 58 | }, 59 | { 60 | name: "no cidr or ip", 61 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 62 | wanterr: true, 63 | }, 64 | { 65 | name: "no services", 66 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 67 | cidr: "192.168.170.0/32", 68 | wanterr: true, 69 | }, 70 | { 71 | name: "invalid cidr", 72 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 73 | cidr: "invalid cidr", 74 | wanterr: true, 75 | }, 76 | { 77 | name: "valid cidr", 78 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 79 | cidr: "192.168.170.0/32", 80 | services: []string{"foo", "bar"}, 81 | wanterr: false, 82 | }, 83 | { 84 | name: "invalid ip", 85 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 86 | ip: "invalid ip", 87 | wanterr: true, 88 | }, 89 | { 90 | name: "valid ip", 91 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 92 | ip: "10.0.0.0", 93 | services: []string{"foo", "bar"}, 94 | wanterr: false, 95 | }, 96 | } 97 | 98 | for _, tt := range ts { 99 | o := &option{cidr: tt.cidr, ip: tt.ip, outputFormat: tt.outputFormat, services: tt.services} 100 | goterr := o.Validate() 101 | if tt.wanterr && goterr == nil { 102 | t.Fatalf("[%s] want error, got no error", tt.name) 103 | } 104 | if !tt.wanterr && goterr != nil { 105 | t.Fatalf("[%s] want no error, got error: %s", tt.name, goterr) 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /cmd/observe/observe.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package observe 32 | 33 | import ( 34 | "context" 35 | "errors" 36 | "fmt" 37 | "io" 38 | "log" 39 | "net" 40 | 41 | "github.com/aurae-runtime/ae/pkg/cli" 42 | "github.com/aurae-runtime/ae/pkg/cli/printer" 43 | "github.com/aurae-runtime/ae/pkg/client" 44 | "github.com/aurae-runtime/ae/pkg/config" 45 | "github.com/spf13/cobra" 46 | 47 | aeCMD "github.com/aurae-runtime/ae/cmd" 48 | observev0 "github.com/aurae-runtime/ae/pkg/api/v0/observe" 49 | ) 50 | 51 | type outputObserve struct{} 52 | 53 | type option struct { 54 | aeCMD.Option 55 | ctx context.Context 56 | ip string 57 | logtype string 58 | port uint16 59 | protocol string 60 | verbose bool 61 | writer io.Writer 62 | output *outputObserve 63 | outputFormat *cli.OutputFormat 64 | } 65 | 66 | func (o *option) Complete(args []string) error { 67 | log.Println(args) 68 | if len(args) != 2 { 69 | return errors.New("expected ip address and log type to be passed to this command") 70 | } 71 | o.ip = args[0] 72 | o.logtype = args[1] 73 | return nil 74 | } 75 | 76 | func (o *option) Validate() error { 77 | if err := o.outputFormat.Validate(); err != nil { 78 | return err 79 | } 80 | if len(o.ip) == 0 { 81 | return errors.New("ip address must be passed to this command") 82 | } 83 | if ip := net.ParseIP(o.ip); ip == nil { 84 | return fmt.Errorf("failed to parse IP %q", o.ip) 85 | } 86 | if o.logtype != "daemon" && o.logtype != "subprocesses" { 87 | return errors.New("either 'daemon' or 'subprocesses' must be passed to the command") 88 | } 89 | return nil 90 | } 91 | 92 | func (o *option) Execute(ctx context.Context) error { 93 | o.ctx = ctx 94 | o.output = &outputObserve{} 95 | 96 | o.protocol = "tcp4" 97 | if net.ParseIP(o.ip).To4() == nil { 98 | o.protocol = "tcp6" 99 | } 100 | o.observeHost(o.ip) 101 | return o.outputFormat.ToPrinter().Print(o.writer, o.output) 102 | } 103 | 104 | func (o *option) SetWriter(writer io.Writer) { 105 | o.writer = writer 106 | } 107 | 108 | func (o option) observeHost(ip_str string) bool { 109 | if o.verbose { 110 | log.Printf("connecting to %s:%d using protocol %s\n", ip_str, o.port, o.protocol) 111 | } 112 | 113 | c, err := client.New(o.ctx, config.WithSystem(config.System{Protocol: o.protocol, Socket: net.JoinHostPort(ip_str, fmt.Sprintf("%d", o.port))})) 114 | if err != nil { 115 | log.Fatalf("failed to create client: %s", err) 116 | return false 117 | } 118 | 119 | obs, err := c.Observe() 120 | if err != nil { 121 | log.Fatalf("failed to dial Observe service: %s", err) 122 | return false 123 | } 124 | 125 | // TODO: handle output format 126 | switch o.logtype { 127 | case "daemon": 128 | // TODO: request parameters 129 | req := observev0.GetAuraeDaemonLogStreamRequest{} // TODO 130 | stream, err := obs.GetAuraeDaemonLogStream(o.ctx, &req) 131 | if err != nil { 132 | log.Fatalf("%s", err) 133 | return false 134 | } 135 | for { 136 | resp, err := stream.Recv() 137 | if err == io.EOF { 138 | break 139 | } 140 | if err != nil { 141 | log.Fatalf("%s", err) 142 | return false 143 | } 144 | if _, err := o.writer.Write([]byte(resp.Item.Line)); err != nil { 145 | log.Fatalf("%s", err) 146 | return false 147 | } 148 | } 149 | case "subprocesses": 150 | // TODO: request parameters 151 | req := observev0.GetSubProcessStreamRequest{} // TODO 152 | stream, err := obs.GetSubProcessStream(o.ctx, &req) 153 | if err != nil { 154 | log.Fatalf("%s", err) 155 | return false 156 | } 157 | for { 158 | resp, err := stream.Recv() 159 | if err == io.EOF { 160 | break 161 | } 162 | if err != nil { 163 | log.Fatalf("%s", err) 164 | return false 165 | } 166 | if _, err := o.writer.Write([]byte(resp.Item.Line)); err != nil { 167 | log.Fatalf("%s", err) 168 | return false 169 | } 170 | } 171 | } 172 | 173 | return true 174 | } 175 | 176 | func NewCMD(ctx context.Context) *cobra.Command { 177 | o := &option{ 178 | outputFormat: cli.NewOutputFormat().WithDefaultFormat(printer.NewJSON().Format()).WithPrinter(printer.NewJSON()), 179 | } 180 | cmd := &cobra.Command{ 181 | Use: "observe ", 182 | Short: "get a stream of logs either from the aurae daemon or spawned subprocesses running on the given IP address", 183 | Args: cobra.MinimumNArgs(2), 184 | RunE: func(cmd *cobra.Command, args []string) error { 185 | return aeCMD.Run(ctx, o, cmd, args) 186 | }, 187 | } 188 | o.outputFormat.AddFlags(cmd) 189 | cmd.Flags().Uint16Var(&o.port, "port", o.port, "The port to use when connecting") 190 | cmd.Flags().BoolVar(&o.verbose, "verbose", o.verbose, "Lots of output") 191 | return cmd 192 | } 193 | -------------------------------------------------------------------------------- /cmd/observe/observe_test.go: -------------------------------------------------------------------------------- 1 | package observe 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/aurae-runtime/ae/pkg/cli" 7 | "github.com/aurae-runtime/ae/pkg/cli/printer" 8 | ) 9 | 10 | func TestComplete(t *testing.T) { 11 | ts := []struct { 12 | args []string 13 | wantip string 14 | wantlogtype string 15 | wanterr bool 16 | }{ 17 | { 18 | []string{""}, 19 | "", "", true, 20 | }, 21 | { 22 | []string{"foo"}, 23 | "", "", true, 24 | }, 25 | { 26 | []string{"foo", "bar"}, 27 | "foo", "bar", false, 28 | }, 29 | { 30 | []string{"foo", "bar", "baz"}, 31 | "", "", true, 32 | }, 33 | } 34 | 35 | for _, tt := range ts { 36 | o := &option{} 37 | goterr := o.Complete(tt.args) 38 | if tt.wanterr && goterr == nil { 39 | t.Fatal("want error, got no error") 40 | } 41 | if !tt.wanterr && goterr != nil { 42 | t.Fatal("want no error, got error") 43 | } 44 | if tt.wantip != o.ip { 45 | t.Fatalf("want ip %q, got ip %q", tt.wantip, o.ip) 46 | } 47 | if tt.wantlogtype != o.logtype { 48 | t.Fatalf("want logtype %q, got logtype %q", tt.wantlogtype, o.logtype) 49 | } 50 | } 51 | } 52 | 53 | func TestValidate(t *testing.T) { 54 | ts := []struct { 55 | name string 56 | outputFormat *cli.OutputFormat 57 | ip string 58 | logtype string 59 | wanterr bool 60 | }{ 61 | { 62 | name: "no output format", 63 | outputFormat: cli.NewOutputFormat(), 64 | wanterr: true, 65 | }, 66 | { 67 | name: "no ip or logtype", 68 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 69 | wanterr: true, 70 | }, 71 | { 72 | name: "invalid ip", 73 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 74 | ip: "invalid ip", 75 | wanterr: true, 76 | }, 77 | { 78 | name: "invalid logtype", 79 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 80 | ip: "10.0.0.0", 81 | logtype: "invalid", 82 | wanterr: true, 83 | }, 84 | { 85 | name: "valid ip and logtype", 86 | outputFormat: cli.NewOutputFormat().WithDefaultFormat("json").WithPrinter(printer.NewJSON()), 87 | ip: "10.0.0.0", 88 | logtype: "daemon", 89 | wanterr: false, 90 | }, 91 | } 92 | 93 | for _, tt := range ts { 94 | o := &option{ip: tt.ip, logtype: tt.logtype, outputFormat: tt.outputFormat} 95 | goterr := o.Validate() 96 | if tt.wanterr && goterr == nil { 97 | t.Fatalf("[%s] want error, got no error", tt.name) 98 | } 99 | if !tt.wanterr && goterr != nil { 100 | t.Fatalf("[%s] want no error, got error: %s", tt.name, goterr) 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /cmd/oci/create/create.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package create 32 | 33 | import ( 34 | "context" 35 | "fmt" 36 | "io" 37 | 38 | aeCMD "github.com/aurae-runtime/ae/cmd" 39 | "github.com/aurae-runtime/ae/pkg/cli" 40 | "github.com/aurae-runtime/ae/pkg/cli/printer" 41 | "github.com/spf13/cobra" 42 | ) 43 | 44 | type option struct { 45 | aeCMD.Option 46 | writer io.Writer 47 | outputFormat *cli.OutputFormat 48 | } 49 | 50 | func (o *option) Complete(_ []string) error { 51 | return nil 52 | } 53 | 54 | func (o *option) Validate() error { 55 | if err := o.outputFormat.Validate(); err != nil { 56 | return err 57 | } 58 | return nil 59 | } 60 | 61 | func (o *option) Execute(_ context.Context) error { 62 | fmt.Fprintln(o.writer, "create called") 63 | return nil 64 | } 65 | 66 | func (o *option) SetWriter(writer io.Writer) { 67 | o.writer = writer 68 | } 69 | 70 | func NewCMD(ctx context.Context) *cobra.Command { 71 | o := &option{ 72 | outputFormat: cli.NewOutputFormat(). 73 | WithPrinter(printer.NewJSON()). 74 | WithPrinter(printer.NewYAML()), 75 | } 76 | cmd := &cobra.Command{ 77 | Use: "create", 78 | Short: "Create a container from a bundle directory.", 79 | Long: `Create a container from a bundle directory.`, 80 | RunE: func(cmd *cobra.Command, args []string) error { 81 | return aeCMD.Run(ctx, o, cmd, args) 82 | }, 83 | } 84 | o.outputFormat.AddFlags(cmd) 85 | 86 | return cmd 87 | } 88 | -------------------------------------------------------------------------------- /cmd/oci/delete/delete.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package delete 32 | 33 | import ( 34 | "context" 35 | "fmt" 36 | "io" 37 | 38 | aeCMD "github.com/aurae-runtime/ae/cmd" 39 | "github.com/aurae-runtime/ae/pkg/cli" 40 | "github.com/aurae-runtime/ae/pkg/cli/printer" 41 | "github.com/spf13/cobra" 42 | ) 43 | 44 | type option struct { 45 | aeCMD.Option 46 | writer io.Writer 47 | outputFormat *cli.OutputFormat 48 | } 49 | 50 | func (o *option) Complete(_ []string) error { 51 | return nil 52 | } 53 | 54 | func (o *option) Validate() error { 55 | if err := o.outputFormat.Validate(); err != nil { 56 | return err 57 | } 58 | return nil 59 | } 60 | 61 | func (o *option) Execute(_ context.Context) error { 62 | fmt.Fprintln(o.writer, "delete called") 63 | return nil 64 | } 65 | 66 | func (o *option) SetWriter(writer io.Writer) { 67 | o.writer = writer 68 | } 69 | 70 | func NewCMD(ctx context.Context) *cobra.Command { 71 | o := &option{ 72 | outputFormat: cli.NewOutputFormat(). 73 | WithPrinter(printer.NewJSON()). 74 | WithPrinter(printer.NewYAML()), 75 | } 76 | cmd := &cobra.Command{ 77 | Use: "delete", 78 | Short: "Release container resources after the container process has exited.", 79 | Long: `Release container resources after the container process has exited.`, 80 | RunE: func(cmd *cobra.Command, args []string) error { 81 | return aeCMD.Run(ctx, o, cmd, args) 82 | }, 83 | } 84 | o.outputFormat.AddFlags(cmd) 85 | 86 | return cmd 87 | } 88 | -------------------------------------------------------------------------------- /cmd/oci/kill/kill.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package kill 32 | 33 | import ( 34 | "context" 35 | "fmt" 36 | "io" 37 | 38 | aeCMD "github.com/aurae-runtime/ae/cmd" 39 | "github.com/aurae-runtime/ae/pkg/cli" 40 | "github.com/aurae-runtime/ae/pkg/cli/printer" 41 | "github.com/spf13/cobra" 42 | ) 43 | 44 | type option struct { 45 | aeCMD.Option 46 | writer io.Writer 47 | outputFormat *cli.OutputFormat 48 | } 49 | 50 | func (o *option) Complete(_ []string) error { 51 | return nil 52 | } 53 | 54 | func (o *option) Validate() error { 55 | if err := o.outputFormat.Validate(); err != nil { 56 | return err 57 | } 58 | return nil 59 | } 60 | 61 | func (o *option) Execute(_ context.Context) error { 62 | fmt.Fprintln(o.writer, "kill called") 63 | return nil 64 | } 65 | 66 | func (o *option) SetWriter(writer io.Writer) { 67 | o.writer = writer 68 | } 69 | 70 | func NewCMD(ctx context.Context) *cobra.Command { 71 | o := &option{ 72 | outputFormat: cli.NewOutputFormat(). 73 | WithPrinter(printer.NewJSON()). 74 | WithPrinter(printer.NewYAML()), 75 | } 76 | cmd := &cobra.Command{ 77 | Use: "kill", 78 | Short: "Send a signal to the container process.", 79 | Long: `Send a signal to the container process.`, 80 | RunE: func(cmd *cobra.Command, args []string) error { 81 | return aeCMD.Run(ctx, o, cmd, args) 82 | }, 83 | } 84 | o.outputFormat.AddFlags(cmd) 85 | 86 | return cmd 87 | } 88 | -------------------------------------------------------------------------------- /cmd/oci/oci.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package oci 32 | 33 | import ( 34 | "context" 35 | "fmt" 36 | "io" 37 | 38 | aeCMD "github.com/aurae-runtime/ae/cmd" 39 | "github.com/aurae-runtime/ae/cmd/oci/create" 40 | "github.com/aurae-runtime/ae/cmd/oci/delete" 41 | "github.com/aurae-runtime/ae/cmd/oci/kill" 42 | "github.com/aurae-runtime/ae/cmd/oci/start" 43 | "github.com/aurae-runtime/ae/cmd/oci/state" 44 | "github.com/spf13/cobra" 45 | ) 46 | 47 | type option struct { 48 | aeCMD.Option 49 | writer io.Writer 50 | } 51 | 52 | func (o *option) Complete(_ []string) error { 53 | return nil 54 | } 55 | 56 | func (o *option) Validate() error { 57 | return nil 58 | } 59 | 60 | func (o *option) Execute(_ context.Context) error { 61 | fmt.Fprintln(o.writer, "oci called") 62 | return nil 63 | } 64 | 65 | func (o *option) SetWriter(writer io.Writer) { 66 | o.writer = writer 67 | } 68 | 69 | func NewCMD(ctx context.Context) *cobra.Command { 70 | o := &option{} 71 | cmd := &cobra.Command{ 72 | Use: "oci", 73 | Short: "OCI Runtime Command Line Interface.", 74 | Long: `OCI Runtime Command Line Interface.`, 75 | RunE: func(cmd *cobra.Command, args []string) error { 76 | return aeCMD.Run(ctx, o, cmd, args) 77 | }, 78 | } 79 | 80 | cmd.AddCommand(create.NewCMD(ctx)) 81 | cmd.AddCommand(delete.NewCMD(ctx)) 82 | cmd.AddCommand(kill.NewCMD(ctx)) 83 | cmd.AddCommand(start.NewCMD(ctx)) 84 | cmd.AddCommand(state.NewCMD(ctx)) 85 | 86 | return cmd 87 | } 88 | -------------------------------------------------------------------------------- /cmd/oci/start/start.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package start 32 | 33 | import ( 34 | "context" 35 | "fmt" 36 | "io" 37 | 38 | aeCMD "github.com/aurae-runtime/ae/cmd" 39 | "github.com/aurae-runtime/ae/pkg/cli" 40 | "github.com/aurae-runtime/ae/pkg/cli/printer" 41 | "github.com/spf13/cobra" 42 | ) 43 | 44 | type option struct { 45 | aeCMD.Option 46 | writer io.Writer 47 | outputFormat *cli.OutputFormat 48 | } 49 | 50 | func (o *option) Complete(_ []string) error { 51 | return nil 52 | } 53 | 54 | func (o *option) Validate() error { 55 | if err := o.outputFormat.Validate(); err != nil { 56 | return err 57 | } 58 | return nil 59 | } 60 | 61 | func (o *option) Execute(_ context.Context) error { 62 | fmt.Fprintln(o.writer, "start called") 63 | return nil 64 | } 65 | 66 | func (o *option) SetWriter(writer io.Writer) { 67 | o.writer = writer 68 | } 69 | 70 | func NewCMD(ctx context.Context) *cobra.Command { 71 | o := &option{ 72 | outputFormat: cli.NewOutputFormat(). 73 | WithPrinter(printer.NewJSON()). 74 | WithPrinter(printer.NewYAML()), 75 | } 76 | cmd := &cobra.Command{ 77 | Use: "start", 78 | Short: "Start the user-specified code from process.", 79 | Long: `Start the user-specified code from process.`, 80 | RunE: func(cmd *cobra.Command, args []string) error { 81 | return aeCMD.Run(ctx, o, cmd, args) 82 | }, 83 | } 84 | o.outputFormat.AddFlags(cmd) 85 | 86 | return cmd 87 | } 88 | -------------------------------------------------------------------------------- /cmd/oci/state/state.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package state 32 | 33 | import ( 34 | "context" 35 | "fmt" 36 | "io" 37 | 38 | aeCMD "github.com/aurae-runtime/ae/cmd" 39 | "github.com/aurae-runtime/ae/pkg/cli" 40 | "github.com/aurae-runtime/ae/pkg/cli/printer" 41 | "github.com/spf13/cobra" 42 | ) 43 | 44 | type option struct { 45 | aeCMD.Option 46 | writer io.Writer 47 | outputFormat *cli.OutputFormat 48 | } 49 | 50 | func (o *option) Complete(_ []string) error { 51 | return nil 52 | } 53 | 54 | func (o *option) Validate() error { 55 | if err := o.outputFormat.Validate(); err != nil { 56 | return err 57 | } 58 | return nil 59 | } 60 | 61 | func (o *option) Execute(_ context.Context) error { 62 | fmt.Fprintln(o.writer, "state called") 63 | return nil 64 | } 65 | 66 | func (o *option) SetWriter(writer io.Writer) { 67 | o.writer = writer 68 | } 69 | 70 | func NewCMD(ctx context.Context) *cobra.Command { 71 | o := &option{ 72 | outputFormat: cli.NewOutputFormat(). 73 | WithPrinter(printer.NewJSON()). 74 | WithPrinter(printer.NewYAML()), 75 | } 76 | cmd := &cobra.Command{ 77 | Use: "state", 78 | Short: "Request the container state.", 79 | Long: `Request the container state.`, 80 | RunE: func(cmd *cobra.Command, args []string) error { 81 | return aeCMD.Run(ctx, o, cmd, args) 82 | }, 83 | } 84 | o.outputFormat.AddFlags(cmd) 85 | 86 | return cmd 87 | } 88 | -------------------------------------------------------------------------------- /cmd/pki/create/create.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package create 32 | 33 | import ( 34 | "context" 35 | "fmt" 36 | "io" 37 | 38 | aeCMD "github.com/aurae-runtime/ae/cmd" 39 | "github.com/aurae-runtime/ae/pkg/cli" 40 | "github.com/aurae-runtime/ae/pkg/cli/printer" 41 | "github.com/aurae-runtime/ae/pkg/pki" 42 | "github.com/spf13/cobra" 43 | ) 44 | 45 | type option struct { 46 | aeCMD.Option 47 | outputFormat *cli.OutputFormat 48 | directory string 49 | domain string 50 | user string 51 | silent bool 52 | writer io.Writer 53 | } 54 | 55 | func (o *option) Complete(args []string) error { 56 | if len(args) == 0 { 57 | return fmt.Errorf("command 'create' requires a domain name as argument") 58 | } 59 | if len(args) > 1 { 60 | return fmt.Errorf("too many arguments for command 'create', expect %d, got %d", 1, len(args)) 61 | } 62 | 63 | o.domain = args[0] 64 | 65 | return nil 66 | } 67 | 68 | func (o *option) Validate() error { 69 | return nil 70 | } 71 | 72 | func (o *option) Execute(_ context.Context) error { 73 | if o.user != "" { 74 | clientCSR, err := pki.CreateClientCSR(o.directory, o.domain, o.user) 75 | if err != nil { 76 | return fmt.Errorf("failed to create client csr: %w", err) 77 | } 78 | if !o.silent { 79 | o.outputFormat.ToPrinter().Print(o.writer, &clientCSR) 80 | } 81 | 82 | return nil 83 | } 84 | 85 | rootCA, err := pki.CreateAuraeRootCA(o.directory, o.domain) 86 | if err != nil { 87 | return fmt.Errorf("failed to create aurae root ca: %w", err) 88 | } 89 | if !o.silent { 90 | o.outputFormat.ToPrinter().Print(o.writer, &rootCA) 91 | } 92 | return nil 93 | } 94 | 95 | func (o *option) SetWriter(writer io.Writer) { 96 | o.writer = writer 97 | } 98 | 99 | func NewCMD(ctx context.Context) *cobra.Command { 100 | o := &option{ 101 | outputFormat: cli.NewOutputFormat(). 102 | WithDefaultFormat(printer.NewJSON().Format()). 103 | WithPrinter(printer.NewJSON()). 104 | WithPrinter(printer.NewYAML()), 105 | silent: false, 106 | } 107 | cmd := &cobra.Command{ 108 | Use: "create", 109 | Short: "Creates a CA for auraed.", 110 | Example: `ae pki create my.domain.com 111 | ae pki create --dir ./pki/ my.domain.com`, 112 | 113 | RunE: func(cmd *cobra.Command, args []string) error { 114 | return aeCMD.Run(ctx, o, cmd, args) 115 | }, 116 | } 117 | 118 | o.outputFormat.AddFlags(cmd) 119 | cmd.Flags().StringVarP(&o.directory, "dir", "d", o.directory, "Output directory to store CA files.") 120 | cmd.Flags().StringVarP(&o.user, "user", "u", o.user, "Creates client certificate for a given user.") 121 | cmd.Flags().BoolVarP(&o.silent, "silent", "s", o.silent, "Silent mode, omits output") 122 | 123 | return cmd 124 | } 125 | -------------------------------------------------------------------------------- /cmd/pki/create/create_test.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package create 32 | 33 | import ( 34 | "bytes" 35 | "context" 36 | "crypto/x509" 37 | "encoding/json" 38 | "encoding/pem" 39 | "testing" 40 | 41 | "github.com/aurae-runtime/ae/pkg/pki" 42 | ) 43 | 44 | func TestPKICreateCMD(t *testing.T) { 45 | t.Run("ae pki create my.domain.com", func(t *testing.T) { 46 | buffer := &bytes.Buffer{} 47 | cmd := NewCMD(context.Background()) 48 | cmd.SetOut(buffer) 49 | cmd.SetErr(buffer) 50 | cmd.SetArgs([]string{"my.domain.com"}) 51 | err := cmd.Execute() 52 | if err != nil { 53 | t.Errorf("ae pki create my.domain.com") 54 | } 55 | 56 | var ca pki.Certificate 57 | err = json.Unmarshal(buffer.Bytes(), &ca) 58 | if err != nil { 59 | t.Errorf("could not marshall certificate") 60 | } 61 | 62 | // load ca.cert 63 | cert, _ := pem.Decode([]byte(ca.Certificate)) 64 | 65 | crt, err := x509.ParseCertificate(cert.Bytes) 66 | if err != nil { 67 | t.Errorf("could not parse certificate") 68 | } 69 | if crt.Subject.CommonName != "my.domain.com" { 70 | t.Errorf("certificate does not contain common name") 71 | } 72 | }) 73 | } 74 | -------------------------------------------------------------------------------- /cmd/pki/pki.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package pki 32 | 33 | import ( 34 | "context" 35 | "io" 36 | 37 | aeCMD "github.com/aurae-runtime/ae/cmd" 38 | pki_create "github.com/aurae-runtime/ae/cmd/pki/create" 39 | "github.com/spf13/cobra" 40 | ) 41 | 42 | type option struct { 43 | aeCMD.Option 44 | writer io.Writer 45 | } 46 | 47 | func (o *option) Complete(args []string) error { 48 | return nil 49 | } 50 | 51 | func (o *option) Validate() error { 52 | return nil 53 | } 54 | 55 | func (o *option) Execute(_ context.Context) error { 56 | return nil 57 | } 58 | 59 | func (o *option) SetWriter(writer io.Writer) { 60 | o.writer = writer 61 | } 62 | 63 | func NewCMD(ctx context.Context) *cobra.Command { 64 | o := &option{} 65 | cmd := &cobra.Command{ 66 | Use: "pki", 67 | Short: "Contains PKI related subcommands.", 68 | 69 | RunE: func(cmd *cobra.Command, args []string) error { 70 | return aeCMD.Run(ctx, o, cmd, args) 71 | }, 72 | } 73 | cmd.AddCommand(pki_create.NewCMD(ctx)) 74 | return cmd 75 | } 76 | -------------------------------------------------------------------------------- /cmd/root/root.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package root_cmd 32 | 33 | import ( 34 | "context" 35 | "os" 36 | 37 | "github.com/aurae-runtime/ae/cmd/discovery" 38 | "github.com/aurae-runtime/ae/cmd/health" 39 | "github.com/aurae-runtime/ae/cmd/observe" 40 | "github.com/aurae-runtime/ae/cmd/oci" 41 | "github.com/aurae-runtime/ae/cmd/pki" 42 | "github.com/aurae-runtime/ae/cmd/version" 43 | "github.com/spf13/cobra" 44 | ) 45 | 46 | var rootCmd = &cobra.Command{ 47 | Use: "ae", 48 | Short: "Unix inspired command line client for Aurae.\n", 49 | Long: `Unix inspired command line client for Aurae.`, 50 | // TODO help by default 51 | // Run: func(cmd *cobra.Command, args []string) { }, 52 | } 53 | 54 | func Execute() { 55 | err := rootCmd.Execute() 56 | if err != nil { 57 | os.Exit(1) 58 | } 59 | } 60 | 61 | func init() { 62 | // add subcommands 63 | ctx := context.Background() 64 | rootCmd.AddCommand(discovery.NewCMD(ctx)) 65 | rootCmd.AddCommand(health.NewCMD(ctx)) 66 | rootCmd.AddCommand(observe.NewCMD(ctx)) 67 | rootCmd.AddCommand(oci.NewCMD(ctx)) 68 | rootCmd.AddCommand(pki.NewCMD(ctx)) 69 | rootCmd.AddCommand(version.NewCMD(ctx)) 70 | } 71 | -------------------------------------------------------------------------------- /cmd/version/version.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package version 32 | 33 | import ( 34 | "context" 35 | "io" 36 | 37 | aeCMD "github.com/aurae-runtime/ae/cmd" 38 | "github.com/aurae-runtime/ae/pkg/cli" 39 | "github.com/aurae-runtime/ae/pkg/cli/printer" 40 | "github.com/prometheus/common/version" 41 | "github.com/spf13/cobra" 42 | ) 43 | 44 | type outputVersion struct { 45 | BuildTime string `json:"buildTime,omitempty" yaml:"buildTime,omitempty"` 46 | Version string `json:"version" yaml:"version"` 47 | Commit string `json:"commit,omitempty" yaml:"commit,omitempty"` 48 | } 49 | 50 | type option struct { 51 | aeCMD.Option 52 | writer io.Writer 53 | short bool 54 | outputFormat *cli.OutputFormat 55 | } 56 | 57 | func (o *option) Complete(_ []string) error { 58 | return nil 59 | } 60 | 61 | func (o *option) Validate() error { 62 | if err := o.outputFormat.Validate(); err != nil { 63 | return err 64 | } 65 | return nil 66 | } 67 | 68 | func (o *option) Execute(_ context.Context) error { 69 | clientVersion := &outputVersion{ 70 | Version: version.Version, 71 | } 72 | if !o.short { 73 | clientVersion.BuildTime = version.BuildDate 74 | clientVersion.Commit = version.Revision 75 | } 76 | 77 | return o.outputFormat.ToPrinter().Print(o.writer, clientVersion) 78 | } 79 | 80 | func (o *option) SetWriter(writer io.Writer) { 81 | o.writer = writer 82 | } 83 | 84 | func NewCMD(ctx context.Context) *cobra.Command { 85 | o := &option{ 86 | outputFormat: cli.NewOutputFormat(). 87 | WithDefaultFormat(printer.NewJSON().Format()). 88 | WithPrinter(printer.NewJSON()). 89 | WithPrinter(printer.NewYAML()), 90 | } 91 | cmd := &cobra.Command{ 92 | Use: "version", 93 | Short: "Displays Aurae command line client version.", 94 | RunE: func(cmd *cobra.Command, args []string) error { 95 | return aeCMD.Run(ctx, o, cmd, args) 96 | }, 97 | } 98 | o.outputFormat.AddFlags(cmd) 99 | cmd.Flags().BoolVar(&o.short, "short", o.short, "If true, just print the version number.") 100 | return cmd 101 | } 102 | -------------------------------------------------------------------------------- /cmd/version/version_test.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package version 32 | 33 | import ( 34 | "context" 35 | "testing" 36 | 37 | "github.com/aurae-runtime/ae/pkg/cli/testsuite" 38 | "github.com/prometheus/common/version" 39 | ) 40 | 41 | func TestVersionCMD(t *testing.T) { 42 | version.Version = "v0.1.0" 43 | version.BuildDate = "2023-01-07" 44 | version.Revision = "a7c46aa017bc447ece506629196bd0548cbbc469" 45 | tests := []testsuite.Test{ 46 | { 47 | Title: "empty args", 48 | Cmd: NewCMD(context.Background()), 49 | Args: []string{}, 50 | ExpectedStdout: "{\n" + 51 | " \"buildTime\": \"2023-01-07\",\n" + 52 | " \"version\": \"v0.1.0\",\n" + 53 | " \"commit\": \"a7c46aa017bc447ece506629196bd0548cbbc469\"\n" + 54 | "}\n", 55 | }, 56 | { 57 | Title: "print version in json", 58 | Cmd: NewCMD(context.Background()), 59 | Args: []string{"--output", "json"}, 60 | ExpectedStdout: "{\n" + 61 | " \"buildTime\": \"2023-01-07\",\n" + 62 | " \"version\": \"v0.1.0\",\n" + 63 | " \"commit\": \"a7c46aa017bc447ece506629196bd0548cbbc469\"\n" + 64 | "}\n", 65 | }, 66 | { 67 | Title: "print short version", 68 | Cmd: NewCMD(context.Background()), 69 | Args: []string{"--short"}, 70 | ExpectedStdout: "{\n" + 71 | " \"version\": \"v0.1.0\"\n" + 72 | "}\n", 73 | }, 74 | } 75 | testsuite.ExecuteSuite(t, tests) 76 | } 77 | -------------------------------------------------------------------------------- /compile.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package main 32 | 33 | var ( 34 | Name string 35 | Version string 36 | Copyright string 37 | License string 38 | AuthorName string 39 | AuthorEmail string 40 | ) 41 | -------------------------------------------------------------------------------- /docs/aurae_pki.md: -------------------------------------------------------------------------------- 1 | # Creating an Aurae PKI 2 | 3 | ## Usage 4 | 5 | **Create a Root CA** 6 | 7 | Get a new CA certificate and key pair. This is the root of trust for all other certificates. The output is a json string. 8 | 9 | ```bash 10 | $ ./bin/ae pki create unsafe.aurae.io 11 | { 12 | "cert": "", 13 | "key": "" 14 | } 15 | ``` 16 | 17 | With the flag `-d` corresponding files can be created: 18 | 19 | ```bash 20 | $ ./bin/ae pki create unsafe.aurae.io -d ./pki/ 21 | ``` 22 | 23 | Which is the equivalent of 24 | 25 | ```bash 26 | $ openssl req \ 27 | -new \ 28 | -x509 \ 29 | -nodes \ 30 | -days 9999 \ 31 | -addext "subjectAltName = DNS:unsafe.aurae.io" \ 32 | -subj "/C=IS/ST=aurae/L=aurae/O=Aurae/OU=Runtime/CN=unsafe.aurae.io" \ 33 | -keyout "./pki/ca.key" \ 34 | -out "./pki/ca.crt" 2>/dev/null 35 | ``` 36 | 37 | **Create CSR** 38 | 39 | Get a new certificate signing request and key pair. The output is a json string. 40 | 41 | ``` 42 | $ ./bin/ae pki create unsafe.aurae.io --user christoph 43 | { 44 | "csr": "", 45 | "key": "", 46 | "user": "christoph" 47 | } 48 | ``` 49 | 50 | With the flag `-d` corresponding files can be created: 51 | 52 | ```bash 53 | $ ./bin/ae pki create unsafe.aurae.io --user christoph -d ./pki/ 54 | ``` 55 | 56 | Which is the equivalent of 57 | 58 | ``` 59 | $ openssl genrsa -out "./pki/client.${NAME}.key" 4096 2>/dev/null 60 | $ openssl req \ 61 | -new \ 62 | -addext "subjectAltName = DNS:${NAME}.unsafe.aurae.io" \ 63 | -subj "/C=IS/ST=aurae/L=aurae/O=Aurae/OU=Runtime/CN=${NAME}.unsafe.aurae.io" \ 64 | -key "./pki/client.${NAME}.key" \ 65 | -out "./pki/client.${NAME}.csr" 2>/dev/null 66 | ``` 67 | 68 | **Create client certificate** 69 | 70 | *to be implemented* 71 | 72 | 87 | 88 | 99 | 100 | 107 | -------------------------------------------------------------------------------- /docs/subcommands.md: -------------------------------------------------------------------------------- 1 | # Subcommands 2 | 3 | The purpose of this document is to describe the handling of subcommands within `ae`. 4 | 5 | ## Create a new subcommand 6 | 7 | All subcommands reside in a dedicated package within `./cmd/`. Subcommands of subcommands are stored within their parents package. 8 | 9 | Example: `ae oci` is a 1st level subcommand and therefore stored in `./cmd/oci/`. `ae oci create` is a 2nd level subcommand of the `oci` subcommand and therefore stored in `./cmd/oci/create`. This structure is also reflected in its package name `òci_create`. 10 | 11 | ``` 12 | ./cmd 13 | ├── cmd.go 14 | ├── oci 15 | │ ├── create 16 | │ │ └── create.go 17 | │ ├── delete 18 | │ │ └── delete.go 19 | │ ├── kill 20 | │ │ └── kill.go 21 | │ ├── oci.go 22 | │ ├── start 23 | │ │ └── start.go 24 | │ └── state 25 | │ └── state.go 26 | ├── root 27 | │ └── root.go 28 | ├── runtime 29 | │ ├── allocate 30 | │ │ └── allocate.go 31 | │ ├── free 32 | │ │ └── free.go 33 | │ ├── start 34 | │ │ └── start.go 35 | │ └── stop 36 | │ └── stop.go 37 | └── version 38 | └── version.go 39 | ``` 40 | 41 | To create a new command, proceed with the following steps: 42 | 43 | Create a new directory for the new command: 44 | 45 | ``` 46 | $ mkdir ./cmd/new_command 47 | ``` 48 | 49 | Create a new go package within this directory: 50 | 51 | ``` 52 | $ echo "package new_command" > ./cmd/new_command/new_command.go 53 | ``` 54 | 55 | ``` 56 | ./cmd 57 | ├── cmd.go 58 | ├── new_command 59 | │ └── new_command.go 60 | ├── oci 61 | ├── root 62 | └── version 63 | ``` 64 | 65 | ## Structure of a subcommand 66 | 67 | Every command follows the interface defined in `./cmd/cmd.go`. 68 | 69 | **Option struct** 70 | 71 | The option struct is supposed to contain the command's input and holds any relevant information necessary to process the command. 72 | 73 | ```go 74 | type option struct {} 75 | ``` 76 | 77 | **Complete** 78 | 79 | `Complete()` is the method where the option extracts the data from the arguments and sets its different attributes. For example, assuming your subcommand requires a TCP port as a parameter, you should check for its availability within `Complete()` and make it available within `option`. 80 | 81 | `Complete()` is triggered before `Validate()` and `Execute()`. 82 | 83 | ```go 84 | func (o *option) Complete(_ []string) error {} 85 | ``` 86 | 87 | **Validate** 88 | 89 | `Validate()` ensures that the commands attribute values are coherent (when it makes sense). For example, assuming your subcommand requires a TCP port as a parameter, you should check for the validity of the parameter within `Validate()`. 90 | 91 | Validate is triggered after `Complete()` and before `Execute()` 92 | 93 | ```go 94 | func (o *option) Validate() error {} 95 | ``` 96 | 97 | **Execute** 98 | 99 | Entry point to the subcommand action. 100 | 101 | ```go 102 | func (o *option) Execute() error {} 103 | ``` 104 | 105 | **SetWriter** 106 | 107 | Defines an `io.Writer` to output text. `io.Writer` is stored in `option` struct. 108 | 109 | ```go 110 | func (o *option) SetWriter(writer io.Writer) {} 111 | ``` 112 | 113 | **NewCMD** 114 | 115 | `NewCMD()` constructs a new `cobra.Command`. Furthermore, this is where command-specific flags should be defined, or further subcommands can be registered to the command. 116 | 117 | ```go 118 | func NewCMD() *cobra.Command {} 119 | ``` 120 | 121 | ## Registering the new command to the root command 122 | 123 | A new subcommand can be added to the root command by adding it to the `init()` function of root command in `./cmd/root/root.go` via `rootCmd.AddCommand(new_command.NewCMD())`. 124 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/aurae-runtime/ae 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/3th1nk/cidr v0.2.0 7 | github.com/prometheus/common v0.55.0 8 | github.com/spf13/cobra v1.8.1 9 | google.golang.org/grpc v1.64.1 10 | google.golang.org/protobuf v1.34.2 11 | gopkg.in/yaml.v2 v2.4.0 12 | ) 13 | 14 | require ( 15 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 16 | github.com/kr/pretty v0.3.1 // indirect 17 | github.com/spf13/pflag v1.0.5 // indirect 18 | golang.org/x/net v0.26.0 // indirect 19 | golang.org/x/sys v0.21.0 // indirect 20 | golang.org/x/text v0.16.0 // indirect 21 | google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect 22 | ) 23 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/3th1nk/cidr v0.2.0 h1:81jjEknszD8SHPLVTPPk+BZjNVqq1ND2YXLSChl6Lrs= 2 | github.com/3th1nk/cidr v0.2.0/go.mod h1:XsSQnS4rEYyB2veDfnIGgViulFpIITPKtp3f0VxpiLw= 3 | github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 4 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 8 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 9 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 10 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 11 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 12 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 13 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 14 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 15 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 16 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 17 | github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= 18 | github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= 19 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 20 | github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= 21 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 22 | github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= 23 | github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= 24 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 25 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 26 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 27 | github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= 28 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 29 | golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= 30 | golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= 31 | golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= 32 | golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 33 | golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= 34 | golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 35 | google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c= 36 | google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= 37 | google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= 38 | google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= 39 | google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= 40 | google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 41 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 42 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 43 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 44 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 45 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 46 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 47 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package main 32 | 33 | import ( 34 | cmd "github.com/aurae-runtime/ae/cmd/root" 35 | ) 36 | 37 | func main() { 38 | cmd.Execute() 39 | } 40 | -------------------------------------------------------------------------------- /pkg/cli/output_format.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/aurae-runtime/ae/pkg/cli/printer" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var ErrUnknownFormat = errors.New("unknown output format") 13 | 14 | type OutputFormat struct { 15 | format string 16 | printers []printer.Interface 17 | } 18 | 19 | func NewOutputFormat() *OutputFormat { 20 | return &OutputFormat{ 21 | "", 22 | nil, 23 | } 24 | } 25 | 26 | func (o *OutputFormat) WithDefaultFormat(defaultFormat string) *OutputFormat { 27 | o.format = defaultFormat 28 | return o 29 | } 30 | 31 | func (o *OutputFormat) WithPrinter(p printer.Interface) *OutputFormat { 32 | o.printers = append(o.printers, p) 33 | return o 34 | } 35 | 36 | func (o *OutputFormat) AddFlags(cmd *cobra.Command) { 37 | if len(o.printers) == 0 { 38 | return 39 | } 40 | 41 | cmd.Flags().StringVarP( 42 | &o.format, 43 | "output", 44 | "o", 45 | o.format, 46 | fmt.Sprintf("Output format. One of: (%s).", strings.Join(o.allowedFormats(), ", ")), 47 | ) 48 | } 49 | 50 | func (o *OutputFormat) Validate() error { 51 | if o.format == "" { 52 | return nil 53 | } 54 | 55 | for _, printer := range o.printers { 56 | if o.format == printer.Format() { 57 | return nil 58 | } 59 | } 60 | 61 | return fmt.Errorf("%q: %w", o.format, ErrUnknownFormat) 62 | } 63 | 64 | func (o *OutputFormat) ToPrinter() printer.Interface { 65 | for _, printer := range o.printers { 66 | if o.format == printer.Format() { 67 | return printer 68 | } 69 | } 70 | return nil 71 | } 72 | 73 | func (o *OutputFormat) allowedFormats() []string { 74 | var allowed []string 75 | for _, printer := range o.printers { 76 | allowed = append(allowed, printer.Format()) 77 | } 78 | return allowed 79 | } 80 | -------------------------------------------------------------------------------- /pkg/cli/output_format_test.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "errors" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/aurae-runtime/ae/pkg/cli/printer" 9 | "github.com/aurae-runtime/ae/pkg/cli/testsuite" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | func newCmd(printers []printer.Interface) *cobra.Command { 14 | cmd := &cobra.Command{} 15 | output := NewOutputFormat() 16 | for _, p := range printers { 17 | output.WithPrinter(p) 18 | } 19 | output.AddFlags(cmd) 20 | return cmd 21 | } 22 | 23 | func TestOutputFormatAddFlags(t *testing.T) { 24 | tests := []testsuite.Test{ 25 | { 26 | Title: "'output' is valid flag", 27 | Cmd: newCmd([]printer.Interface{printer.NewJSON()}), 28 | Args: []string{"--output", "json"}, 29 | ExpectedStdout: "", 30 | }, 31 | { 32 | Title: "'o' is valid flag", 33 | Cmd: newCmd([]printer.Interface{printer.NewJSON()}), 34 | Args: []string{"-o", "json"}, 35 | ExpectedStdout: "", 36 | }, 37 | } 38 | 39 | testsuite.ExecuteSuite(t, tests) 40 | } 41 | 42 | func TestOutputFormatValidate(t *testing.T) { 43 | tests := []struct { 44 | format string 45 | printers []printer.Interface 46 | expectedErr error 47 | }{ 48 | { 49 | format: "json", 50 | printers: []printer.Interface{printer.NewJSON(), printer.NewYAML()}, 51 | expectedErr: nil, 52 | }, 53 | { 54 | format: "yaml", 55 | printers: []printer.Interface{printer.NewJSON(), printer.NewYAML()}, 56 | expectedErr: nil, 57 | }, 58 | { 59 | format: "json", 60 | printers: []printer.Interface{printer.NewYAML()}, 61 | expectedErr: errors.New("\"json\": unknown output format"), 62 | }, 63 | } 64 | for _, test := range tests { 65 | output := NewOutputFormat() 66 | for _, p := range test.printers { 67 | output.WithPrinter(p) 68 | } 69 | output.format = test.format 70 | err := output.Validate() 71 | if test.expectedErr == nil { 72 | if err != nil { 73 | t.Errorf("expected: nil; got: %v", err) 74 | } 75 | } else if test.expectedErr.Error() != err.Error() { 76 | t.Errorf("expected: %v; got: %v", test.expectedErr, err) 77 | } 78 | } 79 | } 80 | func TestOutputFormatToPrinter(t *testing.T) { 81 | tests := []struct { 82 | format string 83 | printers []printer.Interface 84 | expectedType reflect.Type 85 | }{ 86 | { 87 | format: "json", 88 | printers: []printer.Interface{printer.NewJSON(), printer.NewYAML()}, 89 | expectedType: reflect.TypeOf(printer.JSON{}), 90 | }, 91 | { 92 | format: "yaml", 93 | printers: []printer.Interface{printer.NewJSON(), printer.NewYAML()}, 94 | expectedType: reflect.TypeOf(printer.YAML{}), 95 | }, 96 | } 97 | for _, test := range tests { 98 | output := NewOutputFormat() 99 | for _, p := range test.printers { 100 | output.WithPrinter(p) 101 | } 102 | output.format = test.format 103 | printerType := reflect.Indirect(reflect.ValueOf(output.ToPrinter())).Type() 104 | if test.expectedType != printerType { 105 | t.Errorf("expected: %v; got: %v", test.expectedType, printerType) 106 | } 107 | } 108 | } 109 | 110 | func TestOutputFormatFlagHelp(t *testing.T) { 111 | tests := []testsuite.Test{ 112 | { 113 | Title: "Help is formatted correctly", 114 | Cmd: newCmd([]printer.Interface{printer.NewJSON(), printer.NewYAML()}), 115 | Args: []string{"help"}, 116 | ExpectedStdout: "", 117 | }, 118 | } 119 | 120 | testsuite.ExecuteSuite(t, tests) 121 | } 122 | -------------------------------------------------------------------------------- /pkg/cli/printer/interface.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | type Interface interface { 8 | Format() string 9 | Print(w io.Writer, obj any) error 10 | } 11 | -------------------------------------------------------------------------------- /pkg/cli/printer/json.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | ) 7 | 8 | var _ Interface = NewJSON() 9 | 10 | type JSON struct { 11 | } 12 | 13 | func NewJSON() *JSON { 14 | return &JSON{} 15 | } 16 | 17 | func (printer *JSON) Format() string { 18 | return "json" 19 | } 20 | 21 | func (printer *JSON) Print(w io.Writer, obj any) error { 22 | data, err := json.MarshalIndent(obj, "", " ") 23 | if err != nil { 24 | return err 25 | } 26 | 27 | data = append(data, '\n') 28 | _, err = w.Write(data) 29 | return err 30 | } 31 | -------------------------------------------------------------------------------- /pkg/cli/printer/yaml.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "gopkg.in/yaml.v2" 8 | ) 9 | 10 | var _ Interface = NewYAML() 11 | 12 | type YAML struct { 13 | } 14 | 15 | func NewYAML() *YAML { 16 | return &YAML{} 17 | } 18 | 19 | func (printer *YAML) Format() string { 20 | return "yaml" 21 | } 22 | 23 | func (printer *YAML) Print(w io.Writer, obj any) error { 24 | output, err := yaml.Marshal(obj) 25 | if err != nil { 26 | return err 27 | } 28 | _, err = fmt.Fprint(w, string(output)) 29 | return err 30 | } 31 | -------------------------------------------------------------------------------- /pkg/cli/testsuite/suite.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package testsuite 32 | 33 | import ( 34 | "bytes" 35 | "testing" 36 | 37 | "github.com/spf13/cobra" 38 | ) 39 | 40 | type Test struct { 41 | Title string 42 | Cmd *cobra.Command 43 | Args []string 44 | ExpectedStdout string 45 | } 46 | 47 | func ExecuteSuite(t *testing.T, tests []Test) { 48 | for _, test := range tests { 49 | t.Run(test.Title, func(t *testing.T) { 50 | buffer := &bytes.Buffer{} 51 | test.Cmd.SetOut(buffer) 52 | test.Cmd.SetArgs(test.Args) 53 | 54 | err := test.Cmd.Execute() 55 | 56 | if !IsNil(err) { 57 | t.Errorf("Unexpected error while executing command: %v", err) 58 | } 59 | if !IsEqualString(test.ExpectedStdout, buffer.String()) { 60 | t.Errorf("Unexpected Stdout\ngot:\n%s\nwant:\n%s\n", test.ExpectedStdout, buffer.String()) 61 | } 62 | }) 63 | } 64 | } 65 | 66 | func IsEqualString(s1, s2 string) bool { 67 | return s1 == s2 68 | } 69 | 70 | func IsNil(object any) bool { 71 | return object == nil 72 | } 73 | -------------------------------------------------------------------------------- /pkg/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "crypto/tls" 6 | "crypto/x509" 7 | "fmt" 8 | "net" 9 | "os" 10 | 11 | "google.golang.org/grpc" 12 | "google.golang.org/grpc/credentials" 13 | 14 | "github.com/aurae-runtime/ae/pkg/config" 15 | "github.com/aurae-runtime/ae/pkg/discovery" 16 | "github.com/aurae-runtime/ae/pkg/health" 17 | "github.com/aurae-runtime/ae/pkg/observe" 18 | ) 19 | 20 | type Client interface { 21 | Discovery() (discovery.Discovery, error) 22 | Health() (health.Health, error) 23 | Observe() (observe.Observe, error) 24 | } 25 | 26 | type client struct { 27 | cfg *config.Configs 28 | conn grpc.ClientConnInterface 29 | discovery discovery.Discovery 30 | health health.Health 31 | observe observe.Observe 32 | } 33 | 34 | func New(ctx context.Context, cfg ...config.Config) (Client, error) { 35 | cf, err := config.From(cfg...) 36 | if err != nil { 37 | return nil, fmt.Errorf("Failed to initialize client config: %s", err) 38 | } 39 | 40 | tlsCreds, err := loadTLSCredentials(cf.Auth) 41 | if err != nil { 42 | return nil, fmt.Errorf("Failed to load TLS credentials: %s", err) 43 | } 44 | 45 | dialer := func(ctx context.Context, addr string) (net.Conn, error) { 46 | d := net.Dialer{} 47 | return d.DialContext(ctx, cf.System.Protocol, addr) 48 | } 49 | 50 | conn, err := grpc.NewClient(cf.System.Socket, grpc.WithTransportCredentials(tlsCreds), grpc.WithContextDialer(dialer)) 51 | if err != nil { 52 | return nil, fmt.Errorf("Failed to dial server: %s", err) 53 | } 54 | 55 | return &client{ 56 | cfg: cf, 57 | conn: conn, 58 | discovery: discovery.New(ctx, conn), 59 | health: health.New(ctx, conn), 60 | observe: observe.New(ctx, conn), 61 | }, nil 62 | } 63 | 64 | func loadTLSCredentials(auth config.Auth) (credentials.TransportCredentials, error) { 65 | caPEM, err := os.ReadFile(auth.CaCert) 66 | if err != nil { 67 | return nil, err 68 | } 69 | 70 | certPool := x509.NewCertPool() 71 | if !certPool.AppendCertsFromPEM(caPEM) { 72 | return nil, fmt.Errorf("Failed to add server CA's certificate") 73 | } 74 | 75 | clientKeyPair, err := tls.LoadX509KeyPair(auth.ClientCert, auth.ClientKey) 76 | if err != nil { 77 | return nil, err 78 | } 79 | 80 | config := &tls.Config{ 81 | Certificates: []tls.Certificate{clientKeyPair}, 82 | RootCAs: certPool, 83 | ServerName: auth.ServerName, 84 | } 85 | 86 | return credentials.NewTLS(config), nil 87 | } 88 | 89 | func (c *client) Discovery() (discovery.Discovery, error) { 90 | if c.discovery == nil { 91 | return nil, fmt.Errorf("Discovery service is not available") 92 | } 93 | return c.discovery, nil 94 | } 95 | 96 | func (c *client) Health() (health.Health, error) { 97 | if c.health == nil { 98 | return nil, fmt.Errorf("Health service is not available") 99 | } 100 | return c.health, nil 101 | } 102 | 103 | func (c *client) Observe() (observe.Observe, error) { 104 | if c.observe == nil { 105 | return nil, fmt.Errorf("Observe service is not available") 106 | } 107 | return c.observe, nil 108 | } 109 | -------------------------------------------------------------------------------- /pkg/config/auth.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "github.com/spf13/cobra" 4 | 5 | type Auth struct { 6 | CaCert string 7 | ClientCert string 8 | ClientKey string 9 | ServerName string 10 | } 11 | 12 | func (a Auth) Set(cfg *Configs) error { 13 | cfg.Auth = a 14 | return nil 15 | } 16 | 17 | func WithAuth(auth Auth) Config { 18 | return auth 19 | } 20 | 21 | func (a *Auth) AddFlags(cmd *cobra.Command) { 22 | cmd.Flags().StringVar(&a.CaCert, "ca_crt", "/etc/aurae/ca.crt", "The CA certificate") 23 | cmd.Flags().StringVar(&a.ClientCert, "client_crt", "/etc/aurae/_signed.client.crt", "The client certificate") 24 | cmd.Flags().StringVar(&a.ClientKey, "client_key", "/etc/aurae/client.key", "The client certificate key") 25 | } 26 | -------------------------------------------------------------------------------- /pkg/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "os/user" 6 | ) 7 | 8 | type Configs struct { 9 | Auth Auth 10 | System System 11 | } 12 | 13 | type Config interface { 14 | Set(p *Configs) error 15 | } 16 | 17 | func Default() (*Configs, error) { 18 | usr, err := user.Current() 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | return &Configs{ 24 | Auth: Auth{ 25 | CaCert: fmt.Sprintf("%s/.aurae/pki/ca.crt", usr.HomeDir), 26 | ClientCert: fmt.Sprintf("%s/.aurae/pki/_signed.client.nova.crt", usr.HomeDir), 27 | ClientKey: fmt.Sprintf("%s/.aurae/pki/client.nova.key", usr.HomeDir), 28 | ServerName: "server.unsafe.aurae.io", 29 | }, 30 | System: System{ 31 | Protocol: "unix", 32 | Socket: "/var/run/aurae/aurae.sock", 33 | }, 34 | }, nil 35 | } 36 | 37 | func From(cfg ...Config) (*Configs, error) { 38 | c, err := Default() 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | for _, config := range cfg { 44 | err := config.Set(c) 45 | 46 | if err != nil { 47 | return nil, err 48 | } 49 | } 50 | 51 | return c, nil 52 | } 53 | -------------------------------------------------------------------------------- /pkg/config/system.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type System struct { 4 | Protocol string 5 | Socket string 6 | } 7 | 8 | func (s System) Set(cfg *Configs) error { 9 | cfg.System = s 10 | return nil 11 | } 12 | 13 | func WithSystem(system System) Config { 14 | return system 15 | } 16 | -------------------------------------------------------------------------------- /pkg/discovery/discovery.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "context" 5 | 6 | "google.golang.org/grpc" 7 | 8 | discoveryv0 "github.com/aurae-runtime/ae/pkg/api/v0/discovery" 9 | ) 10 | 11 | type Discovery interface { 12 | Discover(context.Context, *discoveryv0.DiscoverRequest) (*discoveryv0.DiscoverResponse, error) 13 | } 14 | 15 | type discovery struct { 16 | client discoveryv0.DiscoveryServiceClient 17 | } 18 | 19 | func New(ctx context.Context, conn grpc.ClientConnInterface) Discovery { 20 | return &discovery{ 21 | client: discoveryv0.NewDiscoveryServiceClient(conn), 22 | } 23 | } 24 | 25 | func (d *discovery) Discover(ctx context.Context, req *discoveryv0.DiscoverRequest) (*discoveryv0.DiscoverResponse, error) { 26 | return d.client.Discover(ctx, req) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/health/health.go: -------------------------------------------------------------------------------- 1 | package health 2 | 3 | import ( 4 | "context" 5 | 6 | "google.golang.org/grpc" 7 | 8 | //healthv1 "github.com/aurae-runtime/ae/pkg/api/grpc/health/v1/health" 9 | healthv1 "google.golang.org/grpc/health/grpc_health_v1" 10 | ) 11 | 12 | type Health interface { 13 | Check(context.Context, *healthv1.HealthCheckRequest) (*healthv1.HealthCheckResponse, error) 14 | } 15 | 16 | type health struct { 17 | client healthv1.HealthClient 18 | } 19 | 20 | func New(ctx context.Context, conn grpc.ClientConnInterface) Health { 21 | return &health{ 22 | client: healthv1.NewHealthClient(conn), 23 | } 24 | } 25 | 26 | func (h *health) Check(ctx context.Context, req *healthv1.HealthCheckRequest) (*healthv1.HealthCheckResponse, error) { 27 | return h.client.Check(ctx, req) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/observe/observe.go: -------------------------------------------------------------------------------- 1 | package observe 2 | 3 | import ( 4 | "context" 5 | 6 | "google.golang.org/grpc" 7 | 8 | observev0 "github.com/aurae-runtime/ae/pkg/api/v0/observe" 9 | ) 10 | 11 | type Observe interface { 12 | GetAuraeDaemonLogStream(context.Context, *observev0.GetAuraeDaemonLogStreamRequest) (observev0.ObserveService_GetAuraeDaemonLogStreamClient, error) 13 | GetSubProcessStream(context.Context, *observev0.GetSubProcessStreamRequest) (observev0.ObserveService_GetSubProcessStreamClient, error) 14 | } 15 | 16 | type observe struct { 17 | client observev0.ObserveServiceClient 18 | } 19 | 20 | func New(ctx context.Context, conn grpc.ClientConnInterface) Observe { 21 | return &observe{ 22 | client: observev0.NewObserveServiceClient(conn), 23 | } 24 | } 25 | 26 | func (o *observe) GetAuraeDaemonLogStream(ctx context.Context, req *observev0.GetAuraeDaemonLogStreamRequest) (observev0.ObserveService_GetAuraeDaemonLogStreamClient, error) { 27 | return o.client.GetAuraeDaemonLogStream(ctx, req) 28 | } 29 | 30 | func (o *observe) GetSubProcessStream(ctx context.Context, req *observev0.GetSubProcessStreamRequest) (observev0.ObserveService_GetSubProcessStreamClient, error) { 31 | return o.client.GetSubProcessStream(ctx, req) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/pki/pki.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package pki 32 | 33 | import ( 34 | "crypto/rand" 35 | "crypto/rsa" 36 | "crypto/sha1" 37 | "crypto/x509" 38 | "crypto/x509/pkix" 39 | "encoding/pem" 40 | "fmt" 41 | "math/big" 42 | "os" 43 | "path/filepath" 44 | "time" 45 | ) 46 | 47 | type Certificate struct { 48 | Certificate string `json:"cert" yaml:"cert"` 49 | PrivateKey string `json:"key" yaml:"key"` 50 | } 51 | 52 | type CertificateRequest struct { 53 | CSR string `json:"csr" yaml:"csr"` 54 | PrivateKey string `json:"key" yaml:"key"` 55 | User string `json:"user" yaml:"user"` 56 | } 57 | 58 | func CreateAuraeRootCA(path string, domainName string) (*Certificate, error) { 59 | priv, err := rsa.GenerateKey(rand.Reader, 2048) 60 | if err != nil { 61 | return &Certificate{}, fmt.Errorf("failed to generate private key: %w", err) 62 | } 63 | 64 | subj := pkix.Name{ 65 | Organization: []string{"Aurae"}, 66 | OrganizationalUnit: []string{"Runtime"}, 67 | Province: []string{"aurae"}, 68 | Locality: []string{"aurae"}, 69 | Country: []string{"IS"}, 70 | CommonName: domainName, 71 | } 72 | 73 | now := time.Now() 74 | 75 | serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) 76 | serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) 77 | if err != nil { 78 | return &Certificate{}, fmt.Errorf("failed to generate serial number: %w", err) 79 | } 80 | 81 | template := x509.Certificate{ 82 | Subject: subj, 83 | NotBefore: now, 84 | NotAfter: now.Add(24 * time.Hour * 9999), 85 | IsCA: true, 86 | BasicConstraintsValid: true, 87 | DNSNames: []string{domainName}, 88 | SerialNumber: serialNumber, 89 | } 90 | 91 | // To get an AuthorityKeyId which is equal to SubjectKeyId, we are manually 92 | // setting it according to the rules of x509.go. 93 | // 94 | // From X509 docs: 95 | // > The AuthorityKeyId will be taken from the SubjectKeyId of parent, if any, 96 | // > unless the resulting certificate is self-signed. Otherwise the value from 97 | // > template will be used. 98 | // 99 | // > If SubjectKeyId from template is empty and the template is a CA, SubjectKeyId 100 | // > will be generated from the hash of the public key. 101 | // 102 | // We need a hash of the publickey, so hopefully this link is right 103 | // https://stackoverflow.com/questions/52502511/how-to-generate-bytes-array-from-publickey#comment92269419_52502639 104 | pubHash := sha1.Sum(priv.PublicKey.N.Bytes()) 105 | template.SubjectKeyId = pubHash[:] 106 | template.AuthorityKeyId = template.SubjectKeyId 107 | 108 | crtBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) 109 | if err != nil { 110 | return &Certificate{}, fmt.Errorf("failed to create certificate: %w", err) 111 | } 112 | 113 | crtPem := pem.EncodeToMemory(&pem.Block{ 114 | Type: "CERTIFICATE", 115 | Bytes: crtBytes, 116 | }) 117 | 118 | keyPem := pem.EncodeToMemory(&pem.Block{ 119 | Type: "RSA PRIVATE KEY", 120 | Bytes: x509.MarshalPKCS1PrivateKey(priv), 121 | }) 122 | 123 | ca := &Certificate{ 124 | Certificate: string(crtPem), 125 | PrivateKey: string(keyPem), 126 | } 127 | 128 | if path != "" { 129 | err = createCAFiles(path, ca) 130 | if err != nil { 131 | return ca, err 132 | } 133 | } 134 | 135 | return ca, nil 136 | } 137 | 138 | func CreateClientCSR(path, domain, user string) (*CertificateRequest, error) { 139 | priv, err := rsa.GenerateKey(rand.Reader, 4096) 140 | if err != nil { 141 | return &CertificateRequest{}, fmt.Errorf("failed to generate private key: %w", err) 142 | } 143 | 144 | subj := pkix.Name{ 145 | Organization: []string{"Aurae"}, 146 | OrganizationalUnit: []string{"Runtime"}, 147 | Province: []string{"aurae"}, 148 | Locality: []string{"aurae"}, 149 | Country: []string{"IS"}, 150 | CommonName: fmt.Sprintf("%s.%s", user, domain), 151 | } 152 | 153 | template := x509.CertificateRequest{ 154 | Subject: subj, 155 | SignatureAlgorithm: x509.SHA256WithRSA, 156 | DNSNames: []string{fmt.Sprintf("%s.%s", user, domain)}, 157 | } 158 | 159 | csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, priv) 160 | if err != nil { 161 | return &CertificateRequest{}, fmt.Errorf("could not create certificate request: %w", err) 162 | } 163 | 164 | csrPem := pem.EncodeToMemory(&pem.Block{ 165 | Type: "CERTIFICATE REQUEST", 166 | Bytes: csrBytes, 167 | }) 168 | 169 | keyPem := pem.EncodeToMemory(&pem.Block{ 170 | Type: "RSA PRIVATE KEY", 171 | Bytes: x509.MarshalPKCS1PrivateKey(priv), 172 | }) 173 | 174 | csr := &CertificateRequest{ 175 | CSR: string(csrPem), 176 | PrivateKey: string(keyPem), 177 | User: user, 178 | } 179 | 180 | if path != "" { 181 | err = createCsrFiles(path, csr) 182 | if err != nil { 183 | return csr, err 184 | } 185 | } 186 | 187 | return csr, nil 188 | } 189 | 190 | func createCAFiles(path string, ca *Certificate) error { 191 | path = filepath.Clean(path) 192 | err := os.MkdirAll(path, os.ModePerm) 193 | if err != nil { 194 | return fmt.Errorf("failed to create output directory: %w", err) 195 | } 196 | 197 | crtPath := filepath.Join(path, "ca.crt") 198 | keyPath := filepath.Join(path, "ca.key") 199 | 200 | err = writeStringToFile(crtPath, ca.Certificate) 201 | if err != nil { 202 | return err 203 | } 204 | 205 | err = writeStringToFile(keyPath, ca.PrivateKey) 206 | if err != nil { 207 | return err 208 | } 209 | return nil 210 | } 211 | 212 | func createCsrFiles(path string, ca *CertificateRequest) error { 213 | path = filepath.Clean(path) 214 | err := os.MkdirAll(path, os.ModePerm) 215 | if err != nil { 216 | return fmt.Errorf("failed to create output directory: %w", err) 217 | } 218 | 219 | csrPath := filepath.Join(path, fmt.Sprintf("client.%s.csr", ca.User)) 220 | keyPath := filepath.Join(path, fmt.Sprintf("client.%s.key", ca.User)) 221 | 222 | err = writeStringToFile(csrPath, ca.CSR) 223 | if err != nil { 224 | return err 225 | } 226 | 227 | err = writeStringToFile(keyPath, ca.PrivateKey) 228 | if err != nil { 229 | return err 230 | } 231 | return nil 232 | } 233 | 234 | func writeStringToFile(p string, s string) error { 235 | f, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) 236 | if err != nil { 237 | return fmt.Errorf("failed to open file %s: %w", p, err) 238 | } 239 | defer f.Close() 240 | 241 | _, err = f.WriteString(s) 242 | if err != nil { 243 | return fmt.Errorf("failed to write file %s: %w", p, err) 244 | } 245 | 246 | return nil 247 | } 248 | -------------------------------------------------------------------------------- /pkg/pki/pki_test.go: -------------------------------------------------------------------------------- 1 | /* -------------------------------------------------------------------------- *\ 2 | * Apache 2.0 License Copyright © 2022 The Aurae Authors * 3 | * * 4 | * +--------------------------------------------+ * 5 | * | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | * 6 | * | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | * 7 | * | ███████║██║ ██║██████╔╝███████║█████╗ | * 8 | * | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | * 9 | * | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | * 10 | * | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | * 11 | * +--------------------------------------------+ * 12 | * * 13 | * Distributed Systems Runtime * 14 | * * 15 | * -------------------------------------------------------------------------- * 16 | * * 17 | * Licensed under the Apache License, Version 2.0 (the "License"); * 18 | * you may not use this file except in compliance with the License. * 19 | * You may obtain a copy of the License at * 20 | * * 21 | * http://www.apache.org/licenses/LICENSE-2.0 * 22 | * * 23 | * Unless required by applicable law or agreed to in writing, software * 24 | * distributed under the License is distributed on an "AS IS" BASIS, * 25 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 26 | * See the License for the specific language governing permissions and * 27 | * limitations under the License. * 28 | * * 29 | \* -------------------------------------------------------------------------- */ 30 | 31 | package pki 32 | 33 | import ( 34 | "crypto/rsa" 35 | "crypto/x509" 36 | "encoding/base64" 37 | "encoding/pem" 38 | "fmt" 39 | "os" 40 | "path/filepath" 41 | "testing" 42 | ) 43 | 44 | func TestCreateAuraeRootCA(t *testing.T) { 45 | t.Run("createAuraeCA", func(t *testing.T) { 46 | domainName := "unsafe.aurae.io" 47 | 48 | auraeCa, err := CreateAuraeRootCA("", "unsafe.aurae.io") 49 | if err != nil { 50 | t.Errorf("could not create auraeCA") 51 | } 52 | 53 | cert, _ := pem.Decode([]byte(auraeCa.Certificate)) 54 | crt, err := x509.ParseCertificate(cert.Bytes) 55 | if err != nil { 56 | t.Errorf("could not parse certificate") 57 | } 58 | if crt.Subject.CommonName != domainName { 59 | t.Errorf("certificate does not contain common name") 60 | } 61 | }) 62 | 63 | t.Run("createAuraeCA with local files", func(t *testing.T) { 64 | path := "_tmp/pki" 65 | domainName := "unsafe.aurae.io" 66 | 67 | _, err := CreateAuraeRootCA(path, domainName) 68 | if err != nil { 69 | t.Errorf("could not create auraeCA") 70 | } 71 | 72 | auraeCaFile, err := os.ReadFile(filepath.Join(path, "ca.crt")) 73 | if err != nil { 74 | t.Errorf("could not read ca file") 75 | } 76 | 77 | // load ca.cert 78 | cert, _ := pem.Decode(auraeCaFile) 79 | 80 | crt, err := x509.ParseCertificate(cert.Bytes) 81 | if err != nil { 82 | t.Errorf("could not parse certificate") 83 | } 84 | if crt.Subject.CommonName != domainName { 85 | t.Errorf("certificate does not contain common name") 86 | } 87 | 88 | // cleanup files 89 | err = os.Remove(filepath.Join(path, "ca.crt")) 90 | if err != nil { 91 | t.Errorf("could not delete %s", filepath.Join(path, "ca.crt")) 92 | } 93 | err = os.Remove(filepath.Join(path, "ca.key")) 94 | if err != nil { 95 | t.Errorf("could not delete %s", filepath.Join(path, "ca.key")) 96 | } 97 | }) 98 | } 99 | 100 | func TestCreateCSR(t *testing.T) { 101 | t.Run("createCSR", func(t *testing.T) { 102 | clientCsr, err := CreateClientCSR("", "unsafe.aurae.io", "christoph") 103 | if err != nil { 104 | t.Errorf("could not create csr") 105 | } 106 | 107 | csrBytes := []byte(clientCsr.CSR) 108 | csrPem, _ := pem.Decode(csrBytes) 109 | _, err = x509.ParseCertificateRequest(csrPem.Bytes) 110 | if err != nil { 111 | t.Errorf("could not parse certificate request") 112 | } 113 | 114 | keyBytes := []byte(clientCsr.PrivateKey) 115 | keyPem, _ := pem.Decode(keyBytes) 116 | _, err = x509.ParsePKCS1PrivateKey(keyPem.Bytes) 117 | if err != nil { 118 | t.Errorf("could not parse private key") 119 | } 120 | }) 121 | 122 | t.Run("createCSR with local files", func(t *testing.T) { 123 | path := "_tmp/pki" 124 | clientCsr, err := CreateClientCSR(path, "unsafe.aurae.io", "christoph") 125 | if err != nil { 126 | t.Errorf("could not create csr") 127 | } 128 | 129 | // read and load csr file 130 | csrFilePath := filepath.Join(path, fmt.Sprintf("client.%s.csr", clientCsr.User)) 131 | csrBytes, err := os.ReadFile(csrFilePath) 132 | if err != nil { 133 | t.Errorf("could not read ca file") 134 | } 135 | 136 | csrPem, _ := pem.Decode(csrBytes) 137 | _, err = x509.ParseCertificateRequest(csrPem.Bytes) 138 | if err != nil { 139 | t.Errorf("could not parse certificate request") 140 | } 141 | 142 | // read and load key file 143 | keyFilePath := filepath.Join(path, fmt.Sprintf("client.%s.key", clientCsr.User)) 144 | keyBytes, err := os.ReadFile(keyFilePath) 145 | if err != nil { 146 | t.Errorf("could not read key file") 147 | } 148 | 149 | keyPem, _ := pem.Decode(keyBytes) 150 | _, err = x509.ParsePKCS1PrivateKey(keyPem.Bytes) 151 | if err != nil { 152 | t.Errorf("could not parse private key") 153 | } 154 | 155 | // cleanup files 156 | err = os.Remove(csrFilePath) 157 | if err != nil { 158 | t.Errorf("could not delete %s", csrFilePath) 159 | } 160 | err = os.Remove(keyFilePath) 161 | if err != nil { 162 | t.Errorf("could not delete %s", keyFilePath) 163 | } 164 | }) 165 | 166 | t.Run("test createCSR against reference", func(t *testing.T) { 167 | // Reference material generated with (NAME = "christoph"): 168 | // openssl genrsa -out "./pki/client.${NAME}.key" 4096 2>/dev/null 169 | // openssl req \ 170 | // -new \ 171 | // -addext "subjectAltName = DNS:${NAME}.unsafe.aurae.io" \ 172 | // -subj "/C=IS/ST=aurae/L=aurae/O=Aurae/OU=Runtime/CN=${NAME}.unsafe.aurae.io" \ 173 | // -key "./pki/client.${NAME}.key" \ 174 | // -out "./pki/client.${NAME}.csr" 2>/dev/null 175 | // ...and base64 encoded 176 | referenceCSR64 := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJRTd6Q0NBdGNDQVFBd2N6RUxNQWtHQTFVRUJoTUNTVk14RGpBTUJnTlZCQWdNQldGMWNtRmxNUTR3REFZRApWUVFIREFWaGRYSmhaVEVPTUF3R0ExVUVDZ3dGUVhWeVlXVXhFREFPQmdOVkJBc01CMUoxYm5ScGJXVXhJakFnCkJnTlZCQU1NR1dOb2NtbHpkRzl3YUM1MWJuTmhabVV1WVhWeVlXVXVhVzh3Z2dJaU1BMEdDU3FHU0liM0RRRUIKQVFVQUE0SUNEd0F3Z2dJS0FvSUNBUUMyY2hEc1A2VnZPd1ZybVlGbE1DVjc5Uk95Rk40Qkdxem01NFM5SHVhVAppK09HY3RRUHNzL3loWVhmaWNPN3JWaldEMW02ODgvRXFGeFlXZ2xKbHJRT240NXE0dWxxVitPK0JvZGhqTWlzClNxbU1zamEraGM5cXE0bnIyRDF3Vno1L1RjQnBlZkd5OTVoL05xSElUTStRU0xRbFZ2djA4SWs0QXRybmVmekQKbCtnWGdkSVFVQkpXTTJ5U3VXZ1ViSjlSNURrUm9vRnZOVzhjaU1zQlhOQlJrQTBrZjdhTEQ3NGxCanFYYXNwcwo5bWlxSUhiSUR6TjZLZGZIbDdNOU9UQnFxMURQZHpwMy9qeVNoR21YVFYvai80WVBMUnVtVFRHQmlnK1RLTzFpCm53VnlRUDdoYjVIb0dla2RGK1ZyZ3Z2THcvN0MxUDBhMUYrMXp6YVJITUJwbWQzUVFxZEtwRzlZeW1WelhHd2wKUmJhTXVDWG1Ea2hTY3RYaDJYU0tCM0ZSQVhlc2x5L25Mc0NjOWIzYXZpTThrVUFsTG9rdUlCYzlDanBiZTBJRQpDTlovZklZcnRoZ2dIWVZOaWJpbm1NVTE3b2J5RzRuTW1RWTh1cngwYi95VFdrdk0rbjBLMzljd3FxRGNoNnZLCm5OZU90WVhiY3dDM2djdExpYkZ3Vm1Pai83MTRtSHU0UnVBWWVQcUxtUmlCNVkvcnNQbWR0Q2p0SEJxcE40NzkKdUV3SXZaVUxNVlJZcWFMZ0dFejdDckNCUFoyU09IYzB5akxxb3lLS0tCVGU5OGJVeFdUay9YQUhIYVlibjBIdwoyS1kxWmxnZ1haME1Rd0w1VVcwYkc4VkdlQk9hc2VoQ3M1dzBSckpqcU9QQ3gwQmlUcTVNeXAzV0dtSFFydXdjCmlRSURBUUFCb0Rjd05RWUpLb1pJaHZjTkFRa09NU2d3SmpBa0JnTlZIUkVFSFRBYmdobGphSEpwYzNSdmNHZ3UKZFc1ellXWmxMbUYxY21GbExtbHZNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUJpSzZSbUdRbzd2NXozMXo3UAp5Z3BIS2V0Z2U4U3FvcFU2Yk5TWUdETTVWcmRBYkQ2YUdBOUxQREs3bngwaEx4VVhka3NrcHY0SkpORVdIZHhVCkN2WUFOY1o1RGVNaklHaWlTZi9RVzkvVm4rWWlLaDV0Nk5tdHplZ2xXYi9DN2lzdmwwdytyR3VyQVhBU0VZaGwKUXp0dUNvR3hWVlo2aUo3OGU3cGhVTGFRNjlySDZhd3FIVVV4MXkxKzBPU3JEVG15WkI3Z20yRjVqVlRYZDZyRwp5MjNwajZUd3B3UHc2b1pEditoSjAxVWY2VXhSZ09QR042aS93dWhjQ2E2RFBjbGh4WFZmYjRzR2RwaWZwMWl6Ck9WMFNTZlpRNUdRanNXeWNzQTFOcGRDeTlkNjVXLzRTRG9ZK0JPVTBGZVpoS0Y2ODQrVEhwR3FaWXNaQ1MySGsKOUhaWCtRYnA0WVhKYTQxSjJRb29OdjZ4VHY4VG5OVW5RdGpJaHQ1aEl1eFBTMnZTTWYxUnlpT1oxOWh4NWZoRQo0SmtXdk1nekNNZHh4RVQ1NUFCaWt2SVo0bUNFYm4xT3JiWlNvR2hWZkFjUG5hOHUvOEFvQkZqL1Y5dnF3TzdOCjYwWmFYK1Y4WkRlY3ZIMkUyRHVJdTQyWXBqZVNUMjF0L1FEdE5McHVBdTc1OUYwZjRUbUNJbm1wZG9HcDNFcUwKL0JMNjRNRDByZWRTaDhnUFR2SDUyODEyc2I5dmJxUHBFSjBWOG90amxQMlhKdVJDNFJPOVFoOEo0TWl3TnNUOQp5NFFpSWxaOTl3d0lpL1YvaEUrVzVWNWdybC9wdTk1LzJmTDUrL2RvdC83c0NMb0pnd0UwS2svNm91NUtjZFFICitmeS8rVyswK2JleEdVYTdLWVZ4YkNCMzhRPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0tCg==" 177 | referenceKey64 := "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS0FJQkFBS0NBZ0VBdG5JUTdEK2xienNGYTVtQlpUQWxlL1VUc2hUZUFScXM1dWVFdlI3bWs0dmpobkxVCkQ3TFA4b1dGMzRuRHU2MVkxZzladXZQUHhLaGNXRm9KU1phMERwK09hdUxwYWxmanZnYUhZWXpJckVxcGpMSTIKdm9YUGFxdUo2OWc5Y0ZjK2YwM0FhWG54c3ZlWWZ6YWh5RXpQa0VpMEpWYjc5UENKT0FMYTUzbjh3NWZvRjRIUwpFRkFTVmpOc2tybG9GR3lmVWVRNUVhS0JielZ2SElqTEFWelFVWkFOSkgrMml3KytKUVk2bDJyS2JQWm9xaUIyCnlBOHplaW5YeDVlelBUa3dhcXRRejNjNmQvNDhrb1JwbDAxZjQvK0dEeTBicGsweGdZb1BreWp0WXA4RmNrRCsKNFcrUjZCbnBIUmZsYTRMN3k4UCt3dFQ5R3RSZnRjODJrUnpBYVpuZDBFS25TcVJ2V01wbGMxeHNKVVcyakxnbAo1ZzVJVW5MVjRkbDBpZ2R4VVFGM3JKY3Y1eTdBblBXOTJyNGpQSkZBSlM2SkxpQVhQUW82VzN0Q0JBaldmM3lHCks3WVlJQjJGVFltNHA1akZOZTZHOGh1SnpKa0dQTHE4ZEcvOGsxcEx6UHA5Q3QvWE1LcWczSWVyeXB6WGpyV0YKMjNNQXQ0SExTNG14Y0Zaam8vKzllSmg3dUViZ0dIajZpNWtZZ2VXUDY3RDVuYlFvN1J3YXFUZU8vYmhNQ0wyVgpDekZVV0ttaTRCaE0rd3F3Z1QyZGtqaDNOTW95NnFNaWlpZ1UzdmZHMU1WazVQMXdCeDJtRzU5QjhOaW1OV1pZCklGMmRERU1DK1ZGdEd4dkZSbmdUbXJIb1FyT2NORWF5WTZqandzZEFZazZ1VE1xZDFocGgwSzdzSElrQ0F3RUEKQVFLQ0FnRUFnSDBtMCtzakRJb0prRFRrdnluQVRHTldRcVdWa0R1RUozNUhxcFYzbDlQK0lqTCtqQ3ZIYmFxQgpsT1BHR0lmRnQ4UEowdk5na01SdGZMKzBLTUpjL3F0Nk5tYW1Nb0hCWDVQamhsMEsrdVArTXB0VUdLdk9YdlorClJMM2V6eDV5WWwrVXNmUHl0N0xPRUZHZWNKMC8xUUtPOUhrbEt1UzREdDFiNDRleTd1RXQwRmhhWTZpd3NVcTQKSFVFOFBwNGRPaVE3Mk9LVXU0aHJQekpMbmlNS2gxYW5HdHhpNTk3bmI5WEtMOWRDeHFobkgrR0xKZXdtdWROOApKeEg4WnBLL09YQjdraEVLK1hUd25kTnBOZWlGTHVKSFBLcnMvUnNDVVpPMDBsUVJrdElobU15VGRKc0pxK2VMCm1EUzdHeE45VjQwcC8zYlc1aTFKVnBhZmZHVStVREFBNlRCcTQvNHNNV1ZFL2FRUWdFRW1jMWQzK3d6MWkxQSsKRWF2dGZUU0VlaEIxemlXKzg0WFluVm96M3JQL0RRVE9qNXRIZzdqMzZYMDlQZ2J1LzFUUU9URmtCWDJhbElQQwpab295RkcvVXUrNkN0ZWtjdTVRZStodTYxbEt2MzE3SXlrWFVXMGV0MmEwQXFYZ0twMlFhNExQNDYwdUxRdW0vCjEzZGIvdnVibFNpMDBLQU1lVVRUaVM1N1Z6UU1nUVFUbGVCekZDbGxvZnFwTjNCeTJ6SVJIeHVMVmZPeVlsU0oKTVF3M1B2bTloTkduUkVDSHhIQ2MrT1BBa25WK1VCRTBVLzdTeWF0Rm9rdnFHNitQbGFUbHd6OE1uYXNBTm5HTAoyMW9HSWN5U0xISnpWek1ST3JiZ0Rua2QzREI0QWE4RXFwamN1NGtQdFFSbFlxb1djaTBDZ2dFQkFPTitlRjdDCmcvbnpKSVIzRWtvZGNYc2FsU2xjTjlKcVF6NExYbXdQUm9uYlhoeUlwOEdBR2xYSDJ5YUZ4Z0l3aENDYWtBQmgKT0grVGRGWlVteGFkRTdQRmxiU0lZL3hoK3JlVHNaL2lSSitDcXh4VkpqTHYwcXNtbGN4aWV0NTJSUklOVXJLegp1YVNCZDJwdTV1TStQWEtZSjM4VmdFdVdIVjlVRm9KTG00YVlKdnFQVU0zT1VicTdpTVVYODBGeklKRm93QUhJCnU0dHNvZ3JGdGxLdU5tREZnVEZWY0xNbXJrRG5CUzA2YjFROU9ROVVkWUF6OXBFSHRPcnFuT2ZvTGptQmRpZmgKQTVqQzNnL1NYdW5XcnV3SW13K1ZkTXdGSnVsaDZld01vd0VkRXdXcjUwK0lrWlFuSW84cUs5c0wxZFZJNEJHagp2WEVqY3ZDdUVvYjVZOE1DZ2dFQkFNMU9pVWtuK3lXWVNlV0NHRVhsR0RyYklNN0lLSERiQzg2d1B0bWVWNXVZCkhxeGh5akdhYll4WWZUSEI2RDJjVlUxYlNPSVZ0TEpaVVRhSlRPK0dEMCtKNU4vcStQbWloWldQcXJKNVg1WFMKSWRHQ1BFaEEyZERMc0ZoYlpQMmVyVXBqd1d3YmJBVjBoWU41QUoxdTNuMVQzdlVkTGNsT0xkeGRURkZSd0Q5VApyZG1xL1VMOStTVmprSFpqVEdpQ1UyWHVqVXdvYnNRNlgvUk0yeG52T3JjMDk0R2hRNVdMUmpZbThid0NMclA2CnVES1JLWUFHdkhSakJDYmRGRG5NSGQrMWMrVi95Nk96TGtVdlM3LzZZbkRlenB4Rk1WSGdJRGpvaytBZjFpRnAKc1FXNlEwSktVUmhqQWYzV2llOXJ5aVA5TU1ZT0tFNnBOMXlFWmJWZmRjTUNnZ0VBYXNTaWJhYlJGZS85UllZMAp1VUFVVUhoclpSdjR2dkpNV01ReExub0UyeEp2bXVpd0F1ckNjVnY1Q0oxa0R3Y0NHK011amw4U2l4MkRUamtyCkNIUDBHVDAwUTZSM2VLM3JZMWtYMWpmMWlQOWttMG1EUWdpNFVNY3RLdDFWV1M4Y3Y1b3RJOTJoMVFsR0tGZWcKV1NxTzRFZDAwZm9mV2xvN3NzL2VPSXlQazUyNVBZTWhvMVdmbWdvRjZLcVM2amJFSkRxTFVzc0k2aWl6N0daYQphWGVGNGVrUDl6MW9SVXgwSDlYTTRpczRzTXFEQ3lUU2VMYnFrNnFRU0dpUDkyOUtzb2FHRTdWUllOS2tNYnpECit1OWM3VDRrdUMybXdWSHhyenJhOUlRQnhMWUdoWFRtZkxkVnk3aUtTYks3SG5UeGlNWkpFejVMM051TVNGVUsKTVBxK3pRS0NBUUIrUE9FclMxc2d0YkFTWDlqZStVdlp2SzFDbUU1TmZsS1hSMFdOOTgrMGkyZW81UVEzVmRZdwpLcVRvT0d1OW5tZlJCZVVkcHUwUmtOdmY1Yktad051Zk01RzRvVGx2L1orWDQ5dTRtK3JMSzRiQjFRdU4vZG93CmlWNG9KaUpGMUJDSG9pam5lVUVGWmExR3R0dEs4a1g1MTkxSzZDTWtHVjhYbFlKOHFnREVyNFpCUmVNdUV3M2sKRUlGZVdoWThXSTVCS2RwVnpySzFFNU8ybXA5S0pnLzdZS1VqWHU0NGdJZXVlbW0vQ2JSLzFCVDRlc3VDdmlHWQppdDJkcStobzFYbzArTlNIYy9uWjhTM3RPblNnV2F1MzdUZ3JYRnhFRk1TYldWNjd1N2VsbWVCUVBrUm0rVjA5CjJucjZBcldUc3JwN1FJNkI2V2lkWFd6K0JTYW96RWFUQW9JQkFEY0FzNUQ4elg2dTN6dEhzSjg5amhFSzZ0L0wKOXo1UG5ocERocXA5Rk1BbC9jVUZvZkRQaTVOSGRrbjNPNDFQc1B1TFhHTWZ4SkFpOXFKZTdDVENmS3JvelVBbwprNzR3YXBQbEtIVjNsQm9hQUtDcUpQa1B0bjk3WG8rM215TUZJelRaVGtkRGNlY3ByZHZQeDZBbEZmME9BazBmCjlmZFJ0ZUNUSzhzUnV4WjJKTHJyRSs2ZG9HSm11NEpadmd2c0pheGliZm5yWFZjMVdvSkRCMEFvdmZ3SldxYVUKbStkaVhIcUhPQVE5NWVESlVJNWFsMXI2dW9xQWc4UStTYWY1T204dy9xckhZZWNiZ0pibG5tVDROelhDeFEvZgo1OTZzcFQ1dUllVjZCRFMzS2RBaDJtcWNNR1dwWGFnc3FHRjlNaFpyZlMvM0trMExFdk5xS0pqOVQraz0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K" 178 | 179 | referenceCSRBytes, err := base64.StdEncoding.DecodeString(referenceCSR64) 180 | if err != nil { 181 | t.Errorf("could not decode reference CSR") 182 | } 183 | 184 | referenceKeyBytes, err := base64.StdEncoding.DecodeString(referenceKey64) 185 | if err != nil { 186 | t.Errorf("could not decode reference key") 187 | } 188 | 189 | p, _ := pem.Decode(referenceCSRBytes) 190 | 191 | referenceCSR, err := x509.ParseCertificateRequest(p.Bytes) 192 | if err != nil { 193 | t.Errorf("could not parse certificate request: %s", err) 194 | } 195 | 196 | // Public Key Algorithm: rsaEncryption 197 | // RSA Public-Key: (4096 bit) 198 | if referenceCSR.PublicKeyAlgorithm.String() != x509.RSA.String() { 199 | t.Errorf("public key algorithm of reference is not correct:\ngot: %s, want: %s", referenceCSR.PublicKeyAlgorithm.String(), x509.RSA.String()) 200 | } 201 | if referenceCSR.PublicKey.(*rsa.PublicKey).N.BitLen() != 4096 { 202 | t.Errorf("public key length of reference is not correct:\ngot: %d, want: %d", referenceCSR.PublicKey.(*rsa.PublicKey).N.BitLen(), 4096) 203 | } 204 | 205 | // Checking whether the reference CSR is correct: 206 | // Subject: C=IS, ST=aurae, L=aurae, O=Aurae, OU=Runtime, CN=christoph.unsafe.aurae.io 207 | if referenceCSR.Subject.Country[0] != "IS" { 208 | t.Errorf("subject country of reference is not correct:\ngot: %s, want: %s", referenceCSR.Subject.Country[0], "IS") 209 | } 210 | if referenceCSR.Subject.Province[0] != "aurae" { 211 | t.Errorf("subject state or province of reference is not correct:\ngot: %s, want: %s", referenceCSR.Subject.Province[0], "aurae") 212 | } 213 | if referenceCSR.Subject.Locality[0] != "aurae" { 214 | t.Errorf("subject locality of reference is not correct:\ngot: %s, want: %s", referenceCSR.Subject.Locality[0], "aurae") 215 | } 216 | if referenceCSR.Subject.Organization[0] != "Aurae" { 217 | t.Errorf("subject organization of reference is not correct:\ngot: %s, want: %s", referenceCSR.Subject.Organization[0], "Aurae") 218 | } 219 | if referenceCSR.Subject.OrganizationalUnit[0] != "Runtime" { 220 | t.Errorf("subject organizational unit of reference is not correct:\ngot: %s, want: %s", referenceCSR.Subject.OrganizationalUnit[0], "Runtime") 221 | } 222 | if referenceCSR.Subject.CommonName != "christoph.unsafe.aurae.io" { 223 | t.Errorf("subject common name of reference is not correct:\ngot: %s, want: %s", referenceCSR.Subject.CommonName, "christoph.unsafe.aurae.io") 224 | } 225 | 226 | // Genenerate a new CSR 227 | clientCsr, err := CreateClientCSR("", "unsafe.aurae.io", "christoph") 228 | if err != nil { 229 | t.Errorf("could create csr") 230 | } 231 | csrPem, _ := pem.Decode([]byte(clientCsr.CSR)) 232 | generatedCSR, err := x509.ParseCertificateRequest(csrPem.Bytes) 233 | if err != nil { 234 | t.Errorf("could not parse certificate request") 235 | } 236 | 237 | // Public Key Algorithm: rsaEncryption 238 | if generatedCSR.PublicKeyAlgorithm.String() != referenceCSR.PublicKeyAlgorithm.String() { 239 | t.Errorf("public key algorithm is not correct:\ngot: %s, want: %s", referenceCSR.PublicKeyAlgorithm.String(), x509.RSA.String()) 240 | } 241 | // RSA Public-Key: (4096 bit) 242 | if generatedCSR.PublicKey.(*rsa.PublicKey).N.BitLen() != referenceCSR.PublicKey.(*rsa.PublicKey).N.BitLen() { 243 | t.Errorf("public key length is not correct:\ngot: %d, want: %d", referenceCSR.PublicKey.(*rsa.PublicKey).N.BitLen(), 4096) 244 | } 245 | 246 | // Checking whether the reference CSR is correct: 247 | // Subject: C=IS, ST=aurae, L=aurae, O=Aurae, OU=Runtime, CN=christoph.unsafe.aurae.io 248 | if generatedCSR.Subject.Country[0] != referenceCSR.Subject.Country[0] { 249 | t.Errorf("subject country is not correct:\ngot: %s, want: %s", generatedCSR.Subject.Country[0], referenceCSR.Subject.Country[0]) 250 | } 251 | if generatedCSR.Subject.Province[0] != referenceCSR.Subject.Province[0] { 252 | t.Errorf("subject state or province is not correct:\ngot: %s, want: %s", generatedCSR.Subject.Province[0], referenceCSR.Subject.Province[0]) 253 | } 254 | if generatedCSR.Subject.Locality[0] != referenceCSR.Subject.Locality[0] { 255 | t.Errorf("subject locality is not correct:\ngot: %s, want: %s", generatedCSR.Subject.Locality[0], referenceCSR.Subject.Locality[0]) 256 | } 257 | if generatedCSR.Subject.Organization[0] != referenceCSR.Subject.Organization[0] { 258 | t.Errorf("subject organization is not correct:\ngot: %s, want: %s", generatedCSR.Subject.Organization[0], referenceCSR.Subject.Organization[0]) 259 | } 260 | if generatedCSR.Subject.OrganizationalUnit[0] != referenceCSR.Subject.OrganizationalUnit[0] { 261 | t.Errorf("subject organizational unit is not correct:\ngot: %s, want: %s", generatedCSR.Subject.OrganizationalUnit[0], referenceCSR.Subject.OrganizationalUnit[0]) 262 | } 263 | if generatedCSR.Subject.CommonName != referenceCSR.Subject.CommonName { 264 | t.Errorf("subject common name is not correct:\ngot: %s, want: %s", referenceCSR.Subject.CommonName, referenceCSR.Subject.CommonName) 265 | } 266 | 267 | // Testing attributes: 268 | // Requested Extensions: 269 | // X509v3 Subject Alternative Name: 270 | // DNS:christoph.unsafe.aurae.io 271 | if generatedCSR.DNSNames[0] != referenceCSR.DNSNames[0] { 272 | t.Errorf("subject alternative name is not correct:\ngot: %s, want: %s", referenceCSR.DNSNames[0], referenceCSR.DNSNames[0]) 273 | } 274 | 275 | keyPem, _ := pem.Decode(referenceKeyBytes) 276 | _, err = x509.ParsePKCS1PrivateKey(keyPem.Bytes) 277 | if err != nil { 278 | t.Errorf("could not parse private key") 279 | } 280 | }) 281 | } 282 | --------------------------------------------------------------------------------