This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch feature/SLING-8337 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-committer-cli.git
commit 95330f1312369738740edca534816d70e763ccdc Author: Radu Cotescu <[email protected]> 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 [email protected] +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}"
