I enjoy using Gitlab for internal lab environments as it has a robust api and can be automated using things like terraform. One of things I normally use it for is creating represntaive environments with things like hooks enabled. This might looks like an example CI system thats fully built , and having a student have precreated content. However to create content in gitlab out of the box , or run something like git push --mirror you have to disable the branch protection.

Versions tested
Software Version OS
Gitlab 13.8 AWS Lambda
Python 3 AWS Lambda


#!/usr/bin/env python    
# Usage: GITLAB_PASSWORD=thepassword ./disable_branch_protection.py    

import gitlab    
import requests    
import json    
import os    
import requests    

GITLAB_GROUP   = 'homeops-tech'    
GITLAB_ADMIN   = 'root'    
GITLAB_API_URL = 'https://gitlab.homeops.tech:443'    

def get_gitlab_token(username,password):    
  session = requests.Session()    
  session.verify = '/etc/gitlab/ssl/gitlab.homeops.tech.crt'    
  session.keep_alive = True    
  # https://docs.gitlab.com/ce/api/oauth2.html#resource-owner-password-credential    
  url = 'https://gitlab.homeops.tech/oauth/token'    
  payload = {'username': username,    
             'password': password,    
             'grant_type': 'password'    
  r = session.post(url, data=payload, verify = '/etc/gitlab/ssl/gitlab.homeops.tech.crt')    
  oauth = json.loads(r.text)    
  api_token = oauth['access_token']    
  return api_token    

# Python lib doesn't support this after key creation
# https://docs.gitlab.com/ee/api/deploy_keys.html#enable-a-deploy-key
# This allows students to push to this repo with deploy key
def update_ssh_key():
  project = gl.projects.get('homeops-tech/control-repo')
  url = '%s/api/v4/projects/%s' % (GITLAB_API_URL,project.id)
  payload = {'default_branch': 'master'}
  headers = {'Authorization': 'Bearer %s' % api_token }
  r = requests.put(url, data=payload, verify=False, headers=headers)
  if r.status_code == 200:
    print "\tUpdated Deploy Key push access"
    print "\tUnable to update deploy key access"

api_token = get_gitlab_token(GITLAB_ADMIN,os.environ['GITLAB_PASSWORD'])    
gl = gitlab.Gitlab(GITLAB_API_URL, oauth_token=api_token,ssl_verify=False)    
project = gl.projects.get('homeops-tech/control-repo')    

branch = project.branches.get('master')    

This script will use the local SSL certificate to validate the connection. You can pusht the password via

GITLAB_PASSWORD=thepassword ./disable_branch_protection.py

The Python module supports many other options for automating common tasks

Working with Branches

def create_student_branches(n):
    branch = project.branches.create({'branch': "student%s" % n,
                                    'ref': 'production'})
  except gitlab.exceptions.GitlabCreateError:
    print "\tstudent%s branch already exists" % n

def protect_branch():
  branch = project.branches.get('production')

This example code can create a branch and protect it.

Working with Users

Add a user to the developer group.

# Add the users to the puppet group as a developer
def add_students_to_group(n):
    student = gl.users.list(username="student%s" % n)[0]
    member = group.members.create({'user_id': student.id,
                                 'access_level': gitlab.DEVELOPER_ACCESS})
  except gitlab.exceptions.GitlabCreateError:
     print "\tstudent%s already a member of group" % n

Create a user

# Create the student accounts
def create_students(n):
    user = gl.users.create({'email': "student%s@homeops.tech" % n,
                          'password': 'learn',
                          'username': "student%s" % n,
                          'skip_confirmation': True,
                          'name': "student %s" % n})
  except gitlab.exceptions.GitlabCreateError:
    print "\tstudent%s already created" % n
    # Skip the confirmation if user already created
    student = gl.users.list(username="student%s" % n)[0]
    student.skip_confirmation = True

Create a repo

def create_module_repo(n):
  student = gl.users.list(username="student%s" % n)[0]
    user_project = student.projects.create({'name': 'time',
      'description': 'Example Puppet Module',
  except gitlab.exceptions.GitlabCreateError:
    print "\t%s already has a time module created" % student.name
    user_project  = student.projects.list(name=MODULE_REPO)[0]

Creating a new project

You can create projects and enable a deploy key for them

api_token = get_gitlab_token(GITLAB_ADMIN,os.environ['GITLAB_PASSWORD'])
gl = gitlab.Gitlab(GITLAB_API_URL, oauth_token=api_token,ssl_verify=False)
project = gl.projects.get('homeops-tech/control-repo')
key = project.keys.create({'title': 'Student SSH Key',
                           'key': open('/root/.ssh/id_rsa.pub').read(),
                           'can_push': True,})

Create a webhook for a project

def add_project_hook(module_name):
  project = gl.projects.get('homeops-tech/%s' % module_name)
  project.hooks.create({'url': 'https://puppet.homeops.tech:8170/code-manager/v1/webhook?type=gitlab&token=%s' % (rbac_token),
    'push_events': 1, 'enable_ssl_verification': False})

This is an exmple of using Gitlab to configure a webhook for a given module repo. This example is using code manager with Puppet Enterprise You can retreive a RBAC token with the following example code:

def get_rbac_token():
  data = json.dumps({'login': 'admin',
                     'password': 'defaultpassword',
                     'label' : 'gitlab_webhook',
                     'description' : 'This is the token used for the Gitlab Webhooks',
                     'lifetime': '0'})
  r = requests.post('https://puppet.homeops.tech:4433/rbac-api/v1/auth/token',
  return json.loads(r.text)['token']

This example calls out to the RBAC API in puppet enterprise to get the Gitlab Token for code manager.

Import a project

Gitlab has the ability to export a repo with all its metadata. You can then import this template via a tar.gz. file.

output = gl.projects.import_project(open('/tmp/export.tgz', 'rb'), 'control-repo',group.id)
# Get a ProjectImport object to track the import status
project_import = gl.projects.get(output['id'], lazy=True).imports.get()
while project_import.import_status != 'finished':