From 00837b1379e4bf84a4cad25bcbeaf872414140ff Mon Sep 17 00:00:00 2001 From: Mikhail Naletov <36846182+okgolove@users.noreply.github.com> Date: Sat, 3 Mar 2018 20:34:27 +0200 Subject: [PATCH] Release version 0.1 (#1) Basic keywords --- .gitignore | 2 + .travis.yml | 24 +++++++++ README.md | 18 +++++++ atest/__init__.robot | 15 ++++++ atest/basic.robot | 31 +++++++++++ atest/builds.robot | 37 +++++++++++++ atest/docker/Dockerfile | 3 ++ atest/docker/set_password.groovy | 20 +++++++ atest/execute.robot | 29 +++++++++++ atest/jobs.robot | 89 ++++++++++++++++++++++++++++++++ setup.py | 25 +++++++++ src/JenkinsLibrary/__init__.py | 1 + src/JenkinsLibrary/library.py | 51 ++++++++++++++++++ src/JenkinsLibrary/server.py | 89 ++++++++++++++++++++++++++++++++ src/JenkinsLibrary/version.py | 1 + 15 files changed, 435 insertions(+) create mode 100644 .travis.yml create mode 100644 README.md create mode 100644 atest/__init__.robot create mode 100644 atest/basic.robot create mode 100644 atest/builds.robot create mode 100644 atest/docker/Dockerfile create mode 100644 atest/docker/set_password.groovy create mode 100644 atest/execute.robot create mode 100644 atest/jobs.robot create mode 100644 setup.py create mode 100644 src/JenkinsLibrary/__init__.py create mode 100644 src/JenkinsLibrary/library.py create mode 100644 src/JenkinsLibrary/server.py create mode 100644 src/JenkinsLibrary/version.py diff --git a/.gitignore b/.gitignore index 7bbc71c..a5f8c66 100644 --- a/.gitignore +++ b/.gitignore @@ -99,3 +99,5 @@ ENV/ # mypy .mypy_cache/ + +.pre-commit-config.yaml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..cab1729 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +language: python +python: + - "2.7" + - "3.3" + - "3.4" + - "3.5" + - "3.6" + +services: + - docker + +before_install: + - docker build --pull -t jenkins-test atest/docker + - docker run -p 127.0.0.1:8080:8080 -e JAVA_OPTS=-Djenkins.install.runSetupWizard=false -d jenkins-test + +install: + - pip install . + +script: + - robot --loglevel DEBUG atest/ + +notifications: + webhooks: + - https://fathomless-fjord-24024.herokuapp.com/notify diff --git a/README.md b/README.md new file mode 100644 index 0000000..41238f7 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +[![Build Status](https://travis-ci.org/okgolove/robotframework-jenkins.svg?branch=master)](https://travis-ci.org/okgolove/robotframework-jenkins) + +JenkinsLibrary for Robot Framework +============================== + +Introduction +------------ + +JenkinsLibrary is a Robot Framework test +library which helps you to involve Jenkins in your tests. + + +Installation +------------ + +Just run: + + pip install robotframework-jenkins diff --git a/atest/__init__.robot b/atest/__init__.robot new file mode 100644 index 0000000..24bbba8 --- /dev/null +++ b/atest/__init__.robot @@ -0,0 +1,15 @@ +*** Settings *** +Library JenkinsLibrary +Suite Setup Global Setup + +*** Variables *** +${url} http://127.0.0.1:8080 + +*** Keywords *** +Wait For Container + Wait Until Keyword Succeeds 2 min 5 sec\ + ... Is Jenkins Up url=${jenkins_address} + +Global Setup + Set Global Variable ${jenkins_address} ${url} + Wait For Container diff --git a/atest/basic.robot b/atest/basic.robot new file mode 100644 index 0000000..825452e --- /dev/null +++ b/atest/basic.robot @@ -0,0 +1,31 @@ +*** Settings *** +Library JenkinsLibrary + +*** Test Cases *** +Keyword Without Init + [Tags] init auth + Run Keyword And Expect Error Jenkins server was not initialized Get Jenkins Job By Name example + +Normal Init Jenkins + [Tags] init auth + Set Jenkins Server url=${jenkins_address} username=admin password=admin + +Wrong Username + [Tags] init auth + Run Keyword And Expect Error Can't connect to Jenkins: Error in request. Possibly authentication failed [401]: Invalid password/token for user: nimda\ + ... Set Jenkins Server url=${jenkins_address} username=nimda password=admin + +Wrong Password + [Tags] init auth + Run Keyword And Expect Error Can't connect to Jenkins: Error in request. Possibly authentication failed [401]: Invalid password/token for user: admin\ + ... Set Jenkins Server url=${jenkins_address} username=admin password=nimda + +Wrong URL + [Tags] init auth + Run Keyword And Expect Error Can't connect to Jenkins: Error in request: [Errno 111] Connection refused\ + ... Set Jenkins Server url=http://127.1.1.1 username=admin password=admin + +All Of These Are Wrong + [Tags] init auth + Run Keyword And Expect Error Can't connect to Jenkins: Error in request: [Errno 111] Connection refused\ + ... Set Jenkins Server url=http://127.0.0.1:11111 username=nimda password=nimda diff --git a/atest/builds.robot b/atest/builds.robot new file mode 100644 index 0000000..5fd5e00 --- /dev/null +++ b/atest/builds.robot @@ -0,0 +1,37 @@ +*** Settings *** +Library JenkinsLibrary +Suite Setup Set Jenkins Server url=${jenkins_address} username=admin password=admin + +*** Variables *** +${test_job_name} test_job + +*** Test Cases *** +Get Builds (inexistent job) + [Tags] job build + Run Keyword And Expect Error There is no specified job in Jenkins: ${test_job_name}\ + ... Get Jenkins Job Builds ${test_job_name} + +Get Builds (existent jobs, multiple builds) + [Tags] job build + [Setup] Create Job And Run Multiple Builds + [Teardown] Delete Jenkins Job ${test_job_name} + ${builds} = Get Jenkins Job Builds ${test_job_name} + Should Be True ${builds} + Should Be Equal As Integers 1 ${builds['builds'][2]['number']} + Should Be Equal As Integers 2 ${builds['builds'][1]['number']} + Should Be Equal As Integers 3 ${builds['builds'][0]['number']} + +Get Builds (existent jobs, no builds) + [Tags] job build + [Setup] Create Jenkins Job ${test_job_name} + [Teardown] Delete Jenkins Job ${test_job_name} + ${builds} = Get Jenkins Job Builds ${test_job_name} + Should Not Be True ${builds['lastBuild']} + Should Be Equal As Integers 1 ${builds['nextBuildNumber']} + +*** Keywords *** +Create Job And Run Multiple Builds + Create Jenkins Job ${test_job_name} + Start Jenkins Job ${test_job_name} + Start Jenkins Job ${test_job_name} + Start Jenkins Job ${test_job_name} diff --git a/atest/docker/Dockerfile b/atest/docker/Dockerfile new file mode 100644 index 0000000..bfe3067 --- /dev/null +++ b/atest/docker/Dockerfile @@ -0,0 +1,3 @@ +FROM jenkins/jenkins:latest + +COPY set_password.groovy /usr/share/jenkins/ref/init.groovy.d/ diff --git a/atest/docker/set_password.groovy b/atest/docker/set_password.groovy new file mode 100644 index 0000000..acb4e43 --- /dev/null +++ b/atest/docker/set_password.groovy @@ -0,0 +1,20 @@ +#!groovy +import hudson.security.* +import jenkins.model.* + +def instance = Jenkins.getInstance() +def hudsonRealm = new HudsonPrivateSecurityRealm(false) +def users = hudsonRealm.getAllUsers() +users_s = users.collect { it.toString() } +if ('admin' in users_s) { + println "Admin user already exists" +} else { + println "--> creating local admin user" + + hudsonRealm.createAccount('admin', 'admin') + instance.setSecurityRealm(hudsonRealm) + + def strategy = new FullControlOnceLoggedInAuthorizationStrategy() + instance.setAuthorizationStrategy(strategy) + instance.save() +} diff --git a/atest/execute.robot b/atest/execute.robot new file mode 100644 index 0000000..b65566e --- /dev/null +++ b/atest/execute.robot @@ -0,0 +1,29 @@ +*** Settings *** +Library Collections +Library JenkinsLibrary +Suite Setup Set Jenkins Server url=${jenkins_address} username=admin password=admin + +*** Variables *** +${test_job_name} test_job + +*** Test Cases *** +Wrong Params Type + [Tags] execute + [Setup] Create Jenkins Job ${test_job_name} + [Teardown] Delete Jenkins Job ${test_job_name} + Run Keyword And Expect Error Params must be a dictionary, not *\ + ... Start Jenkins Job ${test_job_name} test_string + +Run Existent Job Without Params + [Tags] execute + [Setup] Create Jenkins Job ${test_job_name} + [Teardown] Delete Jenkins Job ${test_job_name} + Start Jenkins Job ${test_job_name} + +Run Inexistent Job + [Tags] execute + Run Keyword And Expect Error There is no specified job in Jenkins: ${test_job_name}\ + ... Start Jenkins Job ${test_job_name} + + +# TODO: Test with params, also add functionality for methods (params) diff --git a/atest/jobs.robot b/atest/jobs.robot new file mode 100644 index 0000000..5280e1c --- /dev/null +++ b/atest/jobs.robot @@ -0,0 +1,89 @@ +*** Settings *** +Library Collections +Library JenkinsLibrary +Suite Setup Set Jenkins Server url=${jenkins_address} username=admin password=admin + +*** Variables *** +${test_job_name} test_job +${second_test_job_name} blablabla_job + +*** Test Cases *** +Get One Existent Job + [Tags] job + [Setup] Create Jenkins Job ${test_job_name} + [Teardown] Delete Jenkins Job ${test_job_name} + ${job} = Get Jenkins Job By Name ${test_job_name} + Should Be True ${job} + +Get Inexistent Job + [Tags] job + Run Keyword And Expect Error Can't find specified job: ${test_job_name}\ + ... Get Jenkins Job By Name ${test_job_name} + +Get Jobs (empty Jenkins) + [Tags] job + ${jobs} = Get Jenkins Jobs + Should Not Be True ${jobs} + +Get Jobs (multiple jobs) + [Tags] job + [Setup] Create Multiple Jobs + [Teardown] Delete Multiple Jobs + Check Multiple Jobs + +Create Existent Job + [Tags] job + [Setup] Create Jenkins Job ${test_job_name} + [Teardown] Delete Jenkins Job ${test_job_name} + Run Keyword And Expect Error Specified job already exists: ${test_job_name}\ + ... Create Jenkins Job ${test_job_name} + +Delete Inexistent Job + [Tags] job + Run Keyword And Expect Error There is no specified job in Jenkins: ${test_job_name}\ + ... Delete Jenkins Job ${test_job_name} + +Disable Inexistent Job + [Tags] job + Run Keyword And Expect Error There is no specified job in Jenkins: ${test_job_name}\ + ... Disable Jenkins Job ${test_job_name} + +Enable Inexistent Job + [Tags] job + Run Keyword And Expect Error There is no specified job in Jenkins: ${test_job_name}\ + ... Disable Jenkins Job ${test_job_name} + +Enable Disabled Job + [Tags] job + [Setup] Create And Disable Job + [Teardown] Delete Jenkins Job ${test_job_name} + Enable Jenkins Job ${test_job_name} + +Disable Enabled Job + [Tags] job + [Setup] Create Jenkins Job ${test_job_name} + [Teardown] Delete Jenkins Job ${test_job_name} + Disable Jenkins Job ${test_job_name} + +*** Keywords *** +Create And Disable Job + Create Jenkins Job ${test_job_name} + Disable Jenkins Job ${test_job_name} + +Create Multiple Jobs + Create Jenkins Job ${test_job_name} + Create Jenkins Job ${second_test_job_name} + +Delete Multiple Jobs + Delete Jenkins Job ${test_job_name} + Delete Jenkins Job ${second_test_job_name} + +Check Multiple Jobs + ${jobs} = Get Jenkins Jobs + Should Be True ${jobs} + ${second_job} = Get From List ${jobs} 0 + Should Be Equal ${second_job['name']} ${second_test_job_name} + Should Be Equal ${second_job['url']} ${jenkins_address}/job/${second_test_job_name}/ + ${first_job} = Get From List ${jobs} 1 + Should Be Equal ${first_job['name']} ${test_job_name} + Should Be Equal ${first_job['url']} ${jenkins_address}/job/${test_job_name}/ diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..23f869e --- /dev/null +++ b/setup.py @@ -0,0 +1,25 @@ +from os.path import join, dirname +from setuptools import setup + +exec(open(join(dirname(__file__), + 'src', 'JenkinsLibrary', 'version.py')).read()) +PACKAGE = 'robotframework-jenkins' +DESCRIPTION = '''Library for Robot Framework for Jenkins interaction''' +REQUIREMENTS = [ + 'python-jenkins==0.4.15', + 'robotframework==3.0.2', + 'requests==2.18.4' +] + +setup( + name=PACKAGE, + package_dir={'': 'src'}, + packages=['JenkinsLibrary'], + version=VERSION, + description=DESCRIPTION, + author='Mikhail Naletov', + author_email='admin@markeloff.net', + url='https://github.com/okgolove/robotframework-jenkins', + keywords=['jenkins', 'robotframework', 'robot', 'testing'], + install_requires=REQUIREMENTS +) diff --git a/src/JenkinsLibrary/__init__.py b/src/JenkinsLibrary/__init__.py new file mode 100644 index 0000000..b6dc913 --- /dev/null +++ b/src/JenkinsLibrary/__init__.py @@ -0,0 +1 @@ +from .library import JenkinsLibrary diff --git a/src/JenkinsLibrary/library.py b/src/JenkinsLibrary/library.py new file mode 100644 index 0000000..82171bf --- /dev/null +++ b/src/JenkinsLibrary/library.py @@ -0,0 +1,51 @@ +import requests +from .server import Server +from .version import VERSION + +__version__ = VERSION + + +class JenkinsLibrary(object): + ROBOT_LIBRARY_SCOPE = 'GLOBAL' + ROBOT_LIBRARY_VERSION = __version__ + + def __init__(self): + self.jenkins = Server() + + def set_jenkins_server(self, url, username, password): + self.jenkins.initialize(url=url, username=username, password=password) + + def is_jenkins_up(self, url): + try: + r = requests.get("{0}/login".format(url)) + except requests.ConnectionError: + raise RuntimeError('Jenkins server {0} is not available') + else: + if r.status_code != 200: + raise RuntimeError( + 'Jenkins server {0} is not available, status code:{1}'. + format(url, r.status_code)) + + def get_jenkins_jobs(self): + return self.jenkins.get_jobs() + + def get_jenkins_job_by_name(self, name): + return self.jenkins.get_job(name) + + def create_jenkins_job(self, name): + self.jenkins.create_job(name) + + def delete_jenkins_job(self, name): + self.jenkins.delete_job(name) + + def disable_jenkins_job(self, name): + self.jenkins.disable_job(name) + + def enable_jenkins_job(self, name): + self.jenkins.enable_job(name) + + def start_jenkins_job(self, name, params={}): + self.jenkins.build_job(name, params) + + def get_jenkins_job_builds(self, name): + return self.jenkins.get_builds(name) diff --git a/src/JenkinsLibrary/server.py b/src/JenkinsLibrary/server.py new file mode 100644 index 0000000..b50bce8 --- /dev/null +++ b/src/JenkinsLibrary/server.py @@ -0,0 +1,89 @@ +import jenkins + + +def is_server_initialized(func): + def _decorator(self, *args, **kwargs): + if not self.initialized: + raise RuntimeError('Jenkins server was not initialized') + return func(self, *args, **kwargs) + return _decorator + + +class Server(object): + def __init__(self): + self.initialized = False + + def initialize(self, url, username, password): + self.server = jenkins.Jenkins(url, username, password) + try: + self.server.get_whoami() + except jenkins.JenkinsException as e: + raise RuntimeError( + 'Can\'t connect to Jenkins: {0}'.format(e)) + else: + self.initialized = True + + @is_server_initialized + def get_jobs(self): + return self.server.get_jobs() + + @is_server_initialized + def get_job(self, name): + try: + job = self.server.get_job_info(name) + except jenkins.NotFoundException: + raise RuntimeError('Can\'t find specified job: {0}'.format(name)) + return job + + @is_server_initialized + def create_job(self, name): + try: + self.server.create_job(name, jenkins.EMPTY_CONFIG_XML) + except jenkins.JenkinsException: + raise RuntimeError( + 'Specified job already exists: {0}'.format(name)) + + @is_server_initialized + def delete_job(self, name): + try: + self.server.delete_job(name) + except jenkins.NotFoundException: + raise RuntimeError( + 'There is no specified job in Jenkins: {0}'.format(name)) + + @is_server_initialized + def disable_job(self, name): + try: + self.server.disable_job(name) + except jenkins.NotFoundException: + raise RuntimeError( + 'There is no specified job in Jenkins: {0}'.format(name)) + + @is_server_initialized + def enable_job(self, name): + try: + self.server.enable_job(name) + except jenkins.NotFoundException: + raise RuntimeError( + 'There is no specified job in Jenkins: {0}'.format(name)) + + @is_server_initialized + def build_job(self, name, params={}): + if not isinstance(params, dict): + raise RuntimeError('Params must be a dictionary, not {0}'.format( + type(params).__name__)) + try: + self.server.build_job(name, params) + except jenkins.NotFoundException: + raise RuntimeError( + 'There is no specified job in Jenkins: {0}'.format(name)) + # TODO: return build number + + @is_server_initialized + def get_builds(self, name): + try: + builds = self.server.get_job_info(name) + except jenkins.NotFoundException: + raise RuntimeError( + 'There is no specified job in Jenkins: {0}'.format(name)) + return builds diff --git a/src/JenkinsLibrary/version.py b/src/JenkinsLibrary/version.py new file mode 100644 index 0000000..d0bd79f --- /dev/null +++ b/src/JenkinsLibrary/version.py @@ -0,0 +1 @@ +VERSION = '0.1'