You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2019/04/16 15:42:11 UTC

[sling-org-apache-sling-committer-cli] 15/21: SLING-8311 - Investigate creating a Sling CLI tool for development task automation

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-committer-cli.git

commit 95330f1312369738740edca534816d70e763ccdc
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Thu Mar 28 17:41:06 2019 +0100

    SLING-8311 - Investigate creating a Sling CLI tool for development task automation
    
    * added support for updating a release in the Apache Reporter System
    * added a service for extracting the user's credentials from the environment
    * switched Docker image to the openjdk11 provided by azul
    * used jlink to create a minimal JRE
---
 Dockerfile                                         |  10 +-
 pom.xml                                            |  13 ++-
 src/main/features/app.json                         |  14 +--
 .../org/apache/sling/cli/impl/Credentials.java     |  38 +++++++
 .../apache/sling/cli/impl/CredentialsService.java  |  55 +++++++++
 .../cli/impl/nexus/StagingRepositoryFinder.java    |  32 ++----
 .../sling/cli/impl/people/MembersFinder.java       |  17 +--
 .../cli/impl/release/UpdateReporterCommand.java    | 124 +++++++++++++++++++++
 src/main/resources/scripts/launcher.sh             |  24 ++--
 9 files changed, 268 insertions(+), 59 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index c29cd1e..ca438e9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -9,12 +9,16 @@
 # either express or implied. See the License for the specific language governing permissions
 # and limitations under the License.
 # ----------------------------------------------------------------------------------------
-
-FROM openjdk:8-jre-alpine
+FROM azul/zulu-openjdk-alpine:11 as builder
 MAINTAINER dev@sling.apache.org
+RUN jlink --add-modules java.logging,java.naming,java.xml,java.security.jgss,java.sql,jdk.crypto.ec \
+          --output /opt/jre --strip-debug --compress=2 --no-header-files --no-man-pages
+
+FROM alpine
+COPY --from=builder /opt/jre /opt/jre
 
 # Generate class data sharing
-RUN java -Xshare:dump
+RUN /opt/jre/bin/java -Xshare:dump
 
 # escaping required to properly handle arguments with spaces
 ENTRYPOINT ["/usr/share/sling-cli/bin/launcher.sh"]
diff --git a/pom.xml b/pom.xml
index 1f717d7..b27394e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,8 +16,8 @@
 
     <parent>
         <groupId>org.apache.sling</groupId>
-        <artifactId>sling</artifactId>
-        <version>34</version>
+        <artifactId>sling-bundle-parent</artifactId>
+        <version>35-SNAPSHOT</version>
         <relativePath />
     </parent>
 
@@ -28,6 +28,7 @@
 
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <sling.java.version>11</sling.java.version>
     </properties>
 
     <build>
@@ -123,6 +124,14 @@
                     </buildArgs>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>biz.aQute.bnd</groupId>
