├── .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 |
19 |
20 |
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 | - • About The Project
46 | - • Quickstart
47 | - • Usage
48 | - • Philosophy
49 | - • Contribute
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 |
--------------------------------------------------------------------------------