Compare commits
42 Commits
1cbbebff40
...
master
Author | SHA1 | Date | |
---|---|---|---|
927d6e92b0 | |||
f4f3164978 | |||
889b7a4ca2 | |||
7f2afd1bdb | |||
d3056fc0c9 | |||
a0e852feab | |||
747c774c98 | |||
c32698b510 | |||
d8431550f3 | |||
290e872c15 | |||
d50bf24226 | |||
423c0b5a37 | |||
f25a6f7806 | |||
9c56ec0d85 | |||
a115970e7e | |||
8463725b9c | |||
2ab17147fb | |||
3bb03bad87 | |||
3f10f63c28 | |||
f3c725b792 | |||
b24dcfa9d9 | |||
5a6cd4ac16 | |||
4a5b954ad6 | |||
8028a50cc3 | |||
10544e1c2d | |||
77cd6aac14 | |||
7b66f95832 | |||
ed0284a7c7 | |||
07c8f5d676 | |||
1ac8357335 | |||
7c576eae4c | |||
75d7c6aadc | |||
a65651289e | |||
c666bdedd1 | |||
bd80d1213f | |||
56d10bc7e4 | |||
4f933b4e38 | |||
5da99536d1 | |||
ff95becbbd | |||
c039838a9e | |||
7793f7e5f9 | |||
3128354700 |
5
.gitignore
vendored
Executable file
5
.gitignore
vendored
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
/node_modules
|
||||||
|
/output
|
||||||
|
/custom_tls_cert_gen/output
|
||||||
|
|
||||||
|
yarn.lock
|
170
build_manager.sh
170
build_manager.sh
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
|
source ./utils/common.sh
|
||||||
|
|
||||||
WORKDIR=/home/oscarzhou/source/github.com/portainer
|
WORKDIR=/home/oscarzhou/source/github.com/portainer
|
||||||
GLOBAL_VOLUME=/home/oscarzhou/volumes
|
GLOBAL_VOLUME=/home/oscarzhou/volumes
|
||||||
TRUE=0;
|
TRUE=0;
|
||||||
@ -10,12 +12,8 @@ FALSE=1;
|
|||||||
# PORTAINER_FLAGS=
|
# PORTAINER_FLAGS=
|
||||||
# PORTAINER_FLAGS=--enable-init true
|
# PORTAINER_FLAGS=--enable-init true
|
||||||
|
|
||||||
ERROR_COLOR='\033[0;31m';
|
|
||||||
HIGHLIGHT_COLOR='\033[0;32m';
|
|
||||||
NO_COLOR='\033[0m';
|
|
||||||
|
|
||||||
function choose_repo() {
|
function choose_repo() {
|
||||||
read -p "Choose the working project EE/(CE):" REPO
|
read -p "Choose the working project EE/(CE)/(k8s)/(agent):" REPO
|
||||||
|
|
||||||
if [ -z "$REPO" ]; then
|
if [ -z "$REPO" ]; then
|
||||||
REPO="EE";
|
REPO="EE";
|
||||||
@ -26,6 +24,12 @@ function choose_repo() {
|
|||||||
|
|
||||||
elif [[ "${REPO}" == "ce" || "${REPO}" == "CE" ]]; then
|
elif [[ "${REPO}" == "ce" || "${REPO}" == "CE" ]]; then
|
||||||
cd ${WORKDIR}/portainer
|
cd ${WORKDIR}/portainer
|
||||||
|
|
||||||
|
elif [[ "${REPO}" == "k8s" || "${REPO}" == "ks" ]]; then
|
||||||
|
cd ${WORKDIR}/k8s
|
||||||
|
|
||||||
|
elif [[ "${REPO}" == "ag" || "${REPO}" == "agent" ]]; then
|
||||||
|
cd ${WORKDIR}/agent
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,11 +76,12 @@ function choose_export_volume() {
|
|||||||
printf "${HIGHLIGHT_COLOR}export PORTAINER_DATA=${PORTAINER_DATA}${NO_COLOR}\n"
|
printf "${HIGHLIGHT_COLOR}export PORTAINER_DATA=${PORTAINER_DATA}${NO_COLOR}\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepare_temporary_volume() {
|
function cleanup_temporary_volume() {
|
||||||
printf "${HIGHLIGHT_COLOR}Prepare temporary data${NO_COLOR}\n"
|
printf "${HIGHLIGHT_COLOR}Clean temporary data${NO_COLOR}\n"
|
||||||
|
|
||||||
local VOLUME=~/volumes/temp-data
|
local VOLUME=~/volumes/temp-data
|
||||||
if [ -d ${VOLUME} ]; then
|
if [ -d ${VOLUME} ]; then
|
||||||
|
printf "The current volume is ${VOLUME}. "
|
||||||
read -p "Do you want to clean up the existing data N/(Y)?" CLEAN_DATA
|
read -p "Do you want to clean up the existing data N/(Y)?" CLEAN_DATA
|
||||||
if [[ "${CLEAN_DATA}" == "y" || "${CLEAN_DATA}" == "Y" ]]; then
|
if [[ "${CLEAN_DATA}" == "y" || "${CLEAN_DATA}" == "Y" ]]; then
|
||||||
rm -rvf ${VOLUME}/*
|
rm -rvf ${VOLUME}/*
|
||||||
@ -89,6 +94,7 @@ function prepare_temporary_volume() {
|
|||||||
function build_portainer_frontend_without_prompt() {
|
function build_portainer_frontend_without_prompt() {
|
||||||
printf "${HIGHLIGHT_COLOR}Build Portainer Frontend${NO_COLOR}\n"
|
printf "${HIGHLIGHT_COLOR}Build Portainer Frontend${NO_COLOR}\n"
|
||||||
|
|
||||||
|
yarn
|
||||||
yarn start:client
|
yarn start:client
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,6 +131,16 @@ function build_portainer_backend() {
|
|||||||
build_portainer_backend_without_prompt
|
build_portainer_backend_without_prompt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function build_portainer_agent() {
|
||||||
|
choose_repo
|
||||||
|
|
||||||
|
if ! check_branch; then
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
./dev.sh compile
|
||||||
|
}
|
||||||
|
|
||||||
function build_portainer_all() {
|
function build_portainer_all() {
|
||||||
printf "${HIGHLIGHT_COLOR}Build Portainer all${NO_COLOR}\n"
|
printf "${HIGHLIGHT_COLOR}Build Portainer all${NO_COLOR}\n"
|
||||||
|
|
||||||
@ -150,41 +166,155 @@ function run_before_commit() {
|
|||||||
exit;
|
exit;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf "${HIGHLIGHT_COLOR}yarn format${NO_COLOR}\n"
|
input "1. Only frontend 2. Only backend 3. Both" option
|
||||||
|
|
||||||
|
if [ option == "1" ]; then
|
||||||
|
print_highlight "yarn test";
|
||||||
|
yarn test:client
|
||||||
|
|
||||||
|
print_highlight "yarn format";
|
||||||
|
yarn format:client
|
||||||
|
|
||||||
|
print_highlight "yarn lint"
|
||||||
|
yarn lint:client
|
||||||
|
elif [ option == "2" ]; then
|
||||||
|
print_highlight "yarn test";
|
||||||
|
yarn test:server
|
||||||
|
|
||||||
|
print_highlight "yarn format";
|
||||||
|
yarn format:server
|
||||||
|
|
||||||
|
print_highlight "yarn lint"
|
||||||
|
yarn lint:server
|
||||||
|
|
||||||
|
elif [ option == "3" ]; then
|
||||||
|
print_highlight "yarn test";
|
||||||
|
yarn test
|
||||||
|
|
||||||
|
print_highlight "yarn format";
|
||||||
yarn format
|
yarn format
|
||||||
|
|
||||||
printf "${HIGHLIGHT_COLOR}yarn lint${NO_COLOR}\n"
|
print_highlight "yarn lint"
|
||||||
yarn lint
|
yarn lint
|
||||||
|
else
|
||||||
|
print_highlight "yarn test";
|
||||||
|
yarn test:client
|
||||||
|
|
||||||
|
print_highlight "yarn format";
|
||||||
|
yarn format:client
|
||||||
|
|
||||||
|
print_highlight "yarn lint"
|
||||||
|
yarn lint:client
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function run_before_commit_k8s() {
|
||||||
|
printf "${HIGHLIGHT_COLOR}Run before commit${NO_COLOR}\n"
|
||||||
|
|
||||||
|
choose_repo
|
||||||
|
|
||||||
|
if ! check_branch; then
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "${HIGHLIGHT_COLOR}chart-testing ct lint${NO_COLOR}\n"
|
||||||
|
|
||||||
|
docker run --rm -it -w /repo -v `pwd`:/repo quay.io/helmpack/chart-testing ct lint --all --config=.ci/ct-config.yaml
|
||||||
|
}
|
||||||
|
|
||||||
|
function generate_portainer_jwt() {
|
||||||
|
printf "${HIGHLIGHT_COLOR}Generate Portainter JWT${NO_COLOR}\n"
|
||||||
|
|
||||||
|
read -p "Username(admin):" username
|
||||||
|
if [ -z "$username" ]; then
|
||||||
|
username="admin";
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -p "Password(****):" password
|
||||||
|
read -p "Address(127.0.0.1):" address
|
||||||
|
if [ -z "$address" ]; then
|
||||||
|
address="127.0.0.1";
|
||||||
|
fi
|
||||||
|
|
||||||
|
payload="{\"username\":\"${username}\",\"password\":\"${password}\"}"
|
||||||
|
curl -d ${payload} -H 'Content-Type: application/json' "http://${address}:9000/api/auth"
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_portainer_ce_api_reference() {
|
||||||
|
printf "${HIGHLIGHT_COLOR}Get the reference of Portainer CE API${NO_COLOR}\n"
|
||||||
|
|
||||||
|
cd ${WORKDIR}/portainer
|
||||||
|
|
||||||
|
if ! check_branch; then
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -p "Commit(HEAD):" commit
|
||||||
|
if [ -z "$commit" ]; then
|
||||||
|
commit=$(git rev-parse HEAD)
|
||||||
|
fi
|
||||||
|
printf "${HIGHLIGHT_COLOR}Installing github.com/portainer/portainer/api@${commit}${NO_COLOR}\n"
|
||||||
|
|
||||||
|
output=$(go install github.com/portainer/portainer/api@${commit}) | while IFS= read -r line; do
|
||||||
|
echo "$line"
|
||||||
|
done
|
||||||
|
|
||||||
|
# result="
|
||||||
|
# go: downloading github.com/portainer/portainer/api v0.0.0-20220622202437-f0ca3e63db9d
|
||||||
|
# go: downloading github.com/portainer/portainer v0.6.1-0.20220622202437-f0ca3e63db9d
|
||||||
|
# package github.com/portainer/portainer/api is not a main package
|
||||||
|
# "
|
||||||
|
|
||||||
|
# while IFS= read -r line; do
|
||||||
|
# echo "$line"
|
||||||
|
# done <<< $(echo ${result})
|
||||||
}
|
}
|
||||||
|
|
||||||
function menu() {
|
function menu() {
|
||||||
PS3='Please select the option: '
|
PS3='Please select the option: '
|
||||||
OPTIONS=(
|
OPTIONS=(
|
||||||
'Build Portainer All'
|
'Build Portainer EE/CE All'
|
||||||
'Build Portainer Frontend'
|
'Build Portainer EE/CE Frontend'
|
||||||
'Build Portainer Backend'
|
'Build Portainer EE/CE Backend'
|
||||||
'Run Before Commit'
|
'Generate Portainer EE/CE JWT'
|
||||||
'Prepare Temporary Volume'
|
'Run Before Commit [Portainer EE/CE]'
|
||||||
|
'Get Portainer CE API Reference'
|
||||||
|
'Run Before Commit [k8s]'
|
||||||
|
'Build Portainer Agent'
|
||||||
|
'Cleanup Temporary Volume'
|
||||||
'Quit'
|
'Quit'
|
||||||
)
|
)
|
||||||
|
|
||||||
select opt in "${OPTIONS[@]}"
|
select opt in "${OPTIONS[@]}"
|
||||||
do
|
do
|
||||||
case $opt in
|
case $opt in
|
||||||
'Build Portainer All')
|
'Build Portainer EE/CE All')
|
||||||
build_portainer_all
|
build_portainer_all
|
||||||
;;
|
;;
|
||||||
'Build Portainer Frontend')
|
'Build Portainer EE/CE Frontend')
|
||||||
build_portainer_frontend
|
build_portainer_frontend
|
||||||
;;
|
;;
|
||||||
'Build Portainer Backend')
|
'Build Portainer EE/CE Backend')
|
||||||
build_portainer_backend
|
build_portainer_backend
|
||||||
;;
|
;;
|
||||||
'Run Before Commit')
|
'Generate Portainer EE/CE JWT')
|
||||||
|
generate_portainer_jwt
|
||||||
|
;;
|
||||||
|
'Run Before Commit [Portainer EE/CE]')
|
||||||
run_before_commit
|
run_before_commit
|
||||||
;;
|
;;
|
||||||
'Prepare Temporary Volume')
|
'Get Portainer CE API Reference')
|
||||||
prepare_temporary_volume
|
get_portainer_ce_api_reference
|
||||||
|
;;
|
||||||
|
'Run Before Commit [k8s]')
|
||||||
|
run_before_commit_k8s
|
||||||
|
;;
|
||||||
|
'Build Portainer Agent')
|
||||||
|
build_portainer_agent
|
||||||
|
;;
|
||||||
|
'Cleanup Temporary Volume')
|
||||||
|
cleanup_temporary_volume
|
||||||
;;
|
;;
|
||||||
'Quit')
|
'Quit')
|
||||||
break
|
break
|
||||||
|
94
custom_tls_cert_gen/generate-custom-tls.sh
Executable file
94
custom_tls_cert_gen/generate-custom-tls.sh
Executable file
@ -0,0 +1,94 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
source ../utils/common.sh
|
||||||
|
|
||||||
|
input "Specify the output path:" OUTPUT_PATH
|
||||||
|
|
||||||
|
if [ -z "$OUTPUT_PATH" ]; then
|
||||||
|
OUTPUT_PATH="$(pwd)/output"
|
||||||
|
|
||||||
|
if [[ ! -e "$OUTPUT_PATH" ]]; then
|
||||||
|
mkdir "$OUTPUT_PATH"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -e "$OUTPUT_PATH" ]]; then
|
||||||
|
print_error "${OUTPUT_PATH} doesn't exist."
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rvf "$OUTPUT_PATH/*"
|
||||||
|
|
||||||
|
input "Do you have cfssl installed?(y/n): " is_cfssl_installed
|
||||||
|
|
||||||
|
CFSSLEXE=${OUTPUT_PATH}/cfssl
|
||||||
|
CFSSLJSONEXE=${OUTPUT_PATH}/cfssljson
|
||||||
|
|
||||||
|
if [[ "${is_cfssl_installed}" == "y" || "${is_cfssl_installed}" == "Y" ]]; then
|
||||||
|
input "Specify the path where the cfssl and cfssljson are placed: " TOOL_PATH
|
||||||
|
|
||||||
|
CFSSLEXE=${TOOL_PATH}/cfssl
|
||||||
|
CFSSLJSONEXE=${TOOL_PATH}/cfssljson
|
||||||
|
|
||||||
|
print_highlight "Your cfssl binary path is ${CFSSLEXE}"
|
||||||
|
|
||||||
|
if [ ! -e "$CFSSLEXE" ]; then
|
||||||
|
print_error "no cfssl found."
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -e "$CFSSLJSONEXE" ]; then
|
||||||
|
print_error "no cfssljson found."
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
# Download the cfssl for users
|
||||||
|
input "Specify your platform(darwin/linux/windows): " PLATFORM
|
||||||
|
|
||||||
|
if [ -z "$PLATFORM" ]; then
|
||||||
|
print_error "Platform must be provided."
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_highlight "Only amd64 is supported"
|
||||||
|
|
||||||
|
wget "https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssl_1.6.1_${PLATFORM}_amd64" -O "${OUTPUT_PATH}/cfssl"
|
||||||
|
chmod +x "${OUTPUT_PATH}/cfssl"
|
||||||
|
wget "https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssljson_1.6.1_${PLATFORM}_amd64" -O "${OUTPUT_PATH}/cfssljson"
|
||||||
|
chmod +x "${OUTPUT_PATH}/cfssljson"
|
||||||
|
|
||||||
|
print_highlight "Download the cfssl bundle successfully."
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd $OUTPUT_PATH
|
||||||
|
|
||||||
|
input "Give a name to the CA certificate: " CA_CERT_NAME
|
||||||
|
|
||||||
|
CA_CERT_NAME=${CA_CERT_NAME}-ca
|
||||||
|
|
||||||
|
${CFSSLEXE} print-defaults csr | ${CFSSLEXE} gencert -initca - | ${CFSSLJSONEXE} -bare ${CA_CERT_NAME}
|
||||||
|
|
||||||
|
CONFIG_CFSSL_JSON=${OUTPUT_PATH}/cfssl.json
|
||||||
|
|
||||||
|
cat <<EOF >> ${CONFIG_CFSSL_JSON}
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"default": {
|
||||||
|
"expiry": "87600h",
|
||||||
|
"usages": ["signing", "key encipherment", "server auth"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
input "Give a name to the certificate: " CERT_NAME
|
||||||
|
|
||||||
|
input "Input the hostname(example.org,127.0.0.1): " CERT_HOSTNAME
|
||||||
|
|
||||||
|
echo '{}' | ${CFSSLEXE} gencert -ca=${CA_CERT_NAME}.pem -ca-key=${CA_CERT_NAME}-key.pem -config=${CONFIG_CFSSL_JSON} \
|
||||||
|
-hostname="${CERT_HOSTNAME}" - | ${CFSSLJSONEXE} -bare ${CERT_NAME}
|
||||||
|
|
||||||
|
print_highlight "The custom TLS certificates are successfully generated in the path ${OUTPUT_PATH}."
|
1
go/.gitignore
vendored
Normal file
1
go/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/test
|
18
go/commands/git.go
Normal file
18
go/commands/git.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ocl/portainer-devtool/utils"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListBranches(workdir string) error {
|
||||||
|
cmd := exec.Command("git", "branch")
|
||||||
|
cmd.Dir = workdir
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.PrintOutput("Your current checkout branch", out)
|
||||||
|
return nil
|
||||||
|
}
|
15
go/commands/yarn.go
Normal file
15
go/commands/yarn.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ocl/portainer-devtool/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RunClient starts the portainer client
|
||||||
|
func RunPortainerClient(workdir string) error {
|
||||||
|
err := utils.RunCommandWithStdoutPipe(workdir, "yarn")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils.RunCommandWithStdoutPipe(workdir, "yarn", "start:client")
|
||||||
|
}
|
153
go/configs/config.go
Normal file
153
go/configs/config.go
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package configs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"ocl/portainer-devtool/utils"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConfigFileName string = ".devtool"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrConfigNotInitialized error = errors.New("Config file is not initialized")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
// ProjectPath is the location on your host where all dev relevant folders will be stored to
|
||||||
|
ProjectPath string
|
||||||
|
// VolumePath is where all the persisitant data will be stored
|
||||||
|
VolumePath string
|
||||||
|
// Credentials for UI login
|
||||||
|
LoginCredential LoginCredential
|
||||||
|
// key is repository name, for example, "repository-ee"
|
||||||
|
RepositoryConfig map[string]RepositoryConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginCredential stores the user credential for API request
|
||||||
|
type LoginCredential struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Address string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RepositoryConfig struct {
|
||||||
|
Name string
|
||||||
|
URL string
|
||||||
|
Directory string
|
||||||
|
Private bool
|
||||||
|
GitUsername string
|
||||||
|
GitPassword string
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetConfig() (*Config, error) {
|
||||||
|
file, err := getConfigFile(ConfigFileName)
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrConfigNotInitialized {
|
||||||
|
return initializeConfig(file)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
return getConfig(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (config *Config) Summarize() {
|
||||||
|
fmt.Printf("The project path is %s\nThe volume path is %s\n", config.ProjectPath, config.VolumePath)
|
||||||
|
if config.LoginCredential.Username != "" && config.LoginCredential.Password != "" {
|
||||||
|
fmt.Printf("Login credential [%s] is configured\n", config.LoginCredential.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(config.RepositoryConfig) > 0 {
|
||||||
|
for name := range config.RepositoryConfig {
|
||||||
|
fmt.Printf("Repository [%s] is added\n", name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("No repository is added")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initializeConfig will set up the mandatory dev information for the first time.
|
||||||
|
// such as devtool path, login credential
|
||||||
|
// The configuration also can be updated later
|
||||||
|
func initializeConfig(w io.WriteCloser) (*Config, error) {
|
||||||
|
config := &Config{}
|
||||||
|
config.ProjectPath = utils.Prompt("Specify Git Project Root Path")
|
||||||
|
|
||||||
|
// analyze all the repositories in the project root path
|
||||||
|
// add the parsed information to RepositoryConfig
|
||||||
|
config.configureRepositories()
|
||||||
|
|
||||||
|
// generate volume path automatically
|
||||||
|
config.VolumePath = utils.Prompt("Specify Volume Path")
|
||||||
|
|
||||||
|
config.configureLoginCredential()
|
||||||
|
// able to configure multiple project
|
||||||
|
// if utils.PromptConfirm("Do you want to configure the repository now?") {
|
||||||
|
// // configure repository
|
||||||
|
// }
|
||||||
|
|
||||||
|
bytes, err := json.Marshal(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.Write(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfig(f *os.File) (*Config, error) {
|
||||||
|
config := &Config{}
|
||||||
|
|
||||||
|
info, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bytes := make([]byte, info.Size())
|
||||||
|
n, err := f.Read(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
// The file exists, but it's empty file, so we need to initalize
|
||||||
|
return initializeConfig(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(bytes, &config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getConfigFile get the config file handler
|
||||||
|
func getConfigFile(name string) (*os.File, error) {
|
||||||
|
_, err := os.Stat(name)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
//create file
|
||||||
|
file, err := os.Create(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("fail to create config file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// first set up the git project path and volume path
|
||||||
|
// git credential
|
||||||
|
|
||||||
|
return file, err
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return os.OpenFile(name, os.O_RDWR, 0644)
|
||||||
|
}
|
32
go/configs/credential.go
Normal file
32
go/configs/credential.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package configs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"ocl/portainer-devtool/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (config *Config) configureLoginCredential() {
|
||||||
|
var loginCredential LoginCredential
|
||||||
|
loginCredential.Username = utils.Prompt("Set Login Credential Username(admin)")
|
||||||
|
if loginCredential.Username == "" {
|
||||||
|
loginCredential.Username = "admin"
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
loginCredential.Password = utils.Prompt("Set Login Credential Password(*****)")
|
||||||
|
if loginCredential.Password != "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.WarnPrint("Login Credential Password must be provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
loginCredential.Address = utils.Prompt("Set Login Address(127.0.0.1)")
|
||||||
|
if loginCredential.Address == "" {
|
||||||
|
loginCredential.Address = "http://127.0.0.1:9000/api/auth"
|
||||||
|
} else {
|
||||||
|
loginCredential.Address = fmt.Sprintf("http://%s:9000/api/auth", loginCredential.Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.LoginCredential = loginCredential
|
||||||
|
}
|
54
go/configs/repository.go
Normal file
54
go/configs/repository.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package configs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"log"
|
||||||
|
"ocl/portainer-devtool/utils"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (config *Config) configureRepositories() {
|
||||||
|
if config.RepositoryConfig == nil {
|
||||||
|
config.RepositoryConfig = make(map[string]RepositoryConfig)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
if !utils.PromptConfirm("Set up new repository") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
repoConfig := RepositoryConfig{}
|
||||||
|
repoConfig.Name = utils.Prompt("Name")
|
||||||
|
repoConfig.URL = utils.Prompt("URL")
|
||||||
|
repoConfig.Directory = utils.Prompt("Directory")
|
||||||
|
config.RepositoryConfig[repoConfig.Name] = repoConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.HighlightPrint("Configure repositories completed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (config *Config) generateRepositoriesBasedOnProjectPath(projectPath string) error {
|
||||||
|
|
||||||
|
filepath.WalkDir(projectPath, func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("fail to walk in the project path %s, error: %v\n", projectPath, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if utils.MatchPathLength(projectPath, path, 1) {
|
||||||
|
fmt.Println(path)
|
||||||
|
|
||||||
|
// posLastSeparator := strings.LastIndex(path, string(filepath.Separator))
|
||||||
|
// repoName := path[posLastSeparator+1:]
|
||||||
|
|
||||||
|
// repoConfig := RepositoryConfig{
|
||||||
|
// Name: repoName,
|
||||||
|
// // URL:
|
||||||
|
// }
|
||||||
|
// config.RepositoryConfig[repoName] =
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
26
go/main.go
Normal file
26
go/main.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"ocl/portainer-devtool/configs"
|
||||||
|
"ocl/portainer-devtool/tasks"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
config, err := configs.GetConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Summarize()
|
||||||
|
|
||||||
|
// Init tasks
|
||||||
|
taskItems := []tasks.Tasker{
|
||||||
|
tasks.NewGenerateJwtTokenTask(config),
|
||||||
|
tasks.NewCurlLookupTask(),
|
||||||
|
tasks.NewCodeSecurityScanTask(),
|
||||||
|
tasks.NewListDevToolCommandTask(config),
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.ListCommandMenu(taskItems, "Which repository of action do you want operate:")
|
||||||
|
}
|
5
go/repositories/actioner.go
Normal file
5
go/repositories/actioner.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
type Actioner interface {
|
||||||
|
Execute() error
|
||||||
|
}
|
71
go/repositories/ee.go
Normal file
71
go/repositories/ee.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"ocl/portainer-devtool/commands"
|
||||||
|
"ocl/portainer-devtool/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ACTION_EE_BUILD_ALL int = iota + 1
|
||||||
|
ACTION_EE_RUN_FRONTEND
|
||||||
|
ACTION_EE_RUN_BACKEND
|
||||||
|
ACTION_EE_VALIDATE_ALL
|
||||||
|
ACTION_EE_VALIDATE_FRONTEND
|
||||||
|
ACTION_EE_VALIDATE_BACKEND
|
||||||
|
ACTION_EE_RUN_UNIT_TEST_ALL
|
||||||
|
ACTION_EE_RUN_UNIT_TEST_FRONTEND
|
||||||
|
ACTION_EE_RUN_UNIT_TEST_BACKEND
|
||||||
|
)
|
||||||
|
|
||||||
|
type PortainerEE struct {
|
||||||
|
WorkDir string
|
||||||
|
FrontendDir string
|
||||||
|
BackendDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPortainerEERepository() *PortainerEE {
|
||||||
|
repo := &PortainerEE{
|
||||||
|
WorkDir: "/home/oscarzhou/source/github.com/portainer/portainer-ee",
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.HighlightPrint("Your portainer EE repository work directory is ")
|
||||||
|
fmt.Println(repo.WorkDir)
|
||||||
|
|
||||||
|
return repo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *PortainerEE) Execute() error {
|
||||||
|
err := commands.ListBranches(repo.WorkDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.PromptContinue() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
option := utils.PromptMenu(repo.listSubMenu)
|
||||||
|
switch option {
|
||||||
|
case ACTION_EE_BUILD_ALL:
|
||||||
|
|
||||||
|
case ACTION_EE_RUN_FRONTEND:
|
||||||
|
commands.RunPortainerClient(repo.WorkDir)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *PortainerEE) listSubMenu() {
|
||||||
|
utils.MenuPrint("Do you want?", `
|
||||||
|
1. Build both front-end and backend
|
||||||
|
2. Run front-end only
|
||||||
|
3. Run backend only
|
||||||
|
4. Validate both fornt-end and backend before commit
|
||||||
|
5. Validate front-end only before commit
|
||||||
|
6. Validate backend only before commit
|
||||||
|
7. Run unit tests for both front-end and backend
|
||||||
|
8. Run unit tests for front-end only
|
||||||
|
9. Run unit tests for backend only`)
|
||||||
|
}
|
16
go/tasks/build_all.go
Normal file
16
go/tasks/build_all.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import "ocl/portainer-devtool/configs"
|
||||||
|
|
||||||
|
type BuildAllTask struct {
|
||||||
|
Config *configs.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *BuildAllTask) Execute() error {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *BuildAllTask) String() string {
|
||||||
|
return "Build all"
|
||||||
|
}
|
15
go/tasks/build_backend.go
Normal file
15
go/tasks/build_backend.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import "ocl/portainer-devtool/configs"
|
||||||
|
|
||||||
|
type BuildBackendOnlyTask struct {
|
||||||
|
Config *configs.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *BuildBackendOnlyTask) Execute() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *BuildBackendOnlyTask) String() string {
|
||||||
|
return "Build backend only"
|
||||||
|
}
|
35
go/tasks/code_security_scan.go
Normal file
35
go/tasks/code_security_scan.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ocl/portainer-devtool/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CodeSecurityScanTask struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCodeSecurityScanTask() *CodeSecurityScanTask {
|
||||||
|
return &CodeSecurityScanTask{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *CodeSecurityScanTask) Execute() error {
|
||||||
|
utils.SuccessPrint(`
|
||||||
|
1. Scan client with snyk: "snyk test"
|
||||||
|
2. Scan server with snyk: "cd api && snyk test"
|
||||||
|
3. If snyk is not authenticated: "snyk auth"
|
||||||
|
4. Specify the severity threshold: "snyk test --severity-threshold=<low|medium|high|critical>"
|
||||||
|
5. Other commands with snyk: "snyk --help"
|
||||||
|
`)
|
||||||
|
|
||||||
|
utils.SuccessPrint(`
|
||||||
|
Steps to scan portainer image with Trivy:
|
||||||
|
1. Build the local image: "docker build -t oscarzhou/portainer:dev-ee -f build/linux/Dockfile ."
|
||||||
|
2. Scan with trivy: 'docker run --rm -v "/var/run/docker.sock":"/var/run/docker.sock" aquasec/trivy:latest image oscarzhou/portainer:dev-ee'
|
||||||
|
3. Other commands with trivy: 'docker run --rm -v "/var/run/docker.sock":"/var/run/docker.sock" aquasec/trivy:latest --help'
|
||||||
|
`)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *CodeSecurityScanTask) String() string {
|
||||||
|
return "Code Security Scan"
|
||||||
|
}
|
48
go/tasks/curl_lookup.go
Normal file
48
go/tasks/curl_lookup.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"ocl/portainer-devtool/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CurlLookupTask struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCurlLookupTask() *CurlLookupTask {
|
||||||
|
return &CurlLookupTask{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *CurlLookupTask) Execute() error {
|
||||||
|
var option string
|
||||||
|
utils.InputPrint("1.POST 2.GET 3.PUT 4.DELETE: ")
|
||||||
|
fmt.Scanf("%s", &option)
|
||||||
|
switch option {
|
||||||
|
case "1", "POST", "post":
|
||||||
|
utils.HighlightPrint("POST Command:")
|
||||||
|
utils.SuccessPrint("curl -d '{\"repository\":\"https://github.com/portainer/portainer-ee\",\"username\":\"oscarzhou\", \"password\":\"your PAT\"}' -H 'Content-Type: application/json' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInJvbGUiOjEsInNjb3BlIjoiZGVmYXVsdCIsImZvcmNlQ2hhbmdlUGFzc3dvcmQiOmZhbHNlLCJleHAiOjE2NjAwMzQ2MjUsImlhdCI6MTY2MDAwNTgyNX0.S0UbPO4POD9kbuWOmvO9WR6LY6v424bpGw46rlEkNs0' http://127.0.0.1:9000/api/gitops/repo/refs")
|
||||||
|
break
|
||||||
|
|
||||||
|
case "2", "GET", "get":
|
||||||
|
utils.HighlightPrint("GET Command:")
|
||||||
|
utils.SuccessPrint("curl -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInJvbGUiOjEsInNjb3BlIjoiZGVmYXVsdCIsImZvcmNlQ2hhbmdlUGFzc3dvcmQiOmZhbHNlLCJleHAiOjE2NTUxMTg2ODUsImlhdCI6MTY1NTA4OTg4NX0.mJSZomeiEpRlz36MxSsLFWpUbA0BHRXWYijsZAo1NWc' http://127.0.0.1:9000/api/users/1/gitcredentials")
|
||||||
|
break
|
||||||
|
|
||||||
|
case "3", "PUT", "put":
|
||||||
|
utils.HighlightPrint("PUT Command:")
|
||||||
|
utils.SuccessPrint(`curl -X PUT http://127.0.0.1:9000/api/users/1/gitcredentials/11 -d '{"name":"test-credential-11","username":"cred11", "password":"cred11"}' -H 'Content-Type: application/json' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInJvbGUiOjEsInNjb3BlIjoiZGVmYXVsdCIsImZvcmNlQ2hhbmdlUGFzc3dvcmQiOmZhbHNlLCJleHAiOjE2NTcwODQ5MzUsImlhdCI6MTY1NzA1NjEzNX0.kUhkhhSt4WH33Q3hYzLwsYDv1a9a2ygCi6p8MkKMbwc'`)
|
||||||
|
break
|
||||||
|
|
||||||
|
case "4", "DELETE", "delete":
|
||||||
|
utils.HighlightPrint("DELETE Command:")
|
||||||
|
utils.SuccessPrint(`curl -X DELETE http://192.168.1.109:9000/api/users/1/gitcredentials/1 -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInJvbGUiOjEsInNjb3BlIjoiZGVmYXVsdCIsImZvcmNlQ2hhbmdlUGFzc3dvcmQiOmZhbHNlLCJleHAiOjE2NTQ3NTc1NzYsImlhdCI6MTY1NDcyODc3Nn0.GlxGmL6XTTH29Ns8aRnX5qp1qBfDVF2zaPzuSmG7qUs'`)
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("No option %v\n", option)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *CurlLookupTask) String() string {
|
||||||
|
return "Lookup Curl Commands"
|
||||||
|
}
|
18
go/tasks/exit.go
Normal file
18
go/tasks/exit.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
type ExitTask struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewExitTask() *ExitTask {
|
||||||
|
return &ExitTask{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ExitTask) Execute() error {
|
||||||
|
return errors.New("exit")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ExitTask) String() string {
|
||||||
|
return "Exit"
|
||||||
|
}
|
57
go/tasks/jwt_token_gen.go
Normal file
57
go/tasks/jwt_token_gen.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"ocl/portainer-devtool/configs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GenerateJwtTokenTask struct {
|
||||||
|
Config *configs.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
type GenerateJwtTokenResponse struct {
|
||||||
|
JWT string `json:"jwt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGenerateJwtTokenTask(cfg *configs.Config) *GenerateJwtTokenTask {
|
||||||
|
return &GenerateJwtTokenTask{
|
||||||
|
Config: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *GenerateJwtTokenTask) Execute() error {
|
||||||
|
postBody, _ := json.Marshal(map[string]string{
|
||||||
|
"username": task.Config.LoginCredential.Username,
|
||||||
|
"password": task.Config.LoginCredential.Password,
|
||||||
|
})
|
||||||
|
|
||||||
|
responseBody := bytes.NewBuffer(postBody)
|
||||||
|
resp, err := http.Post(task.Config.LoginCredential.Address, "application/json", responseBody)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("http requset error: %s", err.Error())
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
//Read the response body
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse the response body: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret GenerateJwtTokenResponse
|
||||||
|
err = json.Unmarshal(body, &ret)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("jwt token is:\n%s\n", ret.JWT)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *GenerateJwtTokenTask) String() string {
|
||||||
|
return "Generate JWT Token"
|
||||||
|
}
|
31
go/tasks/list_dev_tool_cmd.go
Normal file
31
go/tasks/list_dev_tool_cmd.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ocl/portainer-devtool/configs"
|
||||||
|
"ocl/portainer-devtool/tasks/subtasks"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListDevToolCommandTask struct {
|
||||||
|
Config *configs.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewListDevToolCommandTask(cfg *configs.Config) *ListDevToolCommandTask {
|
||||||
|
return &ListDevToolCommandTask{
|
||||||
|
Config: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ListDevToolCommandTask) Execute() error {
|
||||||
|
subTaskItems := []Tasker{
|
||||||
|
subtasks.NewListVolumeSubTask(task.Config),
|
||||||
|
subtasks.NewListRepositorySubTask(task.Config),
|
||||||
|
}
|
||||||
|
|
||||||
|
ListCommandMenu(subTaskItems, "Which management commands do you want to choose:")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ListDevToolCommandTask) String() string {
|
||||||
|
return "List Dev Tool Commands"
|
||||||
|
}
|
34
go/tasks/subtasks/list_repository.go
Normal file
34
go/tasks/subtasks/list_repository.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package subtasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ocl/portainer-devtool/configs"
|
||||||
|
"ocl/portainer-devtool/utils"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListRepositorySubTask struct {
|
||||||
|
Config *configs.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewListRepositorySubTask(cfg *configs.Config) *ListRepositorySubTask {
|
||||||
|
return &ListRepositorySubTask{
|
||||||
|
Config: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ListRepositorySubTask) Execute() error {
|
||||||
|
utils.InputPrint("Which repository?")
|
||||||
|
|
||||||
|
repositoryList := []string{" "}
|
||||||
|
for _, repo := range task.Config.RepositoryConfig {
|
||||||
|
repositoryList = append(repositoryList, repo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.SuccessPrint(strings.Join(repositoryList, "\n"))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ListRepositorySubTask) String() string {
|
||||||
|
return "List Repositories"
|
||||||
|
}
|
51
go/tasks/subtasks/list_volume.go
Normal file
51
go/tasks/subtasks/list_volume.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package subtasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"ocl/portainer-devtool/configs"
|
||||||
|
"ocl/portainer-devtool/utils"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListVolumeSubTask struct {
|
||||||
|
Config *configs.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewListVolumeSubTask(cfg *configs.Config) *ListVolumeSubTask {
|
||||||
|
return &ListVolumeSubTask{
|
||||||
|
Config: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ListVolumeSubTask) Execute() error {
|
||||||
|
utils.HighlightPrint(fmt.Sprintf("Volume path: %s", task.Config.VolumePath))
|
||||||
|
|
||||||
|
volumeList := []string{" "}
|
||||||
|
filepath.WalkDir(task.Config.VolumePath, func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if path == task.Config.VolumePath {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.IsDir() {
|
||||||
|
if utils.MatchPathLength(task.Config.VolumePath, path, 1) {
|
||||||
|
volumeList = append(volumeList, d.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
utils.SuccessPrint(strings.Join(volumeList, "\n"))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *ListVolumeSubTask) String() string {
|
||||||
|
return "List Volumes"
|
||||||
|
}
|
54
go/tasks/tasker.go
Normal file
54
go/tasks/tasker.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"ocl/portainer-devtool/utils"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Tasker interface {
|
||||||
|
Execute() error
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListCommandMenu iterates task items to display them // on the screen as the menu options
|
||||||
|
func ListCommandMenu(taskItems []Tasker, menuDesp string) error {
|
||||||
|
taskItems = append(taskItems, NewExitTask())
|
||||||
|
for {
|
||||||
|
printMainMenu := func() {
|
||||||
|
taskNames := []string{}
|
||||||
|
for _, task := range taskItems {
|
||||||
|
taskNames = append(taskNames, task.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.PrintMenu(menuDesp, taskNames)
|
||||||
|
|
||||||
|
// utils.MenuPrint("Which repository or action do you want to operate:", `
|
||||||
|
// 1. Portainer EE Repository
|
||||||
|
// 2. Portainer CE Repository
|
||||||
|
// 3. Portainer Agent Repository
|
||||||
|
// 4. Others
|
||||||
|
// 5. Quit`)
|
||||||
|
}
|
||||||
|
|
||||||
|
option := utils.SelectMenuItem(printMainMenu)
|
||||||
|
|
||||||
|
index, err := strconv.Atoi(option)
|
||||||
|
if err != nil {
|
||||||
|
utils.ErrorPrint("please type the option number\n")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if index < 1 || index > len(taskItems) {
|
||||||
|
utils.ErrorPrint(fmt.Sprintf("no such option %s, please select again\n", option))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = taskItems[index-1].Execute()
|
||||||
|
if err != nil {
|
||||||
|
utils.ErrorPrint(err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
go/utils/command.go
Normal file
45
go/utils/command.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RunCommandWithStdoutPipe(workdir, progName string, args ...string) error {
|
||||||
|
cmd := exec.Command(progName, args...)
|
||||||
|
cmd.Dir = workdir
|
||||||
|
out, err := cmd.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(out)
|
||||||
|
go func() {
|
||||||
|
counter := 0
|
||||||
|
for scanner.Scan() {
|
||||||
|
if counter > 10 {
|
||||||
|
// Swallow the output
|
||||||
|
if counter%50 == 0 {
|
||||||
|
fmt.Printf("output %d lines in total.\n", counter)
|
||||||
|
}
|
||||||
|
counter++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
PrintOutput("", scanner.Bytes())
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cmd.Wait()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
18
go/utils/path.go
Normal file
18
go/utils/path.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MatchPathLength matches the length of target path separated by path separator
|
||||||
|
// to the length of base path separated by path separator plus offset
|
||||||
|
func MatchPathLength(basePath, targetPath string, offset int) bool {
|
||||||
|
basePathLength := len(strings.Split(basePath, string(filepath.Separator)))
|
||||||
|
targetPathLength := len(strings.Split(targetPath, string(filepath.Separator)))
|
||||||
|
if basePathLength+offset == targetPathLength {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
87
go/utils/print.go
Normal file
87
go/utils/print.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
colorReset string = "\033[0m"
|
||||||
|
|
||||||
|
colorRed string = "\033[31m"
|
||||||
|
colorGreen string = "\033[32m"
|
||||||
|
colorYellow string = "\033[33m"
|
||||||
|
colorBlue string = "\033[34m"
|
||||||
|
colorPurple string = "\033[35m"
|
||||||
|
colorCyan string = "\033[36m"
|
||||||
|
colorWhite string = "\033[37m"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PrintOutput(message string, output []byte) {
|
||||||
|
if message != "" {
|
||||||
|
HighlightPrint(message)
|
||||||
|
}
|
||||||
|
fmt.Println(string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
func HighlightPrint(message string) {
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println(colorBlue, message, colorReset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SuccessPrint(message string) {
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println(colorGreen, message, colorReset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorPrint(message string) {
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println(colorRed, message, colorReset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WarnPrint(message string) {
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println(colorPurple, message, colorReset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func InputPrint(message string) {
|
||||||
|
// adding \n before setting colorful output can
|
||||||
|
// remove the first space in the colorful output
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println(colorYellow, message, colorReset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintMenu(question string, taskNames []string) {
|
||||||
|
if question != "" {
|
||||||
|
InputPrint(fmt.Sprintf("[%s]", question))
|
||||||
|
}
|
||||||
|
|
||||||
|
// adding \n before setting colorful output can
|
||||||
|
// remove the first space in the colorful output
|
||||||
|
menuContent := "\n"
|
||||||
|
|
||||||
|
for i, name := range taskNames {
|
||||||
|
menuContent += fmt.Sprintf("%d. %s\n", i+1, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(colorCyan, menuContent, colorReset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MenuPrint(question, menu string) {
|
||||||
|
if question != "" {
|
||||||
|
InputPrint(fmt.Sprintf("[%s]", question))
|
||||||
|
}
|
||||||
|
|
||||||
|
// menu := `
|
||||||
|
// 1. Build Portainer EE/CE All
|
||||||
|
// 2. Build Portainer EE/CE Frontend
|
||||||
|
// 3. Build Portainer EE/CE Backend
|
||||||
|
// 4. Generate Portainer EE/CE JWT
|
||||||
|
// 5. Run Before Commit [Portainer EE/CE]
|
||||||
|
// 6. Get Portainer CE API Reference
|
||||||
|
// 7. Run Before Commit [k8s]
|
||||||
|
// 8. Build Portainer Agent
|
||||||
|
// 9. Cleanup Temporary Volume
|
||||||
|
// `
|
||||||
|
|
||||||
|
fmt.Println(colorCyan, menu, colorReset)
|
||||||
|
}
|
39
go/utils/prompt.go
Normal file
39
go/utils/prompt.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PromptContinue() bool {
|
||||||
|
ret := strings.ToLower(Prompt("Continue (y/n)"))
|
||||||
|
if ret == "y" || ret == "yes" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func PromptConfirm(question string) bool {
|
||||||
|
ret := Prompt(fmt.Sprintf("%s (y/n)?", question))
|
||||||
|
if ret == "y" || ret == "yes" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func SelectMenuItem(listMenu func()) string {
|
||||||
|
listMenu()
|
||||||
|
|
||||||
|
var option string
|
||||||
|
fmt.Scanf("%s", &option)
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
|
func Prompt(question string) string {
|
||||||
|
fmt.Printf("%s %s :%s", colorYellow, question, colorReset)
|
||||||
|
var ret string
|
||||||
|
fmt.Scanf("%s", &ret)
|
||||||
|
return ret
|
||||||
|
}
|
BIN
images/setup-openldap.gif
Normal file
BIN
images/setup-openldap.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 MiB |
32
ldap_service/README.md
Normal file
32
ldap_service/README.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# LDAP
|
||||||
|
|
||||||
|
|
||||||
|
This will setup portainer with testing image and openldap service with bootstrap data + StartTLS/TLS enabled
|
||||||
|
|
||||||
|
## 1. How to start?
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://github.com/oscarzhou/portainer-openldap-quick-setup.git && cd portainer-openldap-quick-setup
|
||||||
|
chmod +x ldap-run.sh
|
||||||
|
./ldap-run.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
![setup-openldap](/images/setup-openldap.gif)
|
||||||
|
|
||||||
|
After the output `Portainer run up successfully` shows up, it may take a while for portainer to finish initialization. You can refresh the web page every 5 seconds.
|
||||||
|
|
||||||
|
|
||||||
|
## 2. How to test?
|
||||||
|
|
||||||
|
| Key | Value |
|
||||||
|
|---|---|
|
||||||
|
| Admin Login DN | cn=admin,dc=example,dc=org |
|
||||||
|
| Admin Password | admin_pass |
|
||||||
|
| Server IP | 172.31.0.10 |
|
||||||
|
| Port over TLS (STARTTLS) | 389 |
|
||||||
|
| Port over SSL | 636 |
|
||||||
|
| CA Certificate | ./data/certs/ldap-ca.pem |
|
||||||
|
| username1 | developer |
|
||||||
|
| password1 | developer_pass |
|
||||||
|
| username2 | maintainer |
|
||||||
|
| password2 | maintainer_pass |
|
54
ldap_service/data/bootstrap.ldif
Normal file
54
ldap_service/data/bootstrap.ldif
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
dn: cn=developer,dc=example,dc=org
|
||||||
|
changetype: add
|
||||||
|
objectclass: inetOrgPerson
|
||||||
|
cn: developer
|
||||||
|
givenname: developer
|
||||||
|
sn: Developer
|
||||||
|
displayname: Developer User
|
||||||
|
mail: developer@gmail.com
|
||||||
|
uid: developer
|
||||||
|
userpassword: developer_pass
|
||||||
|
|
||||||
|
dn: cn=maintainer,dc=example,dc=org
|
||||||
|
changetype: add
|
||||||
|
objectclass: inetOrgPerson
|
||||||
|
cn: maintainer
|
||||||
|
givenname: maintainer
|
||||||
|
sn: Maintainer
|
||||||
|
displayname: Maintainer User
|
||||||
|
mail: maintainer@gmail.com
|
||||||
|
uid: maintainer
|
||||||
|
userpassword: maintainer_pass
|
||||||
|
|
||||||
|
dn: cn=admin_gh,dc=example,dc=org
|
||||||
|
changetype: add
|
||||||
|
objectclass: inetOrgPerson
|
||||||
|
cn: admin_gh
|
||||||
|
givenname: admin_gh
|
||||||
|
sn: AdminGithub
|
||||||
|
displayname: Admin Github User
|
||||||
|
mail: admin_gh@gmail.com
|
||||||
|
userpassword: admin_gh_pass
|
||||||
|
|
||||||
|
dn: ou=Groups,dc=example,dc=org
|
||||||
|
changetype: add
|
||||||
|
objectclass: organizationalUnit
|
||||||
|
ou: Groups
|
||||||
|
|
||||||
|
dn: ou=Users,dc=example,dc=org
|
||||||
|
changetype: add
|
||||||
|
objectclass: organizationalUnit
|
||||||
|
ou: Users
|
||||||
|
|
||||||
|
dn: cn=Admins,ou=Groups,dc=example,dc=org
|
||||||
|
changetype: add
|
||||||
|
cn: Admins
|
||||||
|
objectclass: groupOfUniqueNames
|
||||||
|
uniqueMember: cn=admin_gh,dc=example,dc=org
|
||||||
|
|
||||||
|
dn: cn=Maintainers,ou=Groups,dc=example,dc=org
|
||||||
|
changetype: add
|
||||||
|
cn: Maintainers
|
||||||
|
objectclass: groupOfUniqueNames
|
||||||
|
uniqueMember: cn=maintainer,dc=example,dc=org
|
||||||
|
uniqueMember: cn=developer,dc=example,dc=org
|
8
ldap_service/data/certs/cfssl.json
Normal file
8
ldap_service/data/certs/cfssl.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"default": {
|
||||||
|
"expiry": "87600h",
|
||||||
|
"usages": ["signing", "key encipherment", "server auth"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
ldap_service/data/certs/ldap-ca-key.pem
Normal file
5
ldap_service/data/certs/ldap-ca-key.pem
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIOnBF74dtgfFSwUZlY3WPHjLyedZ3YI5H5jrEu33FeX0oAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEc7tkckI5XrRSf+QeUhk4xnSJdabwgpPVY9vg+DdpFocK7i99ubI+
|
||||||
|
p5rBX9xrKGKlcEmM/Yufh32b1drdHmQFaQ==
|
||||||
|
-----END EC PRIVATE KEY-----
|
9
ldap_service/data/certs/ldap-ca.csr
Normal file
9
ldap_service/data/certs/ldap-ca.csr
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIBPTCB5AIBADBIMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcT
|
||||||
|
DVNhbiBGcmFuY2lzY28xFDASBgNVBAMTC2V4YW1wbGUubmV0MFkwEwYHKoZIzj0C
|
||||||
|
AQYIKoZIzj0DAQcDQgAEc7tkckI5XrRSf+QeUhk4xnSJdabwgpPVY9vg+DdpFocK
|
||||||
|
7i99ubI+p5rBX9xrKGKlcEmM/Yufh32b1drdHmQFaaA6MDgGCSqGSIb3DQEJDjEr
|
||||||
|
MCkwJwYDVR0RBCAwHoILZXhhbXBsZS5uZXSCD3d3dy5leGFtcGxlLm5ldDAKBggq
|
||||||
|
hkjOPQQDAgNIADBFAiEA8F+6ILOqzCzCuPB+sgUALDeud27CEu9nIM16cG710ioC
|
||||||
|
IBPdKdWivdCVG+YO/+mYb/g3Hbk5vByB9xj1bVQtt7KE
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
12
ldap_service/data/certs/ldap-ca.pem
Normal file
12
ldap_service/data/certs/ldap-ca.pem
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIB0zCCAXqgAwIBAgIUGzxUvE3E82RNYhT+eG2Hscq7ma4wCgYIKoZIzj0EAwIw
|
||||||
|
SDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNp
|
||||||
|
c2NvMRQwEgYDVQQDEwtleGFtcGxlLm5ldDAeFw0yMjA4MTEyMjM4MDBaFw0yNzA4
|
||||||
|
MTAyMjM4MDBaMEgxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMN
|
||||||
|
U2FuIEZyYW5jaXNjbzEUMBIGA1UEAxMLZXhhbXBsZS5uZXQwWTATBgcqhkjOPQIB
|
||||||
|
BggqhkjOPQMBBwNCAARzu2RyQjletFJ/5B5SGTjGdIl1pvCCk9Vj2+D4N2kWhwru
|
||||||
|
L325sj6nmsFf3GsoYqVwSYz9i5+HfZvV2t0eZAVpo0IwQDAOBgNVHQ8BAf8EBAMC
|
||||||
|
AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOBglz2vX3F2/QSfPb8CE6WgX
|
||||||
|
xowwCgYIKoZIzj0EAwIDRwAwRAIgTPJwMJ/C1AWyduH1VHateYtwSsSiG4CFof/m
|
||||||
|
e7Te0SACIDo8NbjqCxX5q7xNREx/KrWAGblLlk00Ywsqc+qZejC0
|
||||||
|
-----END CERTIFICATE-----
|
5
ldap_service/data/certs/server-key.pem
Normal file
5
ldap_service/data/certs/server-key.pem
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIIxGYodBnPD2v4PlKVfTZYkPl2kf9ckdT63NRVI8pJt8oAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEj0t/ND963wlU/FFeiwI7cSBqkOX4puNwOz/npMwVwYLuVOrY/L+s
|
||||||
|
hjPrZ32WW3lAu3NKsG7bkbDzzw76ppbY1w==
|
||||||
|
-----END EC PRIVATE KEY-----
|
8
ldap_service/data/certs/server.csr
Normal file
8
ldap_service/data/certs/server.csr
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIBADCBpwIBADAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEj0t/ND963wlU
|
||||||
|
/FFeiwI7cSBqkOX4puNwOz/npMwVwYLuVOrY/L+shjPrZ32WW3lAu3NKsG7bkbDz
|
||||||
|
zw76ppbY16BFMEMGCSqGSIb3DQEJDjE2MDQwMgYDVR0RBCswKYIQbGRhcC5leGFt
|
||||||
|
cGxlLm9yZ4IJbG9jYWxob3N0hwSsHwAKhwR/AAABMAoGCCqGSM49BAMCA0gAMEUC
|
||||||
|
IQCMPYIchbVmp1bmvT77sucgUf4fe7CGSdOVWkL3rxkTjAIgIhHivuP62hOyG43O
|
||||||
|
xWi/83L01C7MiOOsQMu6x3NEYQI=
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
13
ldap_service/data/certs/server.pem
Normal file
13
ldap_service/data/certs/server.pem
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIB+DCCAZ6gAwIBAgIUTj+0B76Ev+XH/iW7lPdo28KM884wCgYIKoZIzj0EAwIw
|
||||||
|
SDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNp
|
||||||
|
c2NvMRQwEgYDVQQDEwtleGFtcGxlLm5ldDAeFw0yMjA4MTEyMjM4MDBaFw0zMjA4
|
||||||
|
MDgyMjM4MDBaMAAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASPS380P3rfCVT8
|
||||||
|
UV6LAjtxIGqQ5fim43A7P+ekzBXBgu5U6tj8v6yGM+tnfZZbeUC7c0qwbtuRsPPP
|
||||||
|
DvqmltjXo4GtMIGqMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcD
|
||||||
|
ATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBSefdqGwGdpVarg54WkJioV315BOzAf
|
||||||
|
BgNVHSMEGDAWgBQ4GCXPa9fcXb9BJ89vwITpaBfGjDA1BgNVHREBAf8EKzApghBs
|
||||||
|
ZGFwLmV4YW1wbGUub3Jngglsb2NhbGhvc3SHBKwfAAqHBH8AAAEwCgYIKoZIzj0E
|
||||||
|
AwIDSAAwRQIgIyv9Rifo3PThZm43YJ2nEIeOVANoUHaS1eD34YfLO64CIQDOvpMk
|
||||||
|
WtM/tAn7ufxdRcN51ev6maK6yQMiu4Hj6Fk4gg==
|
||||||
|
-----END CERTIFICATE-----
|
47
ldap_service/docker-compose.yml
Normal file
47
ldap_service/docker-compose.yml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
version: '3.7'
|
||||||
|
|
||||||
|
services:
|
||||||
|
ldap_server:
|
||||||
|
image: osixia/openldap:1.5.0
|
||||||
|
container_name: ldap_server
|
||||||
|
environment:
|
||||||
|
LDAP_ADMIN_PASSWORD: admin_pass
|
||||||
|
LDAP_BASE_DN: dc=example,dc=org
|
||||||
|
LDAP_DOMAIN: example.org
|
||||||
|
LDAP_ORGANISATION: "Example Inc."
|
||||||
|
LDAP_TLS_CRT_FILENAME: server.pem
|
||||||
|
LDAP_TLS_KEY_FILENAME: server-key.pem
|
||||||
|
LDAP_TLS_CA_CRT_FILENAME: ldap-ca.pem
|
||||||
|
LDAP_TLS_VERIFY_CLIENT: try
|
||||||
|
hostname: ldap.example.org
|
||||||
|
command: --copy-service
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
ipv4_address: 172.31.0.10
|
||||||
|
ports:
|
||||||
|
- 389:389
|
||||||
|
- 636:636
|
||||||
|
volumes:
|
||||||
|
- ./data/bootstrap.ldif:/container/service/slapd/assets/config/bootstrap/ldif/50-bootstrap.ldif
|
||||||
|
- ./data/certs:/container/service/slapd/assets/certs
|
||||||
|
|
||||||
|
ldap_server_admin:
|
||||||
|
image: osixia/phpldapadmin:0.7.2
|
||||||
|
container_name: ldap_server_admin
|
||||||
|
ports:
|
||||||
|
- 8090:80
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
ipv4_address: 172.31.0.2
|
||||||
|
environment:
|
||||||
|
PHPLDAPADMIN_LDAP_HOSTS: ldap_server
|
||||||
|
PHPLDAPADMIN_HTTPS: 'false'
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
external: false
|
||||||
|
name: openldap-network
|
||||||
|
ipam:
|
||||||
|
driver: default
|
||||||
|
config:
|
||||||
|
- subnet: "172.31.0.1/16"
|
117
ldap_service/ldap-run.sh
Executable file
117
ldap_service/ldap-run.sh
Executable file
@ -0,0 +1,117 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
source ../utils/common.sh
|
||||||
|
|
||||||
|
DOCKER_COMPOSE_FILE=./docker-compose.yml
|
||||||
|
BOOTSTRAP_FILE=./data/bootstrap.ldif
|
||||||
|
CA_CERT_FILE=./data/certs/ldap-ca.pem
|
||||||
|
CERT_FILE=./data/certs/server.pem
|
||||||
|
KEY_FILE=./data/certs/server-key.pem
|
||||||
|
|
||||||
|
print_highlight "Start setup ldap service..."
|
||||||
|
|
||||||
|
docker-compose -v | grep 'docker-compose version' &> /dev/null
|
||||||
|
if [ $? != 0 ]; then
|
||||||
|
print_error "docker-compose not detected"
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_highlight "docker-compose detected" &> /dev/null
|
||||||
|
|
||||||
|
set +e
|
||||||
|
docker container ls -a | grep 'portainer_ldap' &> /dev/null
|
||||||
|
if [ $? == 0 ]; then
|
||||||
|
docker stop portainer_ldap
|
||||||
|
docker rm portainer_ldap
|
||||||
|
print_highlight "removing existing container portainer_ldap"
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker volume ls | grep 'portainer_ldap_data'
|
||||||
|
if [ $? == 0 ]; then
|
||||||
|
docker volume rm portainer_ldap_data
|
||||||
|
print_highlight "removing existing volume portainer_ldap_data"
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker container ls -a | grep 'ldap_server' &> /dev/null
|
||||||
|
if [ $? == 0 ]; then
|
||||||
|
docker stop ldap_server
|
||||||
|
docker rm ldap_server
|
||||||
|
print_highlight "removing existing container ldap_server"
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker container ls -a | grep 'ldap_server_admin' &> /dev/null
|
||||||
|
if [ $? == 0 ]; then
|
||||||
|
docker stop ldap_server_admin
|
||||||
|
docker rm ldap_server_admin
|
||||||
|
print_highlight "removing existing container ldap_server_admin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker network ls | grep 'openldap-network' &> /dev/null
|
||||||
|
if [ $? == 0 ]; then
|
||||||
|
docker network rm openldap-network
|
||||||
|
print_highlight "removing existing container openldap-network"
|
||||||
|
fi
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [[ ! -e "${DOCKER_COMPOSE_FILE}" ]]; then
|
||||||
|
print_error "${DOCKER_COMPOSE_FILE} not found"
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -e "${BOOTSTRAP_FILE}" ]]; then
|
||||||
|
print_error "${BOOTSTRAP_FILE} not found"
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -e "${CA_CERT_FILE}" ]]; then
|
||||||
|
print_error "${CA_CERT_FILE } not found"
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -e "${CERT_FILE}" ]]; then
|
||||||
|
print_error "${CERT_FILE} not found"
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -e "${KEY_FILE}" ]]; then
|
||||||
|
print_error "${KEY_FILE } not found"
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
print_highlight "Open LDAP service run up successfully."
|
||||||
|
|
||||||
|
print_highlight "Login DN(username): cn=admin,dc=example,dc=org"
|
||||||
|
print_highlight "Password: admin_pass"
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
xdg-open http://localhost:8090
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
input "Input your testing docker image(portainerci/portainer-ee:prxxx): " TEST_IMAGE
|
||||||
|
|
||||||
|
docker volume create portainer_ldap_data
|
||||||
|
|
||||||
|
docker run -d \
|
||||||
|
-p 8000:8000 \
|
||||||
|
-p 9000:9000 \
|
||||||
|
-p 9443:9443 \
|
||||||
|
--network openldap-network \
|
||||||
|
--name portainer_ldap \
|
||||||
|
--restart=always \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
-v /portainer_ldap_data:/data \
|
||||||
|
${TEST_IMAGE}
|
||||||
|
|
||||||
|
print_highlight "Portainer run up successfully."
|
||||||
|
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
xdg-open http://localhost:9000
|
||||||
|
|
183
run.sh
Executable file
183
run.sh
Executable file
@ -0,0 +1,183 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
source ./utils/common.sh
|
||||||
|
|
||||||
|
WORKDIR=/home/oscarzhou/source/github.com/portainer
|
||||||
|
GLOBAL_VOLUME=/home/oscarzhou/volumes
|
||||||
|
TRUE=0;
|
||||||
|
FALSE=1;
|
||||||
|
REPO_DIR=
|
||||||
|
REPO_VOLUME=
|
||||||
|
|
||||||
|
function debug_portainer_client() {
|
||||||
|
print_highlight "[debug portainer client]"
|
||||||
|
yarn
|
||||||
|
yarn start:client
|
||||||
|
}
|
||||||
|
|
||||||
|
function generate_portainer_jwt_token() {
|
||||||
|
print_highlight "[generate portainer jwt token]"
|
||||||
|
|
||||||
|
read -p "Username(admin):" username
|
||||||
|
if [ -z "$username" ]; then
|
||||||
|
username="admin";
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -p "Password(****):" password
|
||||||
|
read -p "Address(http://127.0.0.1:9000):" address
|
||||||
|
if [ -z "$address" ]; then
|
||||||
|
address="http://127.0.0.1:9000";
|
||||||
|
fi
|
||||||
|
|
||||||
|
payload="{\"username\":\"${username}\",\"password\":\"${password}\"}"
|
||||||
|
curl -d ${payload} -H 'Content-Type: application/json' "${address}/api/auth"
|
||||||
|
}
|
||||||
|
|
||||||
|
function list_portainer_ee_menu() {
|
||||||
|
print_highlight "Your current working directory is ${WORKDIR}/portainer-ee"
|
||||||
|
if ! prompt_continue; then
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
REPO_DIR=${WORKDIR}/portainer-ee
|
||||||
|
print_highlight "Your current volume is ${VOLUME}/portainer-ee-data"
|
||||||
|
if ! prompt_continue; then
|
||||||
|
exit;
|
||||||
|
fi
|
||||||
|
|
||||||
|
REPO_VOLUME=${VOLUME}/portainer-ee-data
|
||||||
|
|
||||||
|
PS3='Please select the action: '
|
||||||
|
OPTIONS=(
|
||||||
|
'Debug Client'
|
||||||
|
'Lint Client'
|
||||||
|
'Run Unit Test for Client'
|
||||||
|
'Before Commit'
|
||||||
|
'Build Client'
|
||||||
|
'Build Server'
|
||||||
|
'Run Unit Test for Server'
|
||||||
|
'Get Portainer CE API Reference'
|
||||||
|
'Quit'
|
||||||
|
)
|
||||||
|
|
||||||
|
select opt in "${OPTIONS[@]}"
|
||||||
|
do
|
||||||
|
case $opt in
|
||||||
|
'Debug Client')
|
||||||
|
debug_portainer_client
|
||||||
|
;;
|
||||||
|
'PortainerCE')
|
||||||
|
build_portainer_frontend
|
||||||
|
;;
|
||||||
|
'Build Portainer EE/CE Backend')
|
||||||
|
build_portainer_backend
|
||||||
|
;;
|
||||||
|
'Generate Portainer EE/CE JWT')
|
||||||
|
generate_portainer_jwt
|
||||||
|
;;
|
||||||
|
'Run Before Commit [Portainer EE/CE]')
|
||||||
|
run_before_commit
|
||||||
|
;;
|
||||||
|
'Get Portainer CE API Reference')
|
||||||
|
get_portainer_ce_api_reference
|
||||||
|
;;
|
||||||
|
'Quit')
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function code_security_scan_summary() {
|
||||||
|
echo "
|
||||||
|
1. Scan client with snyk: $(print_highlight "snyk test")
|
||||||
|
2. Scan server with snyk: $(print_highlight "cd api && snyk test")
|
||||||
|
3. If snyk is not authenticated: $(print_highlight "snyk auth")
|
||||||
|
4. Specify the severity threshold: $(print_highlight "snyk test --severity-threshold=<low|medium|high|critical>")
|
||||||
|
5. Other commands with snyk: $(print_highlight "snyk --help")
|
||||||
|
"
|
||||||
|
|
||||||
|
echo "
|
||||||
|
Steps to scan portainer image with Trivy:
|
||||||
|
1. Build the local image: $(print_highlight "docker build -t oscarzhou/portainer:dev-ee -f build/linux/Dockfile .")
|
||||||
|
2. Scan with trivy: $(print_highlight 'docker run --rm -v "/var/run/docker.sock":"/var/run/docker.sock" aquasec/trivy:latest image oscarzhou/portainer:dev-ee')
|
||||||
|
3. Other commands with trivy: $(print_highlight 'docker run --rm -v "/var/run/docker.sock":"/var/run/docker.sock" aquasec/trivy:latest --help')
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
function look_up_curl_commands() {
|
||||||
|
input "1.POST 2.GET 3.PUT 4.DELETE :" option
|
||||||
|
if [[ "${option}" == "1" ]]; then
|
||||||
|
echo "$(print_highlight "curl -d '{\"repository\":\"https://github.com/portainer/portainer-ee\",\"username\":\"oscarzhou\", \"password\":\"your PAT\"}' -H 'Content-Type: application/json' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInJvbGUiOjEsInNjb3BlIjoiZGVmYXVsdCIsImZvcmNlQ2hhbmdlUGFzc3dvcmQiOmZhbHNlLCJleHAiOjE2NjAwMzQ2MjUsImlhdCI6MTY2MDAwNTgyNX0.S0UbPO4POD9kbuWOmvO9WR6LY6v424bpGw46rlEkNs0' http://127.0.0.1:9000/api/gitops/repo/refs")"
|
||||||
|
elif [[ "${option}" == "2" ]]; then
|
||||||
|
echo "$(print_highlight "curl -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInJvbGUiOjEsInNjb3BlIjoiZGVmYXVsdCIsImZvcmNlQ2hhbmdlUGFzc3dvcmQiOmZhbHNlLCJleHAiOjE2NTUxMTg2ODUsImlhdCI6MTY1NTA4OTg4NX0.mJSZomeiEpRlz36MxSsLFWpUbA0BHRXWYijsZAo1NWc' http://127.0.0.1:9000/api/users/1/gitcredentials")"
|
||||||
|
elif [[ "${option}" == "3" ]]; then
|
||||||
|
echo "$(print_highlight "curl -X PUT http://127.0.0.1:9000/api/users/1/gitcredentials/11 -d '{"name":"test-credential-11","username":"cred11", "password":"cred11"}' -H 'Content-Type: application/json' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInJvbGUiOjEsInNjb3BlIjoiZGVmYXVsdCIsImZvcmNlQ2hhbmdlUGFzc3dvcmQiOmZhbHNlLCJleHAiOjE2NTcwODQ5MzUsImlhdCI6MTY1NzA1NjEzNX0.kUhkhhSt4WH33Q3hYzLwsYDv1a9a2ygCi6p8MkKMbwc'")"
|
||||||
|
elif [[ "${option}" == "4" ]]; then
|
||||||
|
echo "$(print_highlight "curl -X DELETE http://192.168.1.109:9000/api/users/1/gitcredentials/1 -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInJvbGUiOjEsInNjb3BlIjoiZGVmYXVsdCIsImZvcmNlQ2hhbmdlUGFzc3dvcmQiOmZhbHNlLCJleHAiOjE2NTQ3NTc1NzYsImlhdCI6MTY1NDcyODc3Nn0.GlxGmL6XTTH29Ns8aRnX5qp1qBfDVF2zaPzuSmG7qUs'")"
|
||||||
|
else
|
||||||
|
print_error "Invalid option"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function menu() {
|
||||||
|
PS3='Please select the action/repository: '
|
||||||
|
OPTIONS=(
|
||||||
|
'PortainerEE'
|
||||||
|
'PortainerCE'
|
||||||
|
'Build Portainer EE/CE Backend'
|
||||||
|
'Generate Portainer JWT Token'
|
||||||
|
'Run Before Commit [Portainer EE/CE]'
|
||||||
|
'Get Portainer CE API Reference'
|
||||||
|
'Look Up Curl Commands'
|
||||||
|
'Code Security Scan'
|
||||||
|
'Cleanup Temporary Volume'
|
||||||
|
'Quit'
|
||||||
|
)
|
||||||
|
|
||||||
|
select opt in "${OPTIONS[@]}"
|
||||||
|
do
|
||||||
|
case $opt in
|
||||||
|
'PortainerEE')
|
||||||
|
list_portainer_ee_menu
|
||||||
|
;;
|
||||||
|
'PortainerCE')
|
||||||
|
build_portainer_frontend
|
||||||
|
;;
|
||||||
|
'Build Portainer EE/CE Backend')
|
||||||
|
build_portainer_backend
|
||||||
|
;;
|
||||||
|
'Generate Portainer JWT Token')
|
||||||
|
generate_portainer_jwt
|
||||||
|
;;
|
||||||
|
'Run Before Commit [Portainer EE/CE]')
|
||||||
|
run_before_commit
|
||||||
|
;;
|
||||||
|
'Get Portainer CE API Reference')
|
||||||
|
get_portainer_ce_api_reference
|
||||||
|
;;
|
||||||
|
'Look Up Curl Commands')
|
||||||
|
look_up_curl_commands
|
||||||
|
;;
|
||||||
|
'Code Security Scan')
|
||||||
|
code_security_scan_summary
|
||||||
|
;;
|
||||||
|
'Cleanup Temporary Volume')
|
||||||
|
cleanup_temporary_volume
|
||||||
|
;;
|
||||||
|
'Quit')
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# check if the function exists (bash specific)
|
||||||
|
if [ "$#" -eq 0 ]; then
|
||||||
|
menu
|
||||||
|
else
|
||||||
|
"$@"
|
||||||
|
fi
|
31
utils/common.sh
Normal file
31
utils/common.sh
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
TRUE=0;
|
||||||
|
FALSE=1;
|
||||||
|
|
||||||
|
ERROR_COLOR='\033[0;31m';
|
||||||
|
HIGHLIGHT_COLOR='\033[0;32m';
|
||||||
|
INPUT_COLOR='\033[0;33m';
|
||||||
|
NO_COLOR='\033[0m';
|
||||||
|
|
||||||
|
|
||||||
|
function print_highlight() {
|
||||||
|
printf "${HIGHLIGHT_COLOR}$1${NO_COLOR}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
function print_error() {
|
||||||
|
printf "${ERROR_COLOR}$1${NO_COLOR}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
function input() {
|
||||||
|
read -p "$(echo -e ${INPUT_COLOR}$1 ${NO_COLOR})" $2
|
||||||
|
}
|
||||||
|
|
||||||
|
function prompt_continue() {
|
||||||
|
read -p "Continue N/(Y)?" is_continue
|
||||||
|
if [[ "${is_continue}" == "N" || "${is_continue}" == "n" ]]; then
|
||||||
|
return $FALSE;
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $TRUE;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user