Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
ericwyles committed Oct 1, 2024
1 parent 36d83cb commit 24c06d0
Show file tree
Hide file tree
Showing 2 changed files with 236 additions and 87 deletions.
287 changes: 226 additions & 61 deletions charts/settings/templates/bot-accounts-job.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,70 +15,29 @@ spec:
- name: gitlab-settings-botaccounts
image: "{{ .Values.global.kubectl.image.repository }}:{{ .Values.global.kubectl.image.tag }}"
command: ["/bin/bash", "-c"]

# A handy command for testing this locally outside of a deploy lifecycle:
# helm template charts/settings --show-only templates/bot-accounts-job.yaml | yq -r '.spec.template.spec.containers[0].args[0]' | sh
args:
- |
create_gitlab_user_token() {
# Input parameters
username="$1"
scopes="$2" # Scopes as a comma-separated list
secretName="$3"
secretNamespace="$4"
secretKeyName="$5"
# Function for logging messages with timestamp in UTC in parentheses
log_message() {
echo "$(date -u +'%Y-%m-%d %H:%M:%S') (UTC) $1"
}
log_message "Making bot account for $username"
# Combining all kubectl exec calls into a single call
user_info=$(kubectl exec -n gitlab deployment/gitlab-toolbox -- bash -c "gitlab-rails runner -e production \"
# Step 1: Check if the user already exists
user = User.find_by_username('$username');
if user.present?
puts 'User already exists'
puts 'User details:'
puts 'Username: ' + user.username
puts 'Email: ' + user.email
puts 'Name: ' + user.name
puts 'Active: ' + user.state # Whether the user is active or blocked
puts 'Created at: ' + user.created_at.to_s # Log the created date
exit 1
end
# Step 2: Create the user account if it doesn't exist
password = SecureRandom.alphanumeric(16)
user = User.new(username: '$username', email: '[email protected]', name: '$username', password: password);
user.assign_personal_namespace(Organizations::Organization.default_organization);
user.skip_confirmation! # Skip email confirmation
user.save!
# Step 3: Generate a personal access token (PAT) for the user with multiple scopes
scopes = '$scopes'.split(',').map(&:strip).map(&:to_sym) # Properly split and convert scopes to symbols
token = user.personal_access_tokens.create(
scopes: scopes, # Pass scopes as an array of symbols
name: 'Bot Account $username Access Token',
expires_at: 365.days.from_now
);
token.save!
puts token.token # Only print the token to stdout
\"")
# If the user already exists, the script will exit early and return additional details
if echo "$user_info" | grep -q "User already exists"; then
log_message "$user_info"
log_message "User '$username' already exists."
elif [ -z "$user_info" ]; then
log_message "Failed to generate personal access token for '$username'."
return 1
fi
# Global variable to store the generated PAT for each account
generated_pat=""
# Function for logging messages with timestamp in UTC in parentheses
log_message() {
echo "$(date -u +'%Y-%m-%d %H:%M:%S') (UTC) $1"
}
create_kubernetes_secret() {
local secretName=$1
local secretNamespace=$2
local secretKeyName=$3
local generated_pat=$4
# Step 4: Create Kubernetes secret with the generated PAT
kubectl create secret generic "$secretName" \
--namespace="$secretNamespace" \
--from-literal="$secretKeyName=$user_info"
--from-literal="$secretKeyName=$generated_pat"
if [ $? -eq 0 ]; then
log_message "Kubernetes secret '$secretName' created successfully in namespace '$secretNamespace'."
Expand All @@ -88,13 +47,219 @@ spec:
fi
}
create_gitlab_user_account() {
# Input parameters
local username="$1"
local scope_list="$2" # Scopes as a comma-separated list
local email="[email protected]"
local name=$username
# Check if the user already exists
local user_exists
user_exists=$(curl --silent --header "PRIVATE-TOKEN: $TOKEN" "https://gitlab.uds.dev/api/v4/users?username=$username")
if echo "$user_exists" | grep -q '"id":'; then
echo "User already exists"
echo "User details: $user_exists"
return 1
fi
# Generate a random password
local password
password=$(openssl rand -base64 16)
# Create the user if it doesn't exist
user_response=$(curl --silent --request POST "https://gitlab.uds.dev/api/v4/users" \
--header "PRIVATE-TOKEN: $TOKEN" \
--header "Content-Type: application/json" \
--data "{
\"email\": \"$email\",
\"username\": \"$username\",
\"name\": \"$name\",
\"password\": \"$password\",
\"skip_confirmation\": true
}")
echo "response is: $user_response"
# Check if user creation was successful
if [ $? -ne 0 ]; then
echo "Error: Failed to create the user."
return 1
fi
# Extract the new user's ID from the response
user_id=$(echo "$user_response" | jq -r '.id')
# Check if the user ID is valid
if [ "$user_id" == "null" ] || [ -z "$user_id" ]; then
echo "Error: Failed to retrieve the user ID."
return 1
fi
echo "User created with ID: $user_id"
# Convert the comma-delimited list into separate --data fields for the request
local scope_data=""
IFS=',' read -ra scopes <<< "$scope_list"
for scope in "${scopes[@]}"; do
scope_data+="&scopes[]=$scope"
done
# Print scope data for debugging
echo "Scopes for PAT: $scope_data"
# Create a Personal Access Token (PAT) for the new user with the specified scopes
pat_response=$(curl --header "PRIVATE-TOKEN: $TOKEN" \
--data "name=Service Account Token$scope_data" \
--request POST "https://gitlab.uds.dev/api/v4/users/$user_id/personal_access_tokens")
echo "pat response is $pat_response"
# Check if token creation was successful
if [ $? -ne 0 ]; then
echo "Error: Failed to create the Personal Access Token."
return 1
fi
# Extract the generated token from the response
generated_pat=$(echo "$pat_response" | jq -r '.token')
# Check if token is returned
if [ "$generated_pat" == "null" ] || [ -z "$generated_pat" ]; then
echo "Error: Failed to retrieve the generated token."
return 1
fi
}
# Function to create a GitLab service account and set the PAT in the global variable
create_gitlab_service_account() {
local username="$1"
local name="$2"
local scope_list="$3"
# Convert the comma-delimited list into separate --data fields for the request
local scope_data=""
IFS=',' read -ra scopes <<< "$scope_list"
for scope in "${scopes[@]}"; do
scope_data+="&scopes[]=$scope"
done
# Print scope data for debugging
echo "Scopes for PAT: $scope_data"
echo "Creating service account with username: $username and name: $name"
# Create the service account user
user_response=$(curl --header "PRIVATE-TOKEN: $TOKEN" \
--data "username=$username&name=$name" \
--request POST "https://gitlab.uds.dev/api/v4/service_accounts")
echo "response is: $user_response"
# Check if user creation was successful
if [ $? -ne 0 ]; then
echo "Error: Failed to create the service account user."
return 1
fi
# Extract the new user's ID from the response
user_id=$(echo "$user_response" | jq -r '.id')
# Check if the user ID is valid
if [ "$user_id" == "null" ] || [ -z "$user_id" ]; then
echo "Error: Failed to retrieve the service account user ID."
return 1
fi
echo "Service account user created with ID: $user_id"
# Create a Personal Access Token (PAT) for the new user with the specified scopes
pat_response=$(curl --header "PRIVATE-TOKEN: $TOKEN" \
--data "name=Service Account Token$scope_data" \
--request POST "https://gitlab.uds.dev/api/v4/users/$user_id/personal_access_tokens")
# Check if token creation was successful
if [ $? -ne 0 ]; then
echo "Error: Failed to create the Personal Access Token."
return 1
fi
# Extract the generated token from the response
generated_pat=$(echo "$pat_response" | jq -r '.token')
# Check if token is returned
if [ "$generated_pat" == "null" ] || [ -z "$generated_pat" ]; then
echo "Error: Failed to retrieve the generated token."
return 1
fi
}
# Generate and capture a GitLab token from the GitLab Toolbox Rails Console for the root user
TOKEN=$(kubectl exec -n gitlab deployment/gitlab-toolbox -- \
gitlab-rails runner -e production \
"token = User.find_by_username('root').personal_access_tokens.create(scopes: ['api', 'admin_mode'], name: 'Bot Accounts API Token', expires_at: 1.days.from_now); token.save!; puts token.token" | tail -n 1)
echo "root token is $TOKEN"
response=$(curl --header "PRIVATE-TOKEN: $TOKEN" "https://gitlab.uds.dev/api/v4/license")
# Check if the request was successful
if [ $? -ne 0 ]; then
echo "Error: Failed to make request to GitLab API."
exit 1
fi
echo "license response is $response"
# Parse and extract the plan information using jq
plan=$(echo "$response" | jq -r '.plan')
service_accounts="false"
# Check if plan is found and proceed based on the plan type
if [ "$plan" != "null" ]; then
echo "GitLab Plan: $plan"
if [ "$plan" == "ultimate" ] || [ "$plan" == "premium" ]; then
echo "Plan is $plan, creating GitLab service accounts..."
service_accounts="true"
else
echo "No license plan information available. Creating Gitlab user accounts..."
fi
fi
service_accounts="false" #todo @ewyles - take this back out, just here to force other path for local testin
# Track failed accounts in a string
failed_accounts=""
{{- range .Values.botAccounts }}
create_gitlab_user_token "{{ .username }}" "{{ .scopes | join "," }}" "{{ .secret.name }}" "{{ .secret.namespace }}" "{{ .secret.keyName }}"
if [ $? -ne 0 ]; then
failed_accounts="$failed_accounts {{ .username }}"
if [ "$service_accounts" == "true" ]; then
echo "Creating service account [{{ .username }}]..."
# Call the function to create the service account and set the PAT
create_gitlab_service_account "{{ .username }}" "{{ .username }}" "{{ .scopes | join "," }}"
if [ $? -ne 0 ]; then
failed_accounts+=" {{ .username }}"
else
create_kubernetes_secret "{{ .secret.name }}" "{{ .secret.namespace }}" "{{ .secret.keyName }}" "$generated_pat"
if [ $? -ne 0 ]; then
log_message "Failed to create Kubernetes secret for account '$account'."
failed_accounts+=" {{ .username }}"
fi
fi
else
echo "Creating user account [{{ .username }}]..."
create_gitlab_user_account "{{ .username }}" "{{ .scopes | join "," }}"
if [ $? -ne 0 ]; then
failed_accounts+=" {{ .username }}"
else
create_kubernetes_secret "{{ .secret.name }}" "{{ .secret.namespace }}" "{{ .secret.keyName }}" "$generated_pat"
if [ $? -ne 0 ]; then
log_message "Failed to create Kubernetes secret for account '$account'."
failed_accounts+=" {{ .username }}"
fi
fi
fi
{{- end }}
Expand Down
36 changes: 10 additions & 26 deletions charts/settings/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,16 @@ global:
repository: registry.gitlab.com/gitlab-org/build/cng/kubectl
tag: v17.2.4

botAccounts: {}
# - username: renovatebot
# scopes:
# - api
# - read_repository
# - write_repository
# secret:
# name: gitlab-renovatebot
# namespace: renovate
# keyName: TOKEN
# - username: renovatebot
# scopes:
# - api
# - read_repository
# - write_repository
# secret:
# name: gitlab-renovatebot
# namespace: renovate
# keyName: TOKEN
# - username: anotherbot
# scopes:
# - api
# secret:
# name: gitlab-otherbotbot
# namespace: otherbot
# keyName: TOKEN
botAccounts:
- username: eric16
scopes:
- api
- read_repository
- write_repository
secret:
name: gitlab-eric16
namespace: renovate
keyName: TOKEN

settingsJob:
enabled: true
Expand Down

0 comments on commit 24c06d0

Please sign in to comment.