+                <artifactId>bnd-baseline-maven-plugin</artifactId>
+                <configuration>
+                    <!-- We don't export an API, so it should be safe to skip baseline. -->
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 
diff --git a/src/main/features/app.json b/src/main/features/app.json
index 4d310c4..5fb7cd2 100644
--- a/src/main/features/app.json
+++ b/src/main/features/app.json
@@ -1,9 +1,5 @@
 {
 	"id": "${project.groupId}:${project.artifactId}:slingfeature:app:${project.version}",
-	"variables": {
-    	"asf.username":"change-me",
-    	"asf.password": "change-me"
-    },
 	"bundles": [
 		{
 			"id": "${project.groupId}:${project.artifactId}:${project.version}",
@@ -69,11 +65,5 @@
 			"id": "org.apache.servicemix.bundles:org.apache.servicemix.bundles.jsch:0.1.55_1",
 			"start-level": "3"
 		}
-	],
-	"configurations": {
-		"org.apache.sling.cli.impl.nexus.StagingRepositoryFinder": {
-			"username": "${asf.username}",
-			"password": "${asf.password}"
-		}
-	}
-}
\ No newline at end of file
+	]
+}
diff --git a/src/main/java/org/apache/sling/cli/impl/Credentials.java b/src/main/java/org/apache/sling/cli/impl/Credentials.java
new file mode 100644
index 0000000..2df5a32
--- /dev/null
+++ b/src/main/java/org/apache/sling/cli/impl/Credentials.java
@@ -0,0 +1,38 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.cli.impl;
+
+public class Credentials {
+
+    private final String user;
+    private final String password;
+
+    public Credentials(String user, String password) {
+        this.user = user;
+        this.password = password;
+    }
+
+    public String getUsername() {
+        return user;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+}
diff --git a/src/main/java/org/apache/sling/cli/impl/CredentialsService.java b/src/main/java/org/apache/sling/cli/impl/CredentialsService.java
new file mode 100644
index 0000000..3ddbcca
--- /dev/null
+++ b/src/main/java/org/apache/sling/cli/impl/CredentialsService.java
@@ -0,0 +1,55 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.cli.impl;
+
+import java.util.Optional;
+
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+
+@Component(service = CredentialsService.class)
+public class CredentialsService {
+
+    private static final String USER_SYS_PROP = "asf.username";
+    private static final String PASSWORD_SYS_PROP = "asf.password";
+    private static final String USER_ENV_PROP = "ASF_USERNAME";
+    private static final String PASSWORD_ENV_PROP = "ASF_PASSWORD";
+
+    private volatile Credentials credentials;
+
+    @Activate
+    private void activate() {
+        Optional<String> username =
+                Optional.ofNullable(System.getProperty(USER_SYS_PROP)).or(() -> Optional.ofNullable(System.getenv(USER_ENV_PROP)));
+        Optional<String> password =
+                Optional.ofNullable(System.getProperty(PASSWORD_SYS_PROP)).or(() -> Optional.ofNullable(System.getenv(PASSWORD_ENV_PROP)));
+        credentials = new Credentials(
+                username.orElseThrow(() -> new IllegalStateException(
+                        String.format("Cannot detect user information after looking for %s system property and %s environment variable.",
+                                USER_SYS_PROP, USER_ENV_PROP))),
+                password.orElseThrow(() -> new IllegalStateException(
+                        String.format("Cannot detect password after looking for %s system property and %s environment variable.",
+                                PASSWORD_SYS_PROP, PASSWORD_ENV_PROP)))
+        );
+    }
+
+    public Credentials getCredentials() {
+        return credentials;
+    }
+}
diff --git a/src/main/java/org/apache/sling/cli/impl/nexus/StagingRepositoryFinder.java b/src/main/java/org/apache/sling/cli/impl/nexus/StagingRepositoryFinder.java
index 21b30ff..9093a04 100644
--- a/src/main/java/org/apache/sling/cli/impl/nexus/StagingRepositoryFinder.java
+++ b/src/main/java/org/apache/sling/cli/impl/nexus/StagingRepositoryFinder.java
@@ -30,45 +30,35 @@ import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.BasicCredentialsProvider;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
+import org.apache.sling.cli.impl.Credentials;
+import org.apache.sling.cli.impl.CredentialsService;
 import org.apache.sling.cli.impl.nexus.StagingRepository.Status;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.ConfigurationPolicy;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.osgi.service.component.annotations.Reference;
 
 import com.google.gson.Gson;
 
-@Component(
-    configurationPolicy = ConfigurationPolicy.REQUIRE,
-    service = StagingRepositoryFinder.class
-)
-@Designate(ocd = StagingRepositoryFinder.Config.class)
+@Component(service = StagingRepositoryFinder.class)
 public class StagingRepositoryFinder {
     
     private static final String REPOSITORY_PREFIX = "orgapachesling-";
 
-    @ObjectClassDefinition
-    static @interface Config {
-        @AttributeDefinition(name="Username")
-        String username();
-        
-        @AttributeDefinition(name="Password")
-        String password();
-    }
+    @Reference
+    private CredentialsService credentialsService;
 
     private BasicCredentialsProvider credentialsProvider;
     
     @Activate
-    protected void activate(Config cfg) {
+    private void activate() {
+        Credentials credentials = credentialsService.getCredentials();
         credentialsProvider = new BasicCredentialsProvider();
         credentialsProvider.setCredentials(new AuthScope("repository.apache.org", 443), 
-                new UsernamePasswordCredentials(cfg.username(), cfg.password()));
+                new UsernamePasswordCredentials(credentials.getUsername(), credentials.getPassword()));
     }
     
     public List<StagingRepository> list() throws IOException {
-        return this. <List<StagingRepository>> withStagingRepositories( reader -> {
+        return this.withStagingRepositories( reader -> {
             Gson gson = new Gson();
             return gson.fromJson(reader, StagingRepositories.class).getData().stream()
                     .filter( r -> r.getType() == Status.closed)
@@ -78,7 +68,7 @@ public class StagingRepositoryFinder {
     }
 
     public StagingRepository find(int stagingRepositoryId) throws IOException {
-        return this.<StagingRepository> withStagingRepositories( reader -> {
+        return this.withStagingRepositories( reader -> {
             Gson gson = new Gson();
             return gson.fromJson(reader, StagingRepositories.class).getData().stream()
                     .filter( r -> r.getType() == Status.closed)
diff --git a/src/main/java/org/apache/sling/cli/impl/people/MembersFinder.java b/src/main/java/org/apache/sling/cli/impl/people/MembersFinder.java
index a27bf6c..70b5469 100644
--- a/src/main/java/org/apache/sling/cli/impl/people/MembersFinder.java
+++ b/src/main/java/org/apache/sling/cli/impl/people/MembersFinder.java
@@ -29,7 +29,9 @@ import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
+import org.apache.sling.cli.impl.CredentialsService;
 import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -47,6 +49,9 @@ public class MembersFinder {
     private Set<Member> members = Collections.emptySet();
     private long lastCheck = 0;
 
+    @Reference
+    private CredentialsService credentialsService;
+
     public synchronized Set<Member> findMembers() {
         final Set<Member> _members = new HashSet<>();
         if (lastCheck == 0 || System.currentTimeMillis() > lastCheck + STALENESS_IN_HOURS * 3600 * 1000) {
@@ -108,17 +113,7 @@ public class MembersFinder {
     }
 
     public Member getCurrentMember() {
-        final String currentUserId;
-        if (System.getProperty("asf.username") != null) {
-            currentUserId = System.getProperty("asf.username");
-        } else {
-            currentUserId = System.getenv("ASF_USERNAME");
-        }
-        if (currentUserId == null) {
-            throw new IllegalStateException(String.format("Expected to find the current user defined either through the %s system " +
-                    "property or through the %s environment variable.", "asf.username", "ASF_USERNAME"));
-        }
-         return getMemberById(currentUserId);
+         return getMemberById(credentialsService.getCredentials().getUsername());
     }
 
 }
diff --git a/src/main/java/org/apache/sling/cli/impl/release/UpdateReporterCommand.java b/src/main/java/org/apache/sling/cli/impl/release/UpdateReporterCommand.java
new file mode 100644
index 0000000..4c0c127
--- /dev/null
+++ b/src/main/java/org/apache/sling/cli/impl/release/UpdateReporterCommand.java
@@ -0,0 +1,124 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.cli.impl.release;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLSocket;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ssl.DefaultHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.sling.cli.impl.Command;
+import org.apache.sling.cli.impl.Credentials;
+import org.apache.sling.cli.impl.CredentialsService;
+import org.apache.sling.cli.impl.nexus.StagingRepository;
+import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(service = Command.class,
+    property = {
+        Command.PROPERTY_NAME_COMMAND + "=release",
+        Command.PROPERTY_NAME_SUBCOMMAND + "=update-reporter",
+        Command.PROPERTY_NAME_SUMMARY + "=Updates the Apache Reporter System with the new release information"
+    }
+)
+public class UpdateReporterCommand implements Command {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(UpdateReporterCommand.class);
+
+    @Reference
+    private StagingRepositoryFinder repoFinder;
+
+    @Reference
+    private CredentialsService credentialsService;
+
+    private CredentialsProvider credentialsProvider;
+
+    @Override
+    public void execute(String target) {
+        try {
+            StagingRepository repository = repoFinder.find(Integer.parseInt(target));
+            Release release = Release.fromString(repository.getDescription());
+            SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
+            sslContext.init(null, null, null);
+            SSLContext.setDefault(sslContext);
+            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
+                    sslContext,
+                    new String[] {"TLSv1.2"},
+                    new String[] {
+                            "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"
+                    },
+                    new DefaultHostnameVerifier()
+            );
+            try (CloseableHttpClient client =
+                         HttpClients.custom().setDefaultCredentialsProvider(credentialsProvider).setSSLSocketFactory(sslConnectionSocketFactory).build()) {
+                HttpPost post = new HttpPost("https://reporter.apache.org/addrelease.py");
+                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+                List<NameValuePair> parameters = new ArrayList<>();
+                Date now = new Date();
+                parameters.add(new BasicNameValuePair("date", Long.toString(now.getTime() / 1000)));
+                parameters.add(new BasicNameValuePair("committee", "sling"));
+                parameters.add(new BasicNameValuePair("version", release.getFullName()));
+                parameters.add(new BasicNameValuePair("xdate", simpleDateFormat.format(now)));
+                post.setEntity(new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8));
+                try (CloseableHttpResponse response = client.execute(post)) {
+                    if (response.getStatusLine().getStatusCode() != 200) {
+                        throw new IOException(String.format("The Apache Reporter System update failed. Got response code %s instead of " +
+                                "200.", response.getStatusLine().getStatusCode()));
+                    }
+                }
+            }
+        } catch (IOException | NoSuchAlgorithmException | KeyManagementException e) {
+            LOGGER.error(String.format("Unable to update reporter service; passed command: %s.", target), e);
+        }
+
+    }
+
+    @Activate
+    private void activate() {
+        Credentials credentials = credentialsService.getCredentials();
+        credentialsProvider = new BasicCredentialsProvider();
+        credentialsProvider.setCredentials(new AuthScope("reporter.apache.org", 443),
+                new UsernamePasswordCredentials(credentials.getUsername(), credentials.getPassword()));
+    }
+}
diff --git a/src/main/resources/scripts/launcher.sh b/src/main/resources/scripts/launcher.sh
index 7b306ba..08f103f 100755
--- a/src/main/resources/scripts/launcher.sh
+++ b/src/main/resources/scripts/launcher.sh
@@ -18,13 +18,17 @@
 ARGS_PROP="exec.args=$@"
 
 # Use exec to become pid 1, see https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
-exec /usr/bin/java \
-     -Xshare:on \
-	 -Dorg.slf4j.simpleLogger.logFile=/dev/null \
-	 -Dlogback.configurationFile=file:/usr/share/sling-cli/conf/logback-default.xml \
-	 -jar /usr/share/sling-cli/launcher/org.apache.sling.feature.launcher.jar \
-	 -f /usr/share/sling-cli/sling-cli.feature \
-	 -c /usr/share/sling-cli/artifacts \
-	 -D "$ARGS_PROP" \
-	 -V "asf.username=${ASF_USERNAME}" \
-	 -V "asf.password=${ASF_PASSWORD}"
+exec /opt/jre/bin/java \
+    --add-opens=java.base/java.lang=ALL-UNNAMED \
+    --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED \
+    --add-opens=java.base/java.net=ALL-UNNAMED \
+    --add-opens=java.base/java.security=ALL-UNNAMED \
+    -Xshare:on \
+    -Dorg.slf4j.simpleLogger.logFile=/dev/null \
+    -Dlogback.configurationFile=file:/usr/share/sling-cli/conf/logback-default.xml \
+    -jar /usr/share/sling-cli/launcher/org.apache.sling.feature.launcher.jar \
+    -f /usr/share/sling-cli/sling-cli.feature \
+    -c /usr/share/sling-cli/artifacts \
+    -D "$ARGS_PROP" \
+    -V "asf.username=${ASF_USERNAME}" \
+    -V "asf.password=${ASF_PASSWORD}"