├── .github
└── workflows
│ └── version-release.yaml
├── .gitignore
├── .goreleaser.yaml
├── .vscode
└── launch.json
├── LICENSE
├── app.json
├── cmd
└── devops
│ ├── config.yaml
│ ├── install.go
│ ├── main.go
│ └── semantic.go
├── commands.sh
├── common
├── conn_init.go
├── monitoring.go
└── release.go
├── devops-frontend
├── .vscode
│ └── launch.json
├── README.md
├── commands.sh
├── index.html
├── openapitools.json
├── package.json
├── public
│ └── vite.svg
├── src
│ ├── App.css
│ ├── App.tsx
│ ├── assets
│ │ ├── helmicon-32.png
│ │ ├── helmicon-64.png
│ │ ├── kubernetes32.png
│ │ └── react.svg
│ ├── components
│ │ ├── drawer
│ │ │ ├── Drawer.css
│ │ │ └── Drawer.tsx
│ │ ├── infoCard
│ │ │ ├── InfoCard.css
│ │ │ └── InfoCard.tsx
│ │ ├── isolator
│ │ │ └── Isolator.tsx
│ │ ├── recentlyUsed
│ │ │ └── RecentlyUsed.tsx
│ │ ├── resourceTable
│ │ │ ├── ResourceTable.css
│ │ │ └── ResourceTable.tsx
│ │ ├── sideNav
│ │ │ └── SideNav.tsx
│ │ ├── specificActionForm
│ │ │ └── SpecificActionForm.tsx
│ │ └── xTerm
│ │ │ └── XTerm.tsx
│ ├── generated-sources
│ │ └── openapi
│ │ │ ├── .openapi-generator-ignore
│ │ │ ├── .openapi-generator
│ │ │ ├── FILES
│ │ │ └── VERSION
│ │ │ ├── apis
│ │ │ ├── DefaultApi.ts
│ │ │ └── index.ts
│ │ │ ├── index.ts
│ │ │ ├── models
│ │ │ ├── ModelAuthResponse.ts
│ │ │ ├── ModelConfig.ts
│ │ │ ├── ModelErrorResponse.ts
│ │ │ ├── ModelEventResponse.ts
│ │ │ ├── ModelFrontendEvent.ts
│ │ │ ├── ModelInfoResponse.ts
│ │ │ ├── ModelPlugin.ts
│ │ │ ├── ModelServer.ts
│ │ │ ├── ProtoAction.ts
│ │ │ ├── ProtoAuthInfo.ts
│ │ │ ├── ProtoExecution.ts
│ │ │ ├── ProtoServerInput.ts
│ │ │ ├── ProtoUserInput.ts
│ │ │ ├── StructpbValue.ts
│ │ │ └── index.ts
│ │ │ └── runtime.ts
│ ├── index.css
│ ├── main.tsx
│ ├── pages
│ │ ├── Home.css
│ │ ├── Home.tsx
│ │ ├── PluginSelector.css
│ │ └── PluginSelector.tsx
│ ├── redux
│ │ ├── actions
│ │ │ └── infoActions.ts
│ │ ├── reducers
│ │ │ ├── Home.tsx
│ │ │ ├── PluginSelectorReducer.tsx
│ │ │ └── infoReducer.ts
│ │ ├── store.ts
│ │ └── typedReduxHook.ts
│ ├── services
│ │ ├── generalInfo.ts
│ │ └── index.ts
│ ├── types
│ │ ├── Event.ts
│ │ ├── InfoCardTypes.ts
│ │ ├── ResourceTypes.ts
│ │ └── utilsTypes.ts
│ ├── utils
│ │ ├── config.ts
│ │ ├── constants.ts
│ │ ├── notification.ts
│ │ ├── settings.ts
│ │ └── utils.ts
│ └── vite-env.d.ts
├── swagger.yaml
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── yarn.lock
├── devops-plugin-sdk
├── devops_plugin.go
├── go.mod
├── go.sum
├── grpc_client.go
├── grpc_server.go
├── interface.go
├── logger.go
├── proto
│ ├── devops.pb.go
│ ├── devops.proto
│ └── devops_grpc.pb.go
└── types.go
├── docs
├── docs.go
├── swagger.json
└── swagger.yaml
├── go.mod
├── go.sum
├── install.sh
├── internal
├── pluginmanager
│ ├── current_plugin_context.go
│ ├── helpers.go
│ ├── normal_operations.go
│ ├── operations.go
│ ├── plugin_client.go
│ ├── session.go
│ ├── specific_operations.go
│ └── table_stack.go
├── transformer
│ ├── testdata
│ │ └── testdata.go
│ ├── transformer.go
│ └── transformer_test.go
└── tui
│ ├── actions.go
│ ├── application.go
│ ├── delete_modal_page.go
│ ├── event_handlers.go
│ ├── eventloop.go
│ ├── flash.go
│ ├── form_page.go
│ ├── general_info.go
│ ├── helpers.go
│ ├── http.go
│ ├── isolator.go
│ ├── main_page.go
│ ├── plugin.go
│ ├── search.go
│ ├── specific_actions.go
│ ├── start.go
│ ├── table.go
│ ├── text_only_page.go
│ └── utils.go
├── model
├── config.go
├── events.go
├── http.go
└── transformer.go
├── plugins
├── helm
│ ├── .goreleaser.yaml
│ ├── config.yaml
│ ├── go.mod
│ ├── go.sum
│ ├── helm.go
│ ├── implementation.go
│ ├── main.go
│ ├── operations.go
│ ├── release.go
│ └── resource_config
│ │ ├── charts.yaml
│ │ ├── defaults.yaml
│ │ ├── releases.yaml
│ │ └── repos.yaml
└── kubernetes
│ ├── .goreleaser.yaml
│ ├── config.yaml
│ ├── go.mod
│ ├── go.sum
│ ├── implementation.go
│ ├── kubernetes.go
│ ├── main.go
│ ├── operations.go
│ ├── release.go
│ └── resource_config
│ ├── configmaps.yaml
│ ├── containers.yaml
│ ├── cronjobs.yaml
│ ├── daemonsets.yaml
│ ├── defaults.yaml
│ ├── deployments.yaml
│ ├── jobs.yaml
│ ├── namespaces.yaml
│ ├── nodes.yaml
│ ├── pods.yaml
│ ├── replicasets.yaml
│ ├── secrets.yaml
│ ├── serviceaccounts.yaml
│ └── services.yaml
├── readme.md
├── release.sh
├── server
├── handlers
│ └── handlers.go
├── middleware.go
└── server.go
├── utils
├── grpc.go
├── http.go
├── logger
│ └── logger.go
├── template.go
└── utils.go
└── validator
├── docker.go
├── kubernetes.go
├── pods.go
└── validation.go
/.github/workflows/version-release.yaml:
--------------------------------------------------------------------------------
1 | name: Go
2 | on:
3 | pull_request:
4 | branches:
5 | - master
6 | - v*
7 |
8 | jobs:
9 | v2:
10 | name: "Version Relase"
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Setup stage golang environment
14 | uses: actions/setup-go@v4
15 | with:
16 | go-version: stable
17 | id: go
18 |
19 | - name: Check out code into the Go module directory
20 | uses: actions/checkout@v3
21 |
22 | - name: Cache Go Modules
23 | uses: actions/cache@v1
24 | with:
25 | path: ~/go/pkg/mod
26 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
27 | restore-keys: |
28 | ${{ runner.os }}-go-
29 |
30 | - name: Setup Nodejs and npm
31 | uses: actions/setup-node@v2
32 | with:
33 | node-version: "16"
34 |
35 | # - name: Build Frontend
36 | # run: |
37 | # cd devops-frontend
38 | # yarn install --frozen-lockfile
39 | # yarn build
40 |
41 | - name: Install goreleaser
42 | run: |
43 | wget -q https://github.com/goreleaser/goreleaser/releases/download/v1.17.2/goreleaser_Linux_x86_64.tar.gz
44 | tar -xzf goreleaser_Linux_x86_64.tar.gz
45 | sudo mv goreleaser /usr/local/bin/
46 |
47 | - uses: ypicard/get-branch-name-github-action@v1
48 | id: current-branch
49 |
50 | - name: Set up Google Application Credentials
51 | run: |
52 | echo '${{ secrets.GOOGLE_APPLICATION_CREDENTIALS_JSON }}' > credentials.json
53 | echo 'GOOGLE_APPLICATION_CREDENTIALS=${{ github.workspace }}/credentials.json' >> $GITHUB_ENV
54 |
55 | - name: Run Build and Release script
56 | run: |
57 | # Download Go Releaser
58 | echo "Echo: Current branch is ${{ steps.current-branch.outputs.branch}}"
59 |
60 | # echo "Creating tag..."
61 | git config --global user.email "sharadregoti15@gmail.com"
62 | git config --global user.name "sharadregoti"
63 | git fetch origin ${{ steps.current-branch.outputs.branch}}:${{ steps.current-branch.outputs.branch}}
64 | git checkout ${{ steps.current-branch.outputs.branch}}
65 | git checkout -b "temp-branch"
66 | TIMESTAMP=$(date +%s)
67 | git tag -a "v0.5.3" -m "dummy tag"
68 | echo "TIMESTAMP=$TIMESTAMP" >> $GITHUB_ENV
69 |
70 | echo "Running script..."
71 | bash release.sh
72 |
73 | # - name: Run Build and Release script
74 | # run: |
75 | # # Download Go Releaser
76 | # echo "Echo: Current branch is ${{ steps.current-branch.outputs.branch}}"
77 |
78 | # # echo "Creating tag..."
79 | # git config --global user.email "sharadregoti15@gmail.com"
80 | # git config --global user.name "sharadregoti"
81 | # git fetch origin ${{ steps.current-branch.outputs.branch}}:${{ steps.current-branch.outputs.branch}}
82 | # git checkout ${{ steps.current-branch.outputs.branch}}
83 | # git tag -a ${{ steps.current-branch.outputs.branch}} -m "dummy tag"
84 | # echo "TIMESTAMP=$TIMESTAMP" >> $GITHUB_ENV
85 |
86 | # echo "Running script..."
87 | # bash release.sh
88 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, built with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # Dependency directories (remove the comment below to include it)
15 | # vendor/
16 | cmd/devops/devops
17 | plugins/kubernetes/kubernetes
18 | plugins/helm/helm
19 | frontend/devops
20 |
21 | *.csv
22 |
23 | dist/
24 |
25 | # Logs
26 | logs
27 | *.log
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 | pnpm-debug.log*
32 | lerna-debug.log*
33 |
34 | node_modules
35 | dist
36 | dist-ssr
37 | *.local
38 |
39 | # Editor directories and files
40 | .vscode/*
41 | !.vscode/extensions.json
42 | .idea
43 | .DS_Store
44 | *.suo
45 | *.ntvs*
46 | *.njsproj
47 | *.sln
48 | *.sw?
49 |
50 | gcs-storage-admin.json
--------------------------------------------------------------------------------
/.goreleaser.yaml:
--------------------------------------------------------------------------------
1 | before:
2 | hooks:
3 | - go mod tidy
4 | builds:
5 | - id: devops-core
6 | env:
7 | - CGO_ENABLED=0
8 | flags:
9 | - -tags=release
10 | main: ./cmd/devops
11 | binary: devops
12 | goos:
13 | - linux
14 | # - windows
15 | - darwin
16 | release:
17 | disable: true
18 | skip_upload: true
19 |
20 | archives:
21 | - builds:
22 | - devops-core
23 | id: devops-core
24 | replacements:
25 | # darwin: Darwin
26 | # linux: Linux
27 | # windows: Windows
28 | 386: i386
29 | amd64: x86_64
30 | files:
31 | - ./cmd/devops/config.yaml
32 | checksum:
33 | name_template: "checksums.txt"
34 | snapshot:
35 | name_template: "{{ incpatch .Version }}-next"
36 | changelog:
37 | sort: asc
38 | filters:
39 | exclude:
40 | - "^docs:"
41 | - "^test:"
42 |
43 | blobs:
44 | - provider: gs
45 | bucket: devops-cli-artifacts
46 | folder: "releases/devops/{{.Version}}"
47 | ids:
48 | - devops-core
49 | extra_files:
50 | - glob: ./install.sh
51 | - glob: devops-frontend/ui.tar.gz
52 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Launch Server",
9 | "type": "go",
10 | "request": "launch",
11 | "mode": "auto",
12 | "program": "${workspaceFolder}/cmd/devops",
13 | },
14 | {
15 | "name": "Launch TUI",
16 | "type": "go",
17 | "request": "launch",
18 | "mode": "auto",
19 | "program": "${workspaceFolder}/cmd/devops",
20 | "args": ["tui"]
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Master
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "user": {
3 | "id": "user-EglFKygBMOzhA0rEAohpLK6R",
4 | "name": "Sharad Regoti",
5 | "email": "sharadregoti15@gmail.com",
6 | "image": "https://lh3.googleusercontent.com/a/AEdFTp7wZy2QTU_Takie4kydPyzpfU37HfKvhsKJIFvKDQ=s96-c",
7 | "picture": "https://lh3.googleusercontent.com/a/AEdFTp7wZy2QTU_Takie4kydPyzpfU37HfKvhsKJIFvKDQ=s96-c",
8 | "groups": []
9 | },
10 | "expires": "2023-03-16T18:04:29.649Z",
11 | "accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1UaEVOVUpHTkVNMVFURTRNMEZCTWpkQ05UZzVNRFUxUlRVd1FVSkRNRU13UmtGRVFrRXpSZyJ9.eyJodHRwczovL2FwaS5vcGVuYWkuY29tL3Byb2ZpbGUiOnsiZW1haWwiOiJzaGFyYWRyZWdvdGkxNUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZ2VvaXBfY291bnRyeSI6IklOIn0sImh0dHBzOi8vYXBpLm9wZW5haS5jb20vYXV0aCI6eyJ1c2VyX2lkIjoidXNlci1FZ2xGS3lnQk1PemhBMHJFQW9ocExLNlIifSwiaXNzIjoiaHR0cHM6Ly9hdXRoMC5vcGVuYWkuY29tLyIsInN1YiI6Imdvb2dsZS1vYXV0aDJ8MTExOTEwMTAwMTM5OTI3NzEyNDY1IiwiYXVkIjpbImh0dHBzOi8vYXBpLm9wZW5haS5jb20vdjEiLCJodHRwczovL29wZW5haS5vcGVuYWkuYXV0aDBhcHAuY29tL3VzZXJpbmZvIl0sImlhdCI6MTY3NjIxMjgxMSwiZXhwIjoxNjc3NDIyNDExLCJhenAiOiJUZEpJY2JlMTZXb1RIdE45NW55eXdoNUU0eU9vNkl0RyIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwgbW9kZWwucmVhZCBtb2RlbC5yZXF1ZXN0IG9yZ2FuaXphdGlvbi5yZWFkIG9mZmxpbmVfYWNjZXNzIn0.daT-sZQRO833xTbxXee1Y3eCaY0KgUOOZn7nZ3HFBvKSVR3YzrvH6P9gZegC2CTMUjt4FPknWsWDUDFfY1gh2wfZ4yPKhp_NVEXJdqbtP0bP94DX-CGI74VwIrlWaCqrpSf5W-3bIe_Bn4fAc09CEnRLVxcTt4WVmgcgVc2iKuwh7Ub3qny2Zeyi1LdzNyjeyITIvHsgxOi3YxX8B69bETDHXZDqx4f0vsnz5AbjS4jWTPRAu9LvKNaqPtPH_5Z68iKHQ0PCYq5uf-09KAeYbJ8zB3HSbFq4ozJs563LZDFi8gbzbLlvxAYOKV9TjxMLNHA5CDPGeb8rpvZ8IhZ4eQ"
12 | }
--------------------------------------------------------------------------------
/cmd/devops/config.yaml:
--------------------------------------------------------------------------------
1 | server:
2 | address: ":9753"
3 | plugins:
4 | - name: kubernetes
5 | isDefault: true
6 | - name: helm
7 |
--------------------------------------------------------------------------------
/cmd/devops/semantic.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 | "strings"
7 | )
8 |
9 | // isGreater returns true if v1 is greater than v2 according to semantic versioning rules.
10 | func isGreater(v1, v2 string) bool {
11 | // parse the version numbers
12 | major1, minor1, patch1, err := parseVersion(v1)
13 | if err != nil {
14 | return false
15 | }
16 | major2, minor2, patch2, err := parseVersion(v2)
17 | if err != nil {
18 | // handle error
19 | return false
20 | }
21 |
22 | // compare the versions
23 | if major1 > major2 {
24 | return true
25 | }
26 | if major1 < major2 {
27 | return false
28 | }
29 | if minor1 > minor2 {
30 | return true
31 | }
32 | if minor1 < minor2 {
33 | return false
34 | }
35 | if patch1 > patch2 {
36 | return true
37 | }
38 | return false
39 | }
40 |
41 | // parseVersion parses a semantic version number and returns its components as integers.
42 | func parseVersion(v string) (int, int, int, error) {
43 | parts := strings.Split(v, ".")
44 | if len(parts) != 3 {
45 | return 0, 0, 0, fmt.Errorf("invalid version number: %s", v)
46 | }
47 |
48 | major, err := strconv.Atoi(parts[0])
49 | if err != nil {
50 | return 0, 0, 0, fmt.Errorf("invalid major version: %s", parts[0])
51 | }
52 | minor, err := strconv.Atoi(parts[1])
53 | if err != nil {
54 | return 0, 0, 0, fmt.Errorf("invalid minor version: %s", parts[1])
55 | }
56 | patch, err := strconv.Atoi(parts[2])
57 | if err != nil {
58 | return 0, 0, 0, fmt.Errorf("invalid patch version: %s", parts[2])
59 | }
60 |
61 | return major, minor, patch, nil
62 | }
63 |
--------------------------------------------------------------------------------
/commands.sh:
--------------------------------------------------------------------------------
1 | # Generating swagger docs
2 | # From root package
3 | swag init -g cmd/devops/main.go --parseDependency
4 |
5 |
6 | step 1: Increment version number in code
7 | step 2: close all milestone & related issues mentioned in it
8 | step 3: release all binaries
9 | # git tag -a v0.2.0 -m "m" #create dummy tag
10 | # Execute this from the root directory where .releaser file exists
11 | # goreleaser release --rm-dist --skip-validate (Run this for core binary as well as for k8s plugin from that directory)
12 | step 4: Create a new release on devops-cli repository, by mentioning the milestone
13 | step 5: Delete issues from github project
14 |
--------------------------------------------------------------------------------
/common/conn_init.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "context"
5 |
6 | "cloud.google.com/go/firestore"
7 | "cloud.google.com/go/logging"
8 | "google.golang.org/api/option"
9 |
10 | "log"
11 | )
12 |
13 | var logger *log.Logger
14 | var Release = false
15 |
16 | var data = `
17 | {
18 | "client_id": "764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com",
19 | "client_secret": "d-FL95Q19q7MQmFpd7hHD0Ty",
20 | "quota_project_id": "try-out-gcp-features",
21 | "refresh_token": "1//0gEi9-8AIjfiVCgYIARAAGBASNwF-L9Ir0stzEqkcB-y0MLsvg9DoBW_8o2fzXeYF9a5Zir-1VL9QXz-vjZiH89OsQ2kcPrdBdSs",
22 | "type": "authorized_user"
23 | }`
24 |
25 | func ConnInit() {
26 |
27 | ctx := context.Background()
28 |
29 | // Creates a client.
30 | client, err := logging.NewClient(ctx, projectID, option.WithCredentialsJSON([]byte(data)))
31 | if err != nil {
32 | log.Printf("Failed to create client: %v", err)
33 | }
34 | // defer client.Close()
35 |
36 | // Sets the name of the log to write to.
37 | logName := "devops-cli"
38 |
39 | logger = client.Logger(logName).StandardLogger(logging.Error)
40 |
41 | clientm, err := firestore.NewClient(ctx, projectID, option.WithCredentialsJSON([]byte(data)))
42 | if err != nil {
43 | log.Printf("Failed to create client: %v", err)
44 | }
45 |
46 | fClient = clientm
47 | }
48 |
49 | func ConnLoggingInit() {
50 | ctx := context.Background()
51 |
52 | // Creates a client.
53 | client, err := logging.NewClient(ctx, projectID, option.WithCredentialsJSON([]byte(data)))
54 | if err != nil {
55 | log.Printf("Failed to create client: %v", err)
56 | }
57 | // defer client.Close()
58 |
59 | // Sets the name of the log to write to.
60 | logName := "devops-cli"
61 |
62 | logger = client.Logger(logName).StandardLogger(logging.Error)
63 | }
64 |
--------------------------------------------------------------------------------
/common/monitoring.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "os"
8 | "time"
9 |
10 | "cloud.google.com/go/firestore"
11 | )
12 |
13 | var fClient *firestore.Client
14 | var projectID = "try-out-gcp-features"
15 |
16 | func IncrementAppStarts() error {
17 | if fClient == nil {
18 | return nil
19 | }
20 |
21 | ctx := context.Background()
22 | docRef := fClient.Collection("metrics").NewDoc()
23 | user := map[string]interface{}{
24 | "type": "start_counter",
25 | "labels": getUniqueInfo(),
26 | "value": 1,
27 | }
28 |
29 | if _, err := docRef.Set(ctx, user); err != nil {
30 | log.Printf("Failed to set data: %v", err)
31 | }
32 | return nil
33 | }
34 |
35 | func getUniqueInfo() map[string]string {
36 | hostname, err := os.Hostname()
37 | if err != nil {
38 | fmt.Println(err)
39 | return map[string]string{}
40 | }
41 |
42 | homeDir, err := os.UserHomeDir()
43 | if err != nil {
44 | fmt.Println(err)
45 | return map[string]string{}
46 | }
47 | return map[string]string{
48 | "home": homeDir,
49 | "host": hostname,
50 | }
51 | }
52 |
53 | func ReportUsageTime(startTime, endTime time.Time) error {
54 | if fClient == nil {
55 | return nil
56 | }
57 |
58 | // Calculate the usage time
59 | usageTime := endTime.Sub(startTime)
60 |
61 | ctx := context.Background()
62 | docRef := fClient.Collection("metrics").NewDoc()
63 | user := map[string]interface{}{
64 | "type": "usage_time",
65 | "labels": getUniqueInfo(),
66 | "value": usageTime.Seconds(),
67 | }
68 |
69 | if _, err := docRef.Set(ctx, user); err != nil {
70 | log.Printf("Failed to set data: %v", err)
71 | }
72 | return nil
73 | }
74 |
--------------------------------------------------------------------------------
/common/release.go:
--------------------------------------------------------------------------------
1 | //go:build release
2 | // +build release
3 |
4 | package common
5 |
6 | import (
7 | // "cloud.google.com/go/firestore"
8 | // "cloud.google.com/go/logging"
9 | // "context"
10 | // "google.golang.org/api/option"
11 |
12 | // "log"
13 | )
14 |
15 | func init() {
16 | Release = true
17 | }
18 |
--------------------------------------------------------------------------------
/devops-frontend/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "chrome",
9 | "request": "launch",
10 | "name": "Launch Chrome against localhost",
11 | "url": "http://localhost:8080",
12 | "webRoot": "${workspaceFolder}"
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/devops-frontend/README.md:
--------------------------------------------------------------------------------
1 | # devops-frontend
2 |
3 | ## Install Dependencies
4 |
5 | `yarn`
6 |
7 | ## Run in local
8 |
9 | `yarn dev`
10 |
--------------------------------------------------------------------------------
/devops-frontend/commands.sh:
--------------------------------------------------------------------------------
1 | # Command to generate initial code from swagger.yaml
2 | openapi-generator-cli generate -i ./swagger.yaml -o src/generated-sources/openapi -g typescript-fetch --additional-properties=supportsES6=true,npmVersion=6.9.0,typescriptThreePlus=true
3 |
4 | # For building the project
5 | # Uncomment the production URL
6 | vite build
7 | tar -czvf ui.tar.gz dist/
8 | cp ui.tar.gz ~/.devops/
9 | tar -xvzf ui.tar.gz
--------------------------------------------------------------------------------
/devops-frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/devops-frontend/openapitools.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
3 | "spaces": 2,
4 | "generator-cli": {
5 | "version": "6.4.0"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/devops-frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "devops-frontend",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview",
10 | "codegen": "rm -rf src/generated-sources/openapi; openapi-generator-cli generate -i ./json-placeholder-api.yaml -o src/generated-sources/openapi -g typescript-fetch --additional-properties=supportsES6=true,npmVersion=6.9.0,typescriptThreePlus=true"
11 | },
12 | "dependencies": {
13 | "@monaco-editor/react": "^4.4.6",
14 | "@reduxjs/toolkit": "^1.9.3",
15 | "@types/react-router-dom": "^5.3.3",
16 | "antd": "^5.1.6",
17 | "axios": "^1.2.3",
18 | "fuse.js": "^6.6.2",
19 | "js-yaml": "^4.1.0",
20 | "monaco-editor": "^0.36.1",
21 | "react": "^18.2.0",
22 | "react-dom": "^18.2.0",
23 | "react-fuzzy": "^1.3.0",
24 | "react-redux": "^8.0.5",
25 | "react-router-dom": "^6.10.0",
26 | "react-use-websocket": "^4.3.1",
27 | "redux": "^4.2.0",
28 | "redux-thunk": "^2.4.2",
29 | "xterm": "^5.1.0",
30 | "xterm-addon-attach": "^0.8.0",
31 | "xterm-addon-fit": "^0.7.0",
32 | "xterm-for-react": "^1.0.4",
33 | "yaml": "^2.2.1",
34 | "yamljs": "^0.3.0"
35 | },
36 | "devDependencies": {
37 | "@types/react": "^18.0.26",
38 | "@types/react-dom": "^18.0.9",
39 | "@types/react-redux": "^7.1.25",
40 | "@types/redux": "^3.6.0",
41 | "@types/redux-thunk": "^2.1.0",
42 | "@vitejs/plugin-react": "^3.0.0",
43 | "typescript": "^4.9.3",
44 | "vite": "^4.0.0"
45 | }
46 | }
--------------------------------------------------------------------------------
/devops-frontend/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/devops-frontend/src/App.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sharadregoti/devops-cli/81b5f40238d73abc985cbbe82e80b10419bf4b2b/devops-frontend/src/App.css
--------------------------------------------------------------------------------
/devops-frontend/src/App.tsx:
--------------------------------------------------------------------------------
1 | import Home from './pages/Home'
2 | import PluginSelector from './pages/PluginSelector'
3 | import { ConfigProvider, theme } from 'antd'
4 | import './App.css'
5 | import { BrowserRouter as Router, Route, Routes, useParams } from 'react-router-dom';
6 | import { Provider } from 'react-redux';
7 | import store from './redux/store';
8 |
9 | function App() {
10 | return (
11 |
16 |
17 |
18 |
19 | } />
20 | } />
21 | {/* } />
22 | } /> */}
23 |
24 |
25 |
26 |
27 | )
28 | }
29 |
30 | export default App
31 |
--------------------------------------------------------------------------------
/devops-frontend/src/assets/helmicon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sharadregoti/devops-cli/81b5f40238d73abc985cbbe82e80b10419bf4b2b/devops-frontend/src/assets/helmicon-32.png
--------------------------------------------------------------------------------
/devops-frontend/src/assets/helmicon-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sharadregoti/devops-cli/81b5f40238d73abc985cbbe82e80b10419bf4b2b/devops-frontend/src/assets/helmicon-64.png
--------------------------------------------------------------------------------
/devops-frontend/src/assets/kubernetes32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sharadregoti/devops-cli/81b5f40238d73abc985cbbe82e80b10419bf4b2b/devops-frontend/src/assets/kubernetes32.png
--------------------------------------------------------------------------------
/devops-frontend/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/devops-frontend/src/components/drawer/Drawer.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sharadregoti/devops-cli/81b5f40238d73abc985cbbe82e80b10419bf4b2b/devops-frontend/src/components/drawer/Drawer.css
--------------------------------------------------------------------------------
/devops-frontend/src/components/drawer/Drawer.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef, useState } from 'react';
2 | import { Drawer, Spin, Button } from 'antd';
3 | import XTermComponent from '../xTerm/XTerm';
4 | import Editor from '@monaco-editor/react';
5 | import { showNotification } from '../../utils/notification';
6 | import { api } from '../../utils/config';
7 | import { AppState } from '../../types/Event'
8 | import { ModelFrontendEvent, ModelFrontendEventNameEnum, HandleEventRequest } from '../../generated-sources/openapi';
9 | import yaml from "yamljs";
10 |
11 | type DrawerBodyType = "editor" | "xterm" | "table";
12 |
13 | export type DrawerPropsTypes = {
14 | socketUrl: string
15 | drawerBodyType: DrawerBodyType
16 | isDrawerOpen: boolean
17 | resourceName: string
18 | appConfig: AppState
19 | editorOptions: EditorOptions
20 | onDrawerClose: () => void
21 | }
22 |
23 | type EditorOptions = {
24 | isReadOnly: boolean
25 | defaultText: string
26 | }
27 |
28 | const SideDrawer: React.FC = ({ isDrawerOpen, socketUrl, drawerBodyType, resourceName, appConfig, editorOptions, onDrawerClose }) => {
29 | console.log('Rendering SideDrawer...');
30 |
31 | const terminalRef = useRef(null);
32 | const editorRef = useRef(null);
33 |
34 | // const [drawerLoading, setDrawerLoading] = useState(false);
35 |
36 | const handleEditorSaveButton = () => {
37 | // TODO: Handle spinner
38 | // setDrawerLoading(true);
39 |
40 | // Get the current content of the editor
41 | const content = editorRef.current.getValue();
42 |
43 | let yamlobj = {}
44 | try {
45 | yamlobj = yaml.parse(content);
46 | } catch (error) {
47 | // TODO: Handle spinner
48 | // setDrawerLoading(false);
49 | showNotification('error', 'Invalid YAML', error.message)
50 | return
51 | }
52 |
53 | const e: ModelFrontendEvent = {
54 | eventType: "normal-action",
55 | name: ModelFrontendEventNameEnum.Edit,
56 | isolatorName: appConfig?.currentIsolator,
57 | pluginName: appConfig?.currentPluginName,
58 | resourceName: resourceName,
59 | resourceType: appConfig?.currentResourceType,
60 | args: yamlobj,
61 | }
62 |
63 | let params: HandleEventRequest = {
64 | id: appConfig.generalInfo.id,
65 | modelFrontendEvent: e
66 | }
67 |
68 | api.handleEvent(params)
69 | .then(res => {
70 | // setDrawerLoading(false);
71 | // setOpen(false);
72 | onDrawerClose()
73 | showNotification('success', `Successfully updated ${e.resourceType}`, '')
74 | })
75 | .catch(err => {
76 | // setDrawerLoading(false);
77 | showNotification('error', 'Event invocation failed', err)
78 | })
79 | }
80 |
81 | return (
82 |
89 | {/* TODO: Handle spinner */}
90 | {/* */}
91 | {drawerBodyType == 'editor' && <>
92 | {
98 | // Store a reference to the editor instance
99 | editorRef.current = editor;
100 | }}
101 | />
102 | {!editorOptions.isReadOnly &&
103 |
104 | }
105 | >}
106 | {drawerBodyType == 'xterm' &&
107 |
108 | }
109 | {/* */}
110 |
111 | );
112 | };
113 |
114 | export default SideDrawer;
--------------------------------------------------------------------------------
/devops-frontend/src/components/infoCard/InfoCard.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sharadregoti/devops-cli/81b5f40238d73abc985cbbe82e80b10419bf4b2b/devops-frontend/src/components/infoCard/InfoCard.css
--------------------------------------------------------------------------------
/devops-frontend/src/components/infoCard/InfoCard.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Card, Descriptions, Typography } from "antd";
3 | import { Action, General, Plugins } from "../../types/InfoCardTypes";
4 | import './InfoCard.css'
5 |
6 | type InfoCardPropsTypes = {
7 | title: string;
8 | content: Action | Plugins | General | { 0: string } | {};
9 | }
10 |
11 | const InfoCard: React.FC = ({ title, content }) => {
12 | const keyValueStyle = {
13 | display: "block",
14 | maxWidth: "40ch",
15 | whiteSpace: "nowrap",
16 | overflow: "hidden",
17 | textOverflow: "ellipsis",
18 | lineHeight: "1.5",
19 | paddingRight: "1ch",
20 | // display: "block",
21 | };
22 |
23 | return (
24 | <>
25 |
29 | {content &&
30 | Object.entries(content).map(([key, value]: [string, string]) => (
31 | // Object.entries(content).slice(0, 4).map(([key, value]: [string, string]) => (
32 |
33 | {/* {key}: {value} */}
34 | {value}
35 |
36 | ))}
37 |
38 | >
39 | );
40 | }
41 |
42 |
43 | export default InfoCard;
--------------------------------------------------------------------------------
/devops-frontend/src/components/isolator/Isolator.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { Card, Typography, Select, Radio, Space, Row, Col } from "antd";
3 | import type { RadioChangeEvent } from 'antd';
4 | import { AppState } from '../../types/Event'
5 |
6 | // import './InfoCard.css'
7 | import Paragraph from "antd/es/skeleton/Paragraph";
8 |
9 | export type InfoCardPropsTypes = {
10 | currentIsolator: string,
11 | defaultIsolator: string,
12 | isolators: string[],
13 | frequentlyUsed: string[],
14 | appConfig: AppState
15 | onNamespaceChange: (isolatorName: string) => void
16 | }
17 |
18 | const IsolatorCard: React.FC = ({ currentIsolator, defaultIsolator, isolators, frequentlyUsed, appConfig, onNamespaceChange }) => {
19 | const onChange = (e: RadioChangeEvent) => {
20 | onNamespaceChange(e.target.value)
21 | };
22 |
23 | const [selectWidth, setSelectWidth] = useState()
24 |
25 | function getTextWidth(text, font) {
26 | const canvas = document.createElement('canvas');
27 | const context = canvas.getContext('2d');
28 | context.font = font;
29 | const metrics = context.measureText(text);
30 | return metrics.width;
31 | }
32 |
33 | useEffect(() => {
34 | if (isolators.length === 0) {
35 | return
36 | }
37 |
38 | const longestIsolatorName = isolators
39 | ? isolators.reduce((longest, isolator) => {
40 | return isolator.length > longest.length ? isolator : longest;
41 | }, '')
42 | : '';
43 |
44 | const selectWidth = getTextWidth(longestIsolatorName, '16px Arial') + 50; // 50px extra for padding and dropdown arrow
45 | setSelectWidth(selectWidth);
46 | }, [isolators])
47 |
48 | return (
49 | <>
50 |
51 |
52 | Isolator
53 |
54 |
55 |
68 |
69 |
70 |
71 | {/* */}
72 |
73 | {frequentlyUsed.map((val, i) => {
74 | if (i < 5) {
75 | return (
76 | ({i}): {val}
77 | )
78 | }
79 | })}
80 |
81 | {/* / */}
82 |
83 |
84 |
85 |
86 | >
87 | );
88 | }
89 |
90 | export default IsolatorCard;
--------------------------------------------------------------------------------
/devops-frontend/src/components/recentlyUsed/RecentlyUsed.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { Card, Descriptions, Space, Typography } from "antd";
3 | import { Action, General, Plugins } from "../../types/InfoCardTypes";
4 | // import './InfoCard.css'
5 | import CheckableTag from "antd/es/tag/CheckableTag";
6 |
7 | type RecentlyUsedPropsTypes = {
8 | title: string;
9 | recentlyUsedItems: string[];
10 | // Define an async function
11 | onSearch: (value: string) => void
12 | // onSearch: async (value: string) => {
13 | // content: Action | Plugins | General | { 0: string } | {};
14 | }
15 |
16 | const RecentlyUsed: React.FC = ({ title, recentlyUsedItems, onSearch }) => {
17 | const keyValueStyle = {
18 | display: "block",
19 | maxWidth: "40ch",
20 | whiteSpace: "nowrap",
21 | overflow: "hidden",
22 | textOverflow: "ellipsis",
23 | lineHeight: "1.5",
24 | paddingRight: "1ch",
25 | // display: "block",
26 | };
27 |
28 | const [selectedTags, setSelectedTags] = useState([]);
29 |
30 | const handleChange = (tag: string, checked: boolean) => {
31 | const nextSelectedTags = checked
32 | ? [tag]
33 | : selectedTags.filter((t) => t !== tag);
34 | console.log('You are interested in: ', nextSelectedTags);
35 | setSelectedTags(nextSelectedTags);
36 |
37 | if (checked) {
38 | onSearch(tag);
39 | }
40 | };
41 |
42 | // const tagsData = ['Movies', 'Books', 'Music', 'Sports'];
43 |
44 | return (
45 | <>
46 |
50 | <>
51 | {/* Categories: */}
52 | {/* */}
53 | {recentlyUsedItems.map((tag) => (
54 | handleChange(tag, checked)}
58 | >
59 | {tag}
60 |
61 | ))}
62 | {/* */}
63 | >
64 |
65 | >
66 | );
67 | }
68 |
69 |
70 | export default RecentlyUsed;
--------------------------------------------------------------------------------
/devops-frontend/src/components/resourceTable/ResourceTable.css:
--------------------------------------------------------------------------------
1 | .lightskyblue {
2 | background-color: lightskyblue;
3 | }
4 |
5 | /* Change background color on hover */
6 | tr:hover {
7 | opacity: 0.5;
8 | }
9 | .ant-table-tbody>tr.ant-table-row:hover>td {
10 | background: none !important;
11 | }
12 |
13 | .darkorange {
14 | background-color: darkorange;
15 | }
16 |
17 | .gray {
18 | background-color: gray;
19 | }
20 |
21 | .white {
22 | background-color: white;
23 | }
24 |
25 | .mediumpurple {
26 | background-color: mediumpurple;
27 | }
28 |
29 | .red {
30 | background-color: red;
31 | }
32 |
33 | .yellow {
34 | background-color: yellow;
35 | }
36 |
37 | .orange {
38 | background-color: orange;
39 | }
40 |
41 | .green {
42 | background-color: green;
43 | }
44 |
45 | .aqua {
46 | background-color: aqua;
47 | }
48 |
49 | .highlighted {
50 | background-color: white;
51 | /* Change this color to your desired highlight color */
52 | }
--------------------------------------------------------------------------------
/devops-frontend/src/components/sideNav/SideNav.tsx:
--------------------------------------------------------------------------------
1 | import { Menu, MenuProps } from "antd"
2 | import Sider from "antd/es/layout/Sider"
3 | import { ReactNode, useEffect, useState } from "react";
4 | import { useDispatch, useSelector } from "react-redux";
5 | import Icon, { HomeOutlined } from '@ant-design/icons';
6 | import { NavBarItem, NavBarState } from "../../redux/reducers/PluginSelectorReducer";
7 | import {
8 | PieChartOutlined, AppstoreFilled, RightCircleFilled
9 | } from '@ant-design/icons';
10 | import { useNavigate } from "react-router-dom";
11 | import { showNotification } from "../../utils/notification";
12 |
13 | type MenuItem = Required['items'][number];
14 |
15 | export type SideNavProps = {
16 | selectedItem: string
17 | newNavItem?: NavBarItem
18 | }
19 |
20 |
21 | import kubernetesImage from '../../assets/kubernetes32.png';
22 | import helmImage from '../../assets/helmicon-32.png';
23 |
24 | const SideNav: React.FC = ({ selectedItem, newNavItem }) => {
25 | const navigate = useNavigate();
26 | const dispatch = useDispatch();
27 | const { items } = useSelector((state: NavBarState) => state.navBar);
28 | const [collapsed, setCollapsed] = useState(true);
29 | const [navBarItems, setNavBarItems] = useState