Merge "Remove newline at EOF in hudson.model.UpdateCenter.xml"
diff --git a/README.rst b/README.rst
index 7dc19af..4d9b9a2 100644
--- a/README.rst
+++ b/README.rst
@@ -611,6 +611,16 @@
           ssl: false
           reply_to: reply_to@address.com
 
+Jenkins admin user email enforcement from client
+
+.. code-block:: yaml
+
+    jenkins:
+      client:
+        smtp:
+          admin_email: "My Jenkins <jenkins@myserver.com>"
+
+
 Slack plugin configuration
 
 .. code-block:: yaml
@@ -638,6 +648,18 @@
             branch: master # optional, default master
             implicit: true # optional default true
 
+Artifactory server enforcing
+
+.. code-block:: yaml
+
+    jenkins:
+      client:
+        artifactory:
+          my-artifactory-server:
+            enabled: true
+            url: https://path-to-my-library
+            credential_id: github
+
 Usage
 =====
 
diff --git a/_states/jenkins_artifactory.py b/_states/jenkins_artifactory.py
new file mode 100644
index 0000000..03a015c
--- /dev/null
+++ b/_states/jenkins_artifactory.py
@@ -0,0 +1,97 @@
+import logging
+logger = logging.getLogger(__name__)
+
+add_artifactory_groovy = u"""\
+import jenkins.model.*
+import org.jfrog.*
+import org.jfrog.hudson.*
+def inst = Jenkins.getInstance()
+def desc = inst.getDescriptor("org.jfrog.hudson.ArtifactoryBuilder")
+def server =  desc.getArtifactoryServers().find{{it -> it.name.equals("{name}")}}
+if(server &&
+   server.getName().equals("{name}") &&
+   server.getUrl().equals("{serverUrl}") &&
+   server.getDeployerCredentialsConfig().getCredentialsId().equals("{credentialsId}") &&
+   server.getResolverCredentialsConfig().getCredentialsId().equals("{credentialsId}")){{
+        print("EXISTS")
+}}else{{
+    desc.getArtifactoryServers().removeIf{{it -> it.name.equals("{name}")}}
+    def newServer = new ArtifactoryServer(
+      "{name}",
+      "{serverUrl}",
+      new CredentialsConfig("", "", "{credentialsId}"),
+      new CredentialsConfig("", "", "{credentialsId}"),
+      300,
+      false,
+      null)
+    desc.getArtifactoryServers().add(newServer)
+    desc.save()
+    print("ADDED/CHANGED")
+}}
+"""  # noqa
+
+delete_artifactory_groovy = u"""\
+def inst = Jenkins.getInstance()
+def desc = inst.getDescriptor("org.jfrog.hudson.ArtifactoryBuilder")
+if(desc.getArtifactoryServers().removeIf{{it -> it.name.equals("{name}")}}){{
+    print("REMOVED")
+}}else{{
+    print("NOT PRESENT")
+}}
+"""  # noqa
+
+
+def present(name, url, credential_id, **kwargs):
+    """
+    Jenkins artifactory present state method
+
+    :param name: artifactory server name
+    :param url: artifactory server url
+    :param credential_id: artifactory server credential id
+    :returns: salt-specified state dict
+    """
+    return _plugin_call(name, url, credential_id, add_artifactory_groovy, ["ADDED/CHANGED", "EXISTS"], **kwargs)
+
+
+def absent(name, **kwargs):
+    """
+    Jenkins artifactory present state method
+
+    :param name: artifactory server name
+    :returns: salt-specified state dict
+    """
+    return _plugin_call(name, None, None, delete_artifactory_groovy, ["REMOVED", "NOT PRESENT"], **kwargs)
+
+
+def _plugin_call(name, url, credentialsId, template, success_msgs, **kwargs):
+    test = __opts__['test']  # noqa
+    ret = {
+        'name': name,
+        'changes': {},
+        'result': False,
+        'comment': '',
+    }
+    result = False
+    if test:
+        status = success_msgs[0]
+        ret['changes'][name] = status
+        ret['comment'] = 'Jenkins artifactory server %s %s' % (
+            name, status.lower())
+    else:
+        call_result = __salt__['jenkins_common.call_groovy_script'](
+            template, {"name": name, "serverUrl": url, "credentialsId": credentialsId})
+        if call_result["code"] == 200 and call_result["msg"] in success_msgs:
+            status = call_result["msg"]
+            if status == success_msgs[0]:
+                ret['changes'][name] = status
+            ret['comment'] = 'Jenkins artifactory server %s %s' % (
+                name, status.lower())
+            result = True
+        else:
+            status = 'FAILED'
+            logger.error(
+                "Jenkins artifactory API call failure: %s", call_result["msg"])
+            ret['comment'] = 'Jenkins artifactory API call failure: %s' % (call_result[
+                "msg"])
+    ret['result'] = None if test else result
+    return ret
diff --git a/_states/jenkins_lib.py b/_states/jenkins_lib.py
index 6492623..10ef75f 100644
--- a/_states/jenkins_lib.py
+++ b/_states/jenkins_lib.py
@@ -28,8 +28,12 @@
     LibraryConfiguration library = new LibraryConfiguration("{lib_name}", retriever)
     library.setDefaultVersion("{branch}")
     library.setImplicit({implicit})
-    globalLibsDesc.get().getLibraries().removeIf{{ it.name.equals("{lib_name}")}}
-    globalLibsDesc.get().getLibraries().add(library)
+    if(globalLibsDesc.get().getLibraries().isEmpty()){{
+      globalLibsDesc.get().setLibraries([library])
+    }}else{{
+      globalLibsDesc.get().getLibraries().removeIf{{ it.name.equals("{lib_name}")}}
+      globalLibsDesc.get().getLibraries().add(library)
+    }}
     print("SUCCESS")
 }}
 """ # noqa
diff --git a/_states/jenkins_smtp.py b/_states/jenkins_smtp.py
index 48e19c4..969019c 100644
--- a/_states/jenkins_smtp.py
+++ b/_states/jenkins_smtp.py
@@ -2,25 +2,44 @@
 logger = logging.getLogger(__name__)
 
 set_smtp_groovy = """\
-def desc = Jenkins.getInstance().getDescriptor("hudson.tasks.Mailer")
-if(desc.getSmtpServer().equals("{host}") && 
-   desc.getSmtpAuthUserName().equals("{username}") &&
-   desc.getSmtpAuthPassword().toString().equals("{password}") && 
-   desc.getSmtpPort().equals("{port}") &&
-   desc.getUseSsl() == {ssl} &&
-   desc.getCharset().equals("{charset}") &&
-   (!{reply_to_exists} || desc.getReplyAddress().equals("{reply_to}"))){{
-        print("EXISTS")
-}}else{{
-    desc.setSmtpAuth("{username}", "{password}")
-    desc.setSmtpHost("{host}")
-    desc.setUseSsl({ssl})
-    desc.setSmtpPort("{port}")
-    desc.setCharset("{charset}")
-    if({reply_to_exists}){{
-        desc.setReplyToAddress("{reply_to}")
+def result = ""
+for(desc in [Jenkins.getInstance().getDescriptor("hudson.plugins.emailext.ExtendedEmailPublisher"),Jenkins.getInstance().getDescriptor("hudson.tasks.Mailer")]){{
+    if(desc.getSmtpServer().equals("{host}") &&
+       ((desc instanceof hudson.plugins.emailext.ExtendedEmailPublisherDescriptor && desc.getSmtpAuthUsername().equals("{username}")) ||
+        (desc instanceof hudson.tasks.Mailer$DescriptorImpl && desc.getSmtpAuthUserName().equals("{username}"))) &&
+       desc.getSmtpAuthPassword().toString().equals("{password}") &&
+       desc.getSmtpPort().equals("{port}") &&
+       desc.getUseSsl() == {ssl} &&
+       desc.getCharset().equals("{charset}") &&
+       (!{reply_to_exists} || desc.getReplyAddress().equals("{reply_to}"))){{
+            result = "EXISTS"
+    }}else{{
+        desc.setSmtpAuth("{username}", "{password}")
+        desc.setUseSsl({ssl})
+        if(desc instanceof hudson.plugins.emailext.ExtendedEmailPublisherDescriptor){{
+            desc.setSmtpServer("{host}")
+        }}else{{
+            desc.setSmtpHost("{host}")
+        }}
+        desc.setSmtpPort("{port}")
+        desc.setCharset("{charset}")
+        if({reply_to_exists}){{
+            desc.setReplyToAddress("{reply_to}")
+        }}
+        desc.save()
+        result = "SUCCESS"
     }}
-    desc.save()
+}}
+print(result)
+""" # noqa
+
+set_admin_email_groovy = """
+def jenkinsLocationConfiguration = JenkinsLocationConfiguration.get()
+if(jenkinsLocationConfiguration.getAdminAddress().equals("{email}")){{
+    print("EXISTS")
+}}else{{
+    jenkinsLocationConfiguration.setAdminAddress("{email}")
+    jenkinsLocationConfiguration.save()
     print("SUCCESS")
 }}
 """ # noqa
@@ -73,3 +92,41 @@
                                                                            "msg"])
     ret['result'] = None if test else result
     return ret
+
+
+def admin_email(name, email):
+    """
+    Jenkins Admin user email config state method
+
+    :param name: jenkins admin email
+    :returns: salt-specified state dict
+    """
+    test = __opts__['test']  # noqa
+    ret = {
+        'name': name,
+        'changes': {},
+        'result': False,
+        'comment': '',
+    }
+    result = False
+    if test:
+        status = "SUCCESS"
+        ret['changes'][name] = status
+        ret['comment'] = 'Jenkins admin email config %s %s' % (name, status.lower())
+    else:
+        call_result = __salt__['jenkins_common.call_groovy_script'](
+            set_admin_email_groovy, {"email": email})
+        if call_result["code"] == 200 and call_result["msg"] in ["SUCCESS", "EXISTS"]:
+            status = call_result["msg"]
+            if status == "SUCCESS":
+                ret['changes'][name] = status
+            ret['comment'] = 'Jenkins admin email config %s %s' % (name, status.lower())
+            result = True
+        else:
+            status = 'FAILED'
+            logger.error(
+                "Jenkins admin email API call failure: %s", call_result["msg"])
+            ret['comment'] = 'Jenkins admin email API call failure: %s' % (call_result[
+                                                                           "msg"])
+    ret['result'] = None if test else result
+    return ret
diff --git a/jenkins/client/_job.sls b/jenkins/client/_job.sls
index 003a11a..1a8e6df 100644
--- a/jenkins/client/_job.sls
+++ b/jenkins/client/_job.sls
@@ -20,7 +20,6 @@
   - config: {{ client.dir.jenkins_jobs_root }}/{{ job_name }}.xml
   - watch:
     - file: jenkins_job_{{ job_name }}_definition
-    - file: /etc/salt/minion.d/_jenkins.conf
 
 {%- else %}
 
@@ -33,7 +32,5 @@
 jenkins_job_{{ job_name }}_absent:
   jenkins_job.absent:
   - name: {{ job_name }}
-  - watch:
-    - file: /etc/salt/minion.d/_jenkins.conf
 
 {%- endif %}
diff --git a/jenkins/client/artifactory.sls b/jenkins/client/artifactory.sls
new file mode 100644
index 0000000..7f06e20
--- /dev/null
+++ b/jenkins/client/artifactory.sls
@@ -0,0 +1,14 @@
+{% from "jenkins/map.jinja" import client with context %}
+{% for name, artifactory in client.get('artifactory',{}).iteritems() %}
+{% if artifactory.get('enabled', True) %}
+jenkins_artifactory_server_{{ name }}:
+  jenkins_artifactory.present:
+  - name: {{ artifactory.get('name', name) }}
+  - url: {{ artifactory.get('url', '') }}
+  - credential_id: {{ artifactory.get('credential_id', '') }}
+{% else %}
+jenkins_artifactory_server_{{ name }}_disable:
+   jenkins_artifactory.absent:
+   - name: {{ artifactory.get('name', name) }}
+{% endif %}
+{% endfor %}
\ No newline at end of file
diff --git a/jenkins/client/init.sls b/jenkins/client/init.sls
index 553fe6b..d93963f 100644
--- a/jenkins/client/init.sls
+++ b/jenkins/client/init.sls
@@ -44,17 +44,15 @@
 {%- if client.approved_scripts is defined %}
   - jenkins.client.approval
 {%- endif %}
+{%- if client.artifactory is defined %}
+  - jenkins.client.artifactory
+{%- endif %}
 
 
 jenkins_client_install:
   pkg.installed:
   - names: {{ client.pkgs }}
 
-/etc/salt/minion.d/_jenkins.conf:
-  file.managed:
-  - source: salt://jenkins/files/_jenkins.conf
-  - template: jinja
-
 jenkins_client_dirs:
   file.directory:
   - names:
diff --git a/jenkins/client/smtp.sls b/jenkins/client/smtp.sls
index ee3a577..ae378eb 100644
--- a/jenkins/client/smtp.sls
+++ b/jenkins/client/smtp.sls
@@ -10,3 +10,9 @@
     - ssl: {{ client.smtp.get('ssl', '') }}
     - charset: {{ client.smtp.get('charset', '') }}
 {%- endif %}
+
+{%- if client.smtp is defined and client.smtp.admin_email is defined %}
+set_jenkins_admin_email:
+  jenkins_smtp.admin_email:
+    - email: {{ client.smtp.admin_email }}
+{%- endif %}
diff --git a/jenkins/files/_jenkins.conf b/jenkins/files/_jenkins.conf
deleted file mode 100644
index 0079d74..0000000
--- a/jenkins/files/_jenkins.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-{%- from "jenkins/map.jinja" import client with context %}
-jenkins:
-  url: "{{ client.master.get('proto', 'http') }}://{{ client.master.host }}:{{ client.master.port }}"
-  {%- if client.master.api_key is defined %}
-  api_key: {{ client.master.api_key }}
-  {%- else %}
-  user: {{ client.master.get('username', 'admin') }}
-  password: {{ client.master.get('password', 'dummy') }}
-  {%- endif %}
diff --git a/jenkins/master/service.sls b/jenkins/master/service.sls
index 8dcd8d7..b5a1215 100644
--- a/jenkins/master/service.sls
+++ b/jenkins/master/service.sls
@@ -89,8 +89,8 @@
 
 jenkins_service_running:
   cmd.wait:
-    - name: "i=0; while true; do curl -s -f http://localhost:{{ master.http.port }} >/dev/null && exit 0; [ $i -gt 60 ] && exit 1; sleep 5; done"
+    - name: "i=0; while true; do curl -s -f http://localhost:{{ master.http.port }}/login >/dev/null && exit 0; [ $i -gt 60 ] && exit 1; sleep 5; done"
     - watch:
       - service: jenkins_master_service
 
-{%- endif %}
\ No newline at end of file
+{%- endif %}
diff --git a/jenkins/meta/salt.yml b/jenkins/meta/salt.yml
new file mode 100644
index 0000000..b46fdb5
--- /dev/null
+++ b/jenkins/meta/salt.yml
@@ -0,0 +1,13 @@
+minion:
+  {%- if pillar.get('jenkins', {}).get('client') %}
+  jenkins:
+    {%- from "jenkins/map.jinja" import client with context %}
+    jenkins:
+      url: "{{ client.master.get('proto', 'http') }}://{{ client.master.host }}:{{ client.master.port }}"
+      {%- if client.master.api_key is defined %}
+      api_key: {{ client.master.api_key }}
+      {%- else %}
+      user: {{ client.master.get('username', 'admin') }}
+      password: {{ client.master.get('password', 'dummy') }}
+      {%- endif %}
+  {%- endif %}