diff --git a/assemblies/nexus-base-overlay/pom.xml b/assemblies/nexus-base-overlay/pom.xml new file mode 100644 index 0000000000..6fc45e7bef --- /dev/null +++ b/assemblies/nexus-base-overlay/pom.xml @@ -0,0 +1,269 @@ + + + + + 4.0.0 + + + org.sonatype.nexus.assemblies + nexus-assemblies + 3.60.0-SNAPSHOT + + + nexus-base-overlay + ${project.groupId}:${project.artifactId} + pom + + + + + org.sonatype.nexus + nexus-main + provided + + + + + org.apache.felix + org.apache.felix.framework + provided + + + + + org.apache.karaf.features + framework + kar + + + + org.apache.karaf.features + standard + features + xml + + + + org.sonatype.nexus.assemblies + nexus-startup-feature + features + xml + + + + + org.sonatype.nexus.assemblies + nexus-boot-feature + features + xml + runtime + + + + org.sonatype.nexus.assemblies + nexus-base-feature + features + xml + runtime + + + + org.sonatype.nexus + nexus-oss-edition + features + xml + runtime + + + + org.sonatype.nexus.assemblies + nexus-core-feature + features + xml + runtime + + + + org.sonatype.nexus + nexus-orient + features + xml + runtime + + + + org.sonatype.nexus + nexus-datastore-mybatis + features + xml + runtime + + + + + org.sonatype.nexus + nexus-orient-console + provided + + + + org.sonatype.nexus + nexus2-npm-metadata-export + provided + + + + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + pax-url-settings + initialize + + execute + + + + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + overlay-assembly + prepare-package + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${org.ops4j.pax.logging:pax-logging-api:jar} + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + archive-assembly + package + + single + + + false + + ${project.basedir}/src/main/assembly/bundle.xml + + + + + + + + + diff --git a/assemblies/nexus-base-overlay/src/main/assembly/bundle.xml b/assemblies/nexus-base-overlay/src/main/assembly/bundle.xml new file mode 100644 index 0000000000..1e1c4b9c9d --- /dev/null +++ b/assemblies/nexus-base-overlay/src/main/assembly/bundle.xml @@ -0,0 +1,73 @@ + + + + + bundle + + + zip + + + false + + + + ${project.build.directory}/assembly + ${project.build.finalName} + 0644 + 0755 + + bin/* + data/** + + + + + ${project.build.directory}/assembly + ${project.build.finalName} + 0644 + 0755 + dos + + bin/*.bat + + + + + ${project.build.directory}/assembly + ${project.build.finalName} + 0755 + 0755 + unix + + bin/* + + + bin/*.bat + + + + + ${project.build.directory}/sonatype-work + sonatype-work + 0644 + 0755 + + + + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/NOTICE.txt b/assemblies/nexus-base-overlay/src/main/resources/overlay/NOTICE.txt similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/NOTICE.txt rename to assemblies/nexus-base-overlay/src/main/resources/overlay/NOTICE.txt diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/OSS-LICENSE.txt b/assemblies/nexus-base-overlay/src/main/resources/overlay/OSS-LICENSE.txt similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/OSS-LICENSE.txt rename to assemblies/nexus-base-overlay/src/main/resources/overlay/OSS-LICENSE.txt diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus b/assemblies/nexus-base-overlay/src/main/resources/overlay/bin/nexus similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus rename to assemblies/nexus-base-overlay/src/main/resources/overlay/bin/nexus diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus.bat b/assemblies/nexus-base-overlay/src/main/resources/overlay/bin/nexus.bat similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus.bat rename to assemblies/nexus-base-overlay/src/main/resources/overlay/bin/nexus.bat diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv b/assemblies/nexus-base-overlay/src/main/resources/overlay/bin/setenv similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv rename to assemblies/nexus-base-overlay/src/main/resources/overlay/bin/setenv diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv.bat b/assemblies/nexus-base-overlay/src/main/resources/overlay/bin/setenv.bat similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv.bat rename to assemblies/nexus-base-overlay/src/main/resources/overlay/bin/setenv.bat diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/deploy/.placeholder b/assemblies/nexus-base-overlay/src/main/resources/overlay/deploy/.placeholder similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/deploy/.placeholder rename to assemblies/nexus-base-overlay/src/main/resources/overlay/deploy/.placeholder diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/ehcache.xml b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/fabric/ehcache.xml similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/ehcache.xml rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/fabric/ehcache.xml diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/elasticsearch.yml b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/fabric/elasticsearch.yml similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/elasticsearch.yml rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/fabric/elasticsearch.yml diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/mybatis.xml b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/fabric/mybatis.xml similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/mybatis.xml rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/fabric/mybatis.xml diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/orientdb-security.json b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/fabric/orientdb-security.json similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/fabric/orientdb-security.json rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/fabric/orientdb-security.json diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-http.xml b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/jetty/jetty-http.xml similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-http.xml rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/jetty/jetty-http.xml diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-https.xml b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/jetty/jetty-https.xml similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-https.xml rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/jetty/jetty-https.xml diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-requestlog.xml b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/jetty/jetty-requestlog.xml similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty-requestlog.xml rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/jetty/jetty-requestlog.xml diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty.xml b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/jetty/jetty.xml similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/jetty.xml rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/jetty/jetty.xml diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/nexus-web.xml b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/jetty/nexus-web.xml similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty/nexus-web.xml rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/jetty/nexus-web.xml diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/config.properties b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/config.properties similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/config.properties rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/config.properties diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/custom.properties b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/custom.properties similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/custom.properties rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/custom.properties diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/java.util.logging.properties b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/java.util.logging.properties similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/java.util.logging.properties rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/java.util.logging.properties diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/jmx.acl.cfg b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/jmx.acl.cfg similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/jmx.acl.cfg rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/jmx.acl.cfg diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.felix.fileinstall-deploy.cfg b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.apache.felix.fileinstall-deploy.cfg similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.felix.fileinstall-deploy.cfg rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.apache.felix.fileinstall-deploy.cfg diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.jaas.cfg b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.apache.karaf.jaas.cfg similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.jaas.cfg rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.apache.karaf.jaas.cfg diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.kar.cfg b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.apache.karaf.kar.cfg similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.kar.cfg rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.apache.karaf.kar.cfg diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.log.cfg b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.apache.karaf.log.cfg similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.log.cfg rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.apache.karaf.log.cfg diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.management.cfg b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.apache.karaf.management.cfg similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.management.cfg rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.apache.karaf.management.cfg diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.service.acl.command.cfg b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.apache.karaf.service.acl.command.cfg similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.service.acl.command.cfg rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.apache.karaf.service.acl.command.cfg diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.shell.cfg b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.apache.karaf.shell.cfg similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.apache.karaf.shell.cfg rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.apache.karaf.shell.cfg diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.ops4j.pax.logging.cfg b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.ops4j.pax.logging.cfg similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.ops4j.pax.logging.cfg rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.ops4j.pax.logging.cfg diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.ops4j.pax.url.mvn.cfg b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.ops4j.pax.url.mvn.cfg similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/org.ops4j.pax.url.mvn.cfg rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/org.ops4j.pax.url.mvn.cfg diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/shell.init.script b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/shell.init.script similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/shell.init.script rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/shell.init.script diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/system.properties b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/system.properties similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/karaf/system.properties rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/system.properties diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback/logback-access.xml b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/logback/logback-access.xml similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/logback/logback-access.xml rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/logback/logback-access.xml diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback/logback.xml b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/logback/logback.xml similarity index 97% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/logback/logback.xml rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/logback/logback.xml index faa1dc11c5..c9109b4870 100644 --- a/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback/logback.xml +++ b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/logback/logback.xml @@ -17,6 +17,11 @@ + + ${karaf.data}/log/jvm.log + true + + ${karaf.data}/log/nexus.log true @@ -147,6 +152,7 @@ + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/nexus-default.properties b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/nexus-default.properties similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/nexus-default.properties rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/nexus-default.properties diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/ssl/.placeholder b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/ssl/.placeholder similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/etc/ssl/.placeholder rename to assemblies/nexus-base-overlay/src/main/resources/overlay/etc/ssl/.placeholder diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/COPYRIGHT.html b/assemblies/nexus-base-overlay/src/main/resources/overlay/public/COPYRIGHT.html similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/public/COPYRIGHT.html rename to assemblies/nexus-base-overlay/src/main/resources/overlay/public/COPYRIGHT.html diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/OSS-LICENSE.html b/assemblies/nexus-base-overlay/src/main/resources/overlay/public/OSS-LICENSE.html similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/public/OSS-LICENSE.html rename to assemblies/nexus-base-overlay/src/main/resources/overlay/public/OSS-LICENSE.html diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/apple-touch-icon.png b/assemblies/nexus-base-overlay/src/main/resources/overlay/public/apple-touch-icon.png similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/public/apple-touch-icon.png rename to assemblies/nexus-base-overlay/src/main/resources/overlay/public/apple-touch-icon.png diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/browserconfig.xml b/assemblies/nexus-base-overlay/src/main/resources/overlay/public/browserconfig.xml similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/public/browserconfig.xml rename to assemblies/nexus-base-overlay/src/main/resources/overlay/public/browserconfig.xml diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon-16x16.png b/assemblies/nexus-base-overlay/src/main/resources/overlay/public/favicon-16x16.png similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/public/favicon-16x16.png rename to assemblies/nexus-base-overlay/src/main/resources/overlay/public/favicon-16x16.png diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon-32x32.png b/assemblies/nexus-base-overlay/src/main/resources/overlay/public/favicon-32x32.png similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/public/favicon-32x32.png rename to assemblies/nexus-base-overlay/src/main/resources/overlay/public/favicon-32x32.png diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon.ico b/assemblies/nexus-base-overlay/src/main/resources/overlay/public/favicon.ico similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/public/favicon.ico rename to assemblies/nexus-base-overlay/src/main/resources/overlay/public/favicon.ico diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-144x144.png b/assemblies/nexus-base-overlay/src/main/resources/overlay/public/mstile-144x144.png similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-144x144.png rename to assemblies/nexus-base-overlay/src/main/resources/overlay/public/mstile-144x144.png diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-150x150.png b/assemblies/nexus-base-overlay/src/main/resources/overlay/public/mstile-150x150.png similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-150x150.png rename to assemblies/nexus-base-overlay/src/main/resources/overlay/public/mstile-150x150.png diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-310x150.png b/assemblies/nexus-base-overlay/src/main/resources/overlay/public/mstile-310x150.png similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-310x150.png rename to assemblies/nexus-base-overlay/src/main/resources/overlay/public/mstile-310x150.png diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-310x310.png b/assemblies/nexus-base-overlay/src/main/resources/overlay/public/mstile-310x310.png similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-310x310.png rename to assemblies/nexus-base-overlay/src/main/resources/overlay/public/mstile-310x310.png diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-70x70.png b/assemblies/nexus-base-overlay/src/main/resources/overlay/public/mstile-70x70.png similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/public/mstile-70x70.png rename to assemblies/nexus-base-overlay/src/main/resources/overlay/public/mstile-70x70.png diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/robots.txt b/assemblies/nexus-base-overlay/src/main/resources/overlay/public/robots.txt similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/public/robots.txt rename to assemblies/nexus-base-overlay/src/main/resources/overlay/public/robots.txt diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/safari-pinned-tab.svg b/assemblies/nexus-base-overlay/src/main/resources/overlay/public/safari-pinned-tab.svg similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/public/safari-pinned-tab.svg rename to assemblies/nexus-base-overlay/src/main/resources/overlay/public/safari-pinned-tab.svg diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/system/settings.xml b/assemblies/nexus-base-overlay/src/main/resources/overlay/system/settings.xml similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/overlay/system/settings.xml rename to assemblies/nexus-base-overlay/src/main/resources/overlay/system/settings.xml diff --git a/assemblies/nexus-base-template/src/main/resources/sonatype-work/nexus3/clean_cache b/assemblies/nexus-base-overlay/src/main/resources/sonatype-work/nexus3/clean_cache similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/sonatype-work/nexus3/clean_cache rename to assemblies/nexus-base-overlay/src/main/resources/sonatype-work/nexus3/clean_cache diff --git a/assemblies/nexus-base-template/src/main/resources/sonatype-work/nexus3/log/.placeholder b/assemblies/nexus-base-overlay/src/main/resources/sonatype-work/nexus3/log/.placeholder similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/sonatype-work/nexus3/log/.placeholder rename to assemblies/nexus-base-overlay/src/main/resources/sonatype-work/nexus3/log/.placeholder diff --git a/assemblies/nexus-base-template/src/main/resources/sonatype-work/nexus3/tmp/.placeholder b/assemblies/nexus-base-overlay/src/main/resources/sonatype-work/nexus3/tmp/.placeholder similarity index 100% rename from assemblies/nexus-base-template/src/main/resources/sonatype-work/nexus3/tmp/.placeholder rename to assemblies/nexus-base-overlay/src/main/resources/sonatype-work/nexus3/tmp/.placeholder diff --git a/assemblies/nexus-base-template/pom.xml b/assemblies/nexus-base-template/pom.xml index fefd16fa02..a17aec2a11 100644 --- a/assemblies/nexus-base-template/pom.xml +++ b/assemblies/nexus-base-template/pom.xml @@ -29,6 +29,20 @@ pom + + + org.sonatype.nexus.assemblies + nexus-base-overlay + zip + runtime + + + * + * + + + + @@ -251,42 +265,6 @@ - - - - - - - - - - - - - - - - - - - - - - @@ -353,14 +331,11 @@ - - - - - - - - + + + diff --git a/assemblies/pom.xml b/assemblies/pom.xml index d7b7aeeed8..b596934f17 100644 --- a/assemblies/pom.xml +++ b/assemblies/pom.xml @@ -33,6 +33,7 @@ nexus-boot-feature nexus-base-feature nexus-core-feature + nexus-base-overlay nexus-base-template diff --git a/components/nexus-cleanup-config/src/main/java/org/sonatype/nexus/cleanup/config/CleanupPolicyConstants.java b/components/nexus-cleanup-config/src/main/java/org/sonatype/nexus/cleanup/config/CleanupPolicyConstants.java index 9c9eef68f8..70e9a6abdd 100644 --- a/components/nexus-cleanup-config/src/main/java/org/sonatype/nexus/cleanup/config/CleanupPolicyConstants.java +++ b/components/nexus-cleanup-config/src/main/java/org/sonatype/nexus/cleanup/config/CleanupPolicyConstants.java @@ -29,5 +29,9 @@ public interface CleanupPolicyConstants String LAST_DOWNLOADED_KEY = "lastDownloaded"; + String RETAIN_KEY = "retain"; + + String RETAIN_SORT_BY_KEY = "sortBy"; + String REGEX_KEY = "regex"; } diff --git a/components/nexus-cleanup-config/src/main/java/org/sonatype/nexus/cleanup/config/DefaultCleanupPolicyConfiguration.java b/components/nexus-cleanup-config/src/main/java/org/sonatype/nexus/cleanup/config/DefaultCleanupPolicyConfiguration.java index 13b63197f6..747909edb2 100644 --- a/components/nexus-cleanup-config/src/main/java/org/sonatype/nexus/cleanup/config/DefaultCleanupPolicyConfiguration.java +++ b/components/nexus-cleanup-config/src/main/java/org/sonatype/nexus/cleanup/config/DefaultCleanupPolicyConfiguration.java @@ -23,6 +23,8 @@ import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.LAST_BLOB_UPDATED_KEY; import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.LAST_DOWNLOADED_KEY; import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.REGEX_KEY; +import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.RETAIN_KEY; +import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.RETAIN_SORT_BY_KEY; /** * Defines which default cleanup policy fields to display. @@ -36,9 +38,14 @@ public class DefaultCleanupPolicyConfiguration { @Override public Map getConfiguration() { - return ImmutableMap.of(LAST_BLOB_UPDATED_KEY, true, - LAST_DOWNLOADED_KEY, true, - IS_PRERELEASE_KEY, false, - REGEX_KEY, false); + return ImmutableMap.builder() + .put(LAST_BLOB_UPDATED_KEY, true) + .put(LAST_DOWNLOADED_KEY, true) + .put(IS_PRERELEASE_KEY, false) + .put(REGEX_KEY, false) + //disabling retain-n properties by default + .put(RETAIN_KEY, false) + .put(RETAIN_SORT_BY_KEY, false) + .build(); } } diff --git a/components/nexus-cleanup-config/src/main/java/org/sonatype/nexus/cleanup/storage/SortType.java b/components/nexus-cleanup-config/src/main/java/org/sonatype/nexus/cleanup/storage/SortType.java new file mode 100644 index 0000000000..b40d3af0c9 --- /dev/null +++ b/components/nexus-cleanup-config/src/main/java/org/sonatype/nexus/cleanup/storage/SortType.java @@ -0,0 +1,19 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.cleanup.storage; + +public enum SortType +{ + VERSION, + DATE +} diff --git a/components/nexus-cleanup/src/main/java/org/sonatype/nexus/cleanup/internal/rest/CleanupPolicyResource.java b/components/nexus-cleanup/src/main/java/org/sonatype/nexus/cleanup/internal/rest/CleanupPolicyResource.java index 67519bea8b..3852698125 100644 --- a/components/nexus-cleanup/src/main/java/org/sonatype/nexus/cleanup/internal/rest/CleanupPolicyResource.java +++ b/components/nexus-cleanup/src/main/java/org/sonatype/nexus/cleanup/internal/rest/CleanupPolicyResource.java @@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; @@ -52,6 +53,7 @@ import org.sonatype.nexus.cleanup.storage.CleanupPolicy; import org.sonatype.nexus.cleanup.storage.CleanupPolicyCriteria; import org.sonatype.nexus.cleanup.storage.CleanupPolicyPreviewXO; +import org.sonatype.nexus.cleanup.storage.CleanupPolicyReleaseType; import org.sonatype.nexus.cleanup.storage.CleanupPolicyStorage; import org.sonatype.nexus.common.event.EventManager; import org.sonatype.nexus.extdirect.model.PagedResponse; @@ -83,6 +85,8 @@ import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.LAST_BLOB_UPDATED_KEY; import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.LAST_DOWNLOADED_KEY; import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.REGEX_KEY; +import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.RETAIN_KEY; +import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.RETAIN_SORT_BY_KEY; import static org.sonatype.nexus.cleanup.internal.rest.CleanupPolicyResource.RESOURCE_URI; import static org.sonatype.nexus.cleanup.storage.CleanupPolicy.ALL_FORMATS; import static org.sonatype.nexus.cleanup.storage.CleanupPolicyReleaseType.PRERELEASES; @@ -178,6 +182,9 @@ public CleanupPolicyXO add(@Valid final CleanupPolicyXO cleanupPolicyXO) { if (!this.formatNames.contains(cleanupPolicyXO.getFormat())) { throw new ValidationErrorsException("format", "specified format " + cleanupPolicyXO.getFormat() + " is not valid."); } + + validateRetainAttributes(cleanupPolicyXO); + return CleanupPolicyXO.fromCleanupPolicy(cleanupPolicyStorage.add(toCleanupPolicy(cleanupPolicyXO)), 0); } @@ -222,6 +229,8 @@ public CleanupPolicyXO edit( throw new ValidationErrorsException("format", "You cannot change the format of a policy that is in use."); } + validateRetainAttributes(cleanupPolicyXO); + cleanupPolicy.setNotes(cleanupPolicyXO.getNotes()); cleanupPolicy.setFormat(cleanupPolicyXO.getFormat()); cleanupPolicy.setCriteria(toCriteriaMap(cleanupPolicyXO)); @@ -300,43 +309,49 @@ public PageResult previewContent(PreviewRequestXO request) } } - @POST + @GET @Path("preview/components/csv") @RequiresAuthentication @RequiresPermissions("nexus:*") @Produces(APPLICATION_OCTET_STREAM) @Timed - public Response previewContentCsv(final PreviewRequestXO request) + public Response previewContentCsv( + @QueryParam("name") @Nullable String name, + @QueryParam("repository") String repositoryName, + @QueryParam("criteriaLastBlobUpdated") @Nullable Integer criteriaLastBlobUpdated, + @QueryParam("criteriaLastDownloaded") @Nullable Integer criteriaLastDownloaded, + @QueryParam("criteriaReleaseType") @Nullable CleanupPolicyReleaseType criteriaReleaseType, + @QueryParam("criteriaAssetRegex") @Nullable String criteriaAssetRegex + ) { + if (!isDatastoreEnabled || !isPreviewEnabled) { return Response.status(Status.NOT_FOUND).build(); } Map cleanupDryRunXO = new HashMap<>(); cleanupDryRunXO.put(STARTED_AT_IN_MILLISECONDS, System.currentTimeMillis()); - Repository repository = repositoryManager.get(request.getRepository()); + Repository repository = repositoryManager.get(repositoryName); if (repository == null) { - throw new NotFoundException("Repository " + request.getRepository() + " not found."); + throw new NotFoundException("Repository " + repositoryName + " not found."); } StreamingOutput streamingOutput = output -> { CleanupPolicyPreviewXO xo = new CleanupPolicyPreviewXO(); - xo.setRepositoryName(request.getRepository()); + xo.setRepositoryName(repositoryName); xo.setCriteria(new CleanupPolicyCriteria()); - xo.getCriteria().setLastBlobUpdated(request.getCriteriaLastBlobUpdated()); - xo.getCriteria().setLastDownloaded(request.getCriteriaLastDownloaded()); - xo.getCriteria().setReleaseType(request.getCriteriaReleaseType()); - xo.getCriteria().setRegex(request.getCriteriaAssetRegex()); - - QueryOptions options = new QueryOptions(request.getFilter(), "name", "asc", 0, null); + xo.getCriteria().setLastBlobUpdated(criteriaLastBlobUpdated); + xo.getCriteria().setLastDownloaded(criteriaLastDownloaded); + xo.getCriteria().setReleaseType(criteriaReleaseType); + xo.getCriteria().setRegex(criteriaAssetRegex); Stream searchResultsStream = - cleanupPreviewHelper.get().getSearchResultsStream(xo, repository, options); + cleanupPreviewHelper.get().getSearchResultsStream(xo, repository, null); csvCleanupPreviewContentWriter.write(repository, searchResultsStream, output); cleanupDryRunXO.put(FINISHED_AT_IN_MILLISECONDS, System.currentTimeMillis()); eventManager.post(new CleanupDryRunEvent(cleanupDryRunXO)); }; - String policyName = request.getName() == null ? "CleanupPreview" : request.getName(); + String policyName = name == null ? "CleanupPreview" : name; String filename = policyName + "-" + repository.getName() + "-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss")) + @@ -346,6 +361,16 @@ public Response previewContentCsv(final PreviewRequestXO request) .build(); } + private void validateRetainAttributes(final CleanupPolicyXO cleanupPolicyXO) { + if (cleanupPolicyXO.getRetain() != null && cleanupPolicyXO.getSortBy() == null) { + throw new ValidationErrorsException("sortBy", "sortBy should be defined if retain is set"); + } + + if (cleanupPolicyXO.getSortBy() != null && cleanupPolicyXO.getRetain() == null) { + throw new ValidationErrorsException("retain", "retain should be defined if sortBy is set"); + } + } + private CleanupPolicy toCleanupPolicy(final CleanupPolicyXO cleanupPolicyXO) { CleanupPolicy policy = cleanupPolicyStorage.newCleanupPolicy(); @@ -378,6 +403,14 @@ private Map toCriteriaMap(final CleanupPolicyXO cleanupPolicyXO) handleCriteria(cleanupFormatConfiguration, criteriaMap, IS_PRERELEASE_KEY, PRERELEASES.equals(cleanupPolicyXO.getCriteriaReleaseType()), "Release type", cleanupPolicyXO.getFormat()); } + if (cleanupPolicyXO.getRetain() != null) { + handleCriteria(cleanupFormatConfiguration, criteriaMap, RETAIN_KEY, + cleanupPolicyXO.getRetain(), "Retain components", cleanupPolicyXO.getFormat()); + } + if (cleanupPolicyXO.getSortBy() != null) { + handleCriteria(cleanupFormatConfiguration, criteriaMap, RETAIN_SORT_BY_KEY, + cleanupPolicyXO.getSortBy(), "Retain sort by", cleanupPolicyXO.getFormat()); + } return criteriaMap; } diff --git a/components/nexus-cleanup/src/main/java/org/sonatype/nexus/cleanup/internal/rest/CleanupPolicyXO.java b/components/nexus-cleanup/src/main/java/org/sonatype/nexus/cleanup/internal/rest/CleanupPolicyXO.java index 72430d81ad..ae0be1d29a 100644 --- a/components/nexus-cleanup/src/main/java/org/sonatype/nexus/cleanup/internal/rest/CleanupPolicyXO.java +++ b/components/nexus-cleanup/src/main/java/org/sonatype/nexus/cleanup/internal/rest/CleanupPolicyXO.java @@ -14,21 +14,25 @@ import java.util.Map; import java.util.concurrent.TimeUnit; - +import javax.validation.constraints.Min; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import org.sonatype.nexus.cleanup.storage.CleanupPolicy; import org.sonatype.nexus.cleanup.storage.CleanupPolicyReleaseType; +import org.sonatype.nexus.cleanup.storage.SortType; import org.sonatype.nexus.cleanup.storage.config.CleanupPolicyAssetNamePattern; import org.sonatype.nexus.cleanup.storage.config.UniqueCleanupPolicyName; +import org.sonatype.nexus.validation.constraint.AnyOf; import org.sonatype.nexus.validation.group.Create; import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.IS_PRERELEASE_KEY; import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.LAST_BLOB_UPDATED_KEY; import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.LAST_DOWNLOADED_KEY; import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.REGEX_KEY; +import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.RETAIN_KEY; +import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.RETAIN_SORT_BY_KEY; import static org.sonatype.nexus.cleanup.storage.CleanupPolicy.ALL_FORMATS; import static org.sonatype.nexus.cleanup.storage.CleanupPolicy.ALL_CLEANUP_POLICY_FORMAT; import static org.sonatype.nexus.cleanup.storage.CleanupPolicyReleaseType.PRERELEASES; @@ -56,6 +60,12 @@ public class CleanupPolicyXO private Long criteriaLastDownloaded; // days + @Min(value = 1, message = "retain must be > 0") + private Integer retain; + + @AnyOf(enumClass = SortType.class, parameterName = "sortBy") + private String sortBy; + private CleanupPolicyReleaseType criteriaReleaseType; @CleanupPolicyAssetNamePattern @@ -87,6 +97,14 @@ public Long getCriteriaLastDownloaded() { return criteriaLastDownloaded; } + public Integer getRetain() { + return retain; + } + + public String getSortBy() { + return sortBy; + } + public String getCriteriaAssetRegex() { return criteriaAssetRegex; } @@ -119,6 +137,14 @@ public void setCriteriaLastDownloaded(final Long criteriaLastDownloaded) { this.criteriaLastDownloaded = criteriaLastDownloaded; } + public void setRetain(final Integer retain) { + this.retain = retain; + } + + public void setSortBy(final String sortBy) { + this.sortBy = sortBy; + } + public void setCriteriaReleaseType(final CleanupPolicyReleaseType criteriaReleaseType) { this.criteriaReleaseType = criteriaReleaseType; } @@ -135,11 +161,23 @@ public static CleanupPolicyXO fromCleanupPolicy(final CleanupPolicy cleanupPolic xo.setCriteriaAssetRegex(cleanupPolicy.getCriteria().get(REGEX_KEY)); xo.setCriteriaLastBlobUpdated(toDays(getNullableLong(cleanupPolicy.getCriteria(), LAST_BLOB_UPDATED_KEY))); xo.setCriteriaLastDownloaded(toDays(getNullableLong(cleanupPolicy.getCriteria(), LAST_DOWNLOADED_KEY))); + xo.setRetain(getNullableInt(cleanupPolicy.getCriteria() , RETAIN_KEY)); + xo.setSortBy(cleanupPolicy.getCriteria().getOrDefault(RETAIN_SORT_BY_KEY , null)); xo.setCriteriaReleaseType(getNullableReleaseType(cleanupPolicy.getCriteria())); xo.setInUseCount(inUseCount); return xo; } + private static Integer getNullableInt(final Map map, final String key) { + String value = map.get(key); + + if (value == null) { + return null; + } + + return Integer.valueOf(value); + } + private static Long getNullableLong(final Map map, final String key) { String value = map.get(key); diff --git a/components/nexus-cleanup/src/test/java/org/sonatype/nexus/cleanup/internal/rest/CleanupPolicyResourceTest.java b/components/nexus-cleanup/src/test/java/org/sonatype/nexus/cleanup/internal/rest/CleanupPolicyResourceTest.java index 5d143520f0..2d73802d8b 100644 --- a/components/nexus-cleanup/src/test/java/org/sonatype/nexus/cleanup/internal/rest/CleanupPolicyResourceTest.java +++ b/components/nexus-cleanup/src/test/java/org/sonatype/nexus/cleanup/internal/rest/CleanupPolicyResourceTest.java @@ -66,15 +66,11 @@ public class CleanupPolicyResourceTest private CleanupPolicyResource underTest; - private PreviewRequestXO request; - private final String repositoryName = "test-repo"; @Before public void setUp() throws Exception { when(cleanupFormatConfigurationMap.get("default")).thenReturn(mock(CleanupPolicyConfiguration.class)); - request = new PreviewRequestXO(); - request.setRepository(repositoryName); Repository repository = mock(Repository.class); when(repositoryManager.get(repositoryName)).thenReturn(repository); when(repository.getName()).thenReturn(repositoryName); @@ -86,7 +82,7 @@ public void testPreviewContentCsv() { new CleanupPolicyResource(cleanupPolicyStorage, formats, cleanupFormatConfigurationMap, cleanupPreviewHelper, repositoryManager, eventManager, true, true, csvCleanupPreviewContentWriter); - Response response = underTest.previewContentCsv(request); + Response response = underTest.previewContentCsv(null, repositoryName, null, null, null, null); assertThat(response.getStatus(), is(200)); String contentDisposition = response.getHeaderString("Content-Disposition"); @@ -95,8 +91,7 @@ public void testPreviewContentCsv() { assertThat(contentDisposition, startsWith(expectedPrefix)); assertThat(contentDisposition, endsWith(".csv")); - request.setName("policy-name"); - response = underTest.previewContentCsv(request); + response = underTest.previewContentCsv("policy-name", repositoryName, null, null, null, null); assertThat(response.getStatus(), is(200)); contentDisposition = response.getHeaderString("Content-Disposition"); @@ -112,7 +107,7 @@ public void testNotFoundResponseForOrient() { new CleanupPolicyResource(cleanupPolicyStorage, formats, cleanupFormatConfigurationMap, cleanupPreviewHelper, repositoryManager, eventManager, false, true, csvCleanupPreviewContentWriter); - Response response = underTest.previewContentCsv(request); + Response response = underTest.previewContentCsv(null, repositoryName, null, null, null, null); assertThat(response.getStatus(), is(404)); } diff --git a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/SystemInformationGeneratorImpl.groovy b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/SystemInformationGeneratorImpl.groovy index 697376b5ce..5517ba936b 100644 --- a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/SystemInformationGeneratorImpl.groovy +++ b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/SystemInformationGeneratorImpl.groovy @@ -34,6 +34,7 @@ import org.eclipse.sisu.Parameters import org.osgi.framework.BundleContext import static com.google.common.base.Preconditions.checkNotNull +import static org.sonatype.nexus.common.text.Strings2.MASK /** * Default {@link SystemInformationGenerator}. @@ -60,12 +61,12 @@ class SystemInformationGeneratorImpl private final NodeAccess nodeAccess - private final DeploymentAccess deploymentAccess; + private final DeploymentAccess deploymentAccess static final Map UNAVAILABLE = ['unavailable': true].asImmutable() - static final List SENSITIVE_FIELD_NAMES = ['password', 'secret', 'token'].asImmutable() - + static final List SENSITIVE_FIELD_NAMES = + ['password', 'secret', 'token', 'sign', 'auth', 'cred', 'key', 'pass'].asImmutable() @Inject SystemInformationGeneratorImpl(final ApplicationDirectories applicationDirectories, final ApplicationVersion applicationVersion, @@ -214,6 +215,9 @@ class SystemInformationGeneratorImpl if (key.toLowerCase(Locale.US).contains(sensitiveName)) { value = Strings2.mask(value) } + if (key == "sun.java.command" && value.contains(sensitiveName)) { + value = value.replaceAll(sensitiveName + "=\\S*", sensitiveName + "=" + MASK) + } } return [key, value] }.sort() diff --git a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/customizers/JmxCustomizer.groovy b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/customizers/JmxCustomizer.groovy index bf71dc3b76..e527722c1a 100644 --- a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/customizers/JmxCustomizer.groovy +++ b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/customizers/JmxCustomizer.groovy @@ -27,6 +27,7 @@ import org.sonatype.nexus.supportzip.SupportBundleCustomizer import groovy.json.JsonBuilder +import static org.sonatype.nexus.common.text.Strings2.MASK import static org.sonatype.nexus.supportzip.SupportBundle.ContentSource.Priority.OPTIONAL import static org.sonatype.nexus.supportzip.SupportBundle.ContentSource.Type.JMX @@ -45,6 +46,9 @@ class JmxCustomizer @Named('platform') MBeanServer server + private static final List SENSITIVE_FIELD_NAMES = + ['password', 'secret', 'token', 'sign', 'auth', 'cred', 'key', 'pass'].asImmutable() + @Override void customize(final SupportBundle supportBundle) { // add jmx.json @@ -100,6 +104,14 @@ class JmxCustomizer // TODO: Cope with password-like fields where we can detect .*password.* or something? + if (value instanceof String) { + SENSITIVE_FIELD_NAMES.each {sensitiveName -> + if (value.contains(sensitiveName)) { + value = value.replaceAll(sensitiveName + "=\\S*", sensitiveName + "=" + MASK) + } + } + } + def type = value.getClass() log.trace "Rendering type: $type" diff --git a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/customizers/JvmLogCustomizer.java b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/customizers/JvmLogCustomizer.java new file mode 100644 index 0000000000..a1665d469d --- /dev/null +++ b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/customizers/JvmLogCustomizer.java @@ -0,0 +1,95 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.atlas.customizers; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.common.log.LogManager; +import org.sonatype.nexus.supportzip.GeneratedContentSourceSupport; +import org.sonatype.nexus.supportzip.SupportBundle; +import org.sonatype.nexus.supportzip.SupportBundleCustomizer; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.text.Strings2.MASK; +import static org.sonatype.nexus.supportzip.SupportBundle.ContentSource.Priority.LOW; +import static org.sonatype.nexus.supportzip.SupportBundle.ContentSource.Type.LOG; + +/** + * Adds jvm log file to support bundle. + * Masks sensitive data passed as JVM arguments. + */ +@Named +@Singleton +public class JvmLogCustomizer + extends ComponentSupport + implements SupportBundleCustomizer +{ + private static final List SENSITIVE_FIELD_NAMES = + Arrays.asList("password", "secret", "token", "sign", "auth", "cred", "key", "pass"); + + private final LogManager logManager; + + @Inject + public JvmLogCustomizer(final LogManager logManager) { + this.logManager = checkNotNull(logManager); + } + + @Override + public void customize(final SupportBundle supportBundle) { + supportBundle.add(new GeneratedContentSourceSupport(LOG, "log/jvm.log", LOW) + { + @Override + protected void generate(final File file) { + File logFile = logManager.getLogFile("jvm.log"); + + if (logFile != null) { + try (BufferedReader reader = new BufferedReader(new FileReader(logFile)); + BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { + + String line; + while ((line = reader.readLine()) != null) { + String redactedLine = maybeMaskSensitiveData(line); + writer.write(redactedLine); + writer.newLine(); + } + } catch (IOException e) { + log.debug("Unable to include jvm.log file", e); + } + } + else { + log.debug("Not including missing jvm.log file"); + } + } + + private String maybeMaskSensitiveData(final String input) { + String result = input; + for (String name : SENSITIVE_FIELD_NAMES) { + result = result.replaceAll(name + "=\\S*", name + "=" + MASK); + } + return result; + } + }); + } +} diff --git a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/customizers/LogCustomizer.groovy b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/customizers/LogCustomizer.groovy index 8625b3cc2a..4143b05f38 100644 --- a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/customizers/LogCustomizer.groovy +++ b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/atlas/customizers/LogCustomizer.groovy @@ -85,7 +85,6 @@ class LogCustomizer maybeIncludeFile new File(applicationDirectories.workDirectory, 'log/karaf.log'), 'log', LOW maybeIncludeFile new File(applicationDirectories.workDirectory, 'log/request.log'), 'log', LOW - maybeIncludeFile new File(applicationDirectories.workDirectory, 'log/jvm.log'), 'log', LOW maybeIncludeFile new File(applicationDirectories.workDirectory, 'log/outbound-request.log'), 'log', LOW logManager.getLogFor("clusterlogfile").ifPresent { maybeIncludeFile new File(applicationDirectories.workDirectory, 'log/' + it), 'log', LOW } } diff --git a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/DefaultsCustomizer.java b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/DefaultsCustomizer.java index 756954253c..21120ca832 100644 --- a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/DefaultsCustomizer.java +++ b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/DefaultsCustomizer.java @@ -20,6 +20,7 @@ import org.sonatype.goodies.common.ComponentSupport; import org.sonatype.goodies.common.Time; import org.sonatype.nexus.httpclient.HttpClientPlan; +import org.sonatype.nexus.utils.httpclient.UserAgentGenerator; import org.apache.http.client.config.CookieSpecs; import org.apache.http.impl.client.StandardHttpRequestRetryHandler; diff --git a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/UserAgentGenerator.java b/components/nexus-core/src/main/java/org/sonatype/nexus/utils/httpclient/UserAgentGenerator.java similarity index 76% rename from components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/UserAgentGenerator.java rename to components/nexus-core/src/main/java/org/sonatype/nexus/utils/httpclient/UserAgentGenerator.java index ef6fdb4d04..92f8c86544 100644 --- a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/UserAgentGenerator.java +++ b/components/nexus-core/src/main/java/org/sonatype/nexus/utils/httpclient/UserAgentGenerator.java @@ -10,13 +10,14 @@ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the * Eclipse Foundation. All other trademarks are the property of their respective owners. */ -package org.sonatype.nexus.internal.httpclient; +package org.sonatype.nexus.utils.httpclient; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.capability.CapabilityReference; import org.sonatype.nexus.common.app.ApplicationVersion; import static com.google.common.base.Preconditions.checkNotNull; @@ -29,8 +30,14 @@ @Named @Singleton public class UserAgentGenerator - extends ComponentSupport + extends ComponentSupport { + private static final String PAU = "; pau)"; + + private static final String PAE = "; pae)"; + + private static final String PAD = "; pad)"; + private final ApplicationVersion applicationVersion; private String value; @@ -59,4 +66,17 @@ public String generate() { return value; } + + public String buildUserAgentForAnalytics(CapabilityReference capabilityReference) { + String ua = generate(); + if (capabilityReference == null) { + return ua.replace(")", PAU); + } + else if (capabilityReference.context().isEnabled()) { + return ua.replace(")", PAE); + } + else { + return ua.replace(")", PAD); + } + } } diff --git a/components/nexus-core/src/test/java/org/sonatype/nexus/internal/atlas/SystemInformationGeneratorImplTest.groovy b/components/nexus-core/src/test/java/org/sonatype/nexus/internal/atlas/SystemInformationGeneratorImplTest.groovy index 18ad3774e4..983fb68c72 100644 --- a/components/nexus-core/src/test/java/org/sonatype/nexus/internal/atlas/SystemInformationGeneratorImplTest.groovy +++ b/components/nexus-core/src/test/java/org/sonatype/nexus/internal/atlas/SystemInformationGeneratorImplTest.groovy @@ -26,6 +26,7 @@ import org.osgi.framework.BundleContext import spock.lang.Specification import static org.hamcrest.MatcherAssert.assertThat +import static org.hamcrest.core.IsEqual.equalTo import static org.hamcrest.text.IsEmptyString.isEmptyString import static org.hamcrest.core.IsNot.not @@ -143,4 +144,25 @@ class SystemInformationGeneratorImplTest assertThat(systemEnvs.get('AZURE_TOKEN'), not('azureTokenValue')) assertThat(systemEnvs.get('MY_PASSWORD_FOR_NXRM'), not('admin123')) } + + def "jvm variable sensitive data is hidden"() { + given: + def generator = new SystemInformationGeneratorImpl( + Mock(ApplicationDirectories.class), + Mock(ApplicationVersion.class), + Mock(ApplicationLicense.class), + Collections.singletonMap("sun.java.command", "test.variable=1 -Dnexus.password=nxrm -Dnexus.token=123456"), + Mock(BundleContext.class), + Mock(BundleService.class), + Mock(NodeAccess.class), + Mock(DeploymentAccess.class)) + + when: + def report = generator.report() + + then: + def nexusProps = report.get("nexus-properties") + assertThat(nexusProps.get("sun.java.command"), + equalTo("test.variable=1 -Dnexus.password=**** -Dnexus.token=****")) + } } diff --git a/components/nexus-core/src/test/java/org/sonatype/nexus/internal/atlas/customizers/JvmLogCustomizerTest.java b/components/nexus-core/src/test/java/org/sonatype/nexus/internal/atlas/customizers/JvmLogCustomizerTest.java new file mode 100644 index 0000000000..cef89b003e --- /dev/null +++ b/components/nexus-core/src/test/java/org/sonatype/nexus/internal/atlas/customizers/JvmLogCustomizerTest.java @@ -0,0 +1,92 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.internal.atlas.customizers; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.time.ZonedDateTime; +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.app.ApplicationDirectories; +import org.sonatype.nexus.common.log.LogManager; +import org.sonatype.nexus.supportzip.SupportBundle; +import org.sonatype.nexus.supportzip.SupportBundle.ContentSource; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.Mock; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.common.io.DirectoryHelper.mkdir; + +public class JvmLogCustomizerTest + extends TestSupport +{ + @Rule + public TemporaryFolder temporaryWorkDirectory = new TemporaryFolder(); + + @Mock + private LogManager mockLogManager; + + @Mock + private ApplicationDirectories mockApplicationDirectories; + + private JvmLogCustomizer underTest; + + + @Before + public void setup() throws IOException { + when(mockApplicationDirectories.getWorkDirectory()).thenReturn(temporaryWorkDirectory.getRoot()); + mkdir(temporaryWorkDirectory.getRoot(), "log"); + underTest = new JvmLogCustomizer(mockLogManager); + } + + @Test + public void customizeJvmLog() throws Exception { + File file = createLogFile(); + when(mockLogManager.getLogFile(anyString())).thenReturn(file); + try(FileWriter writer = new FileWriter(file)) { + writer.write("-Dnexus.password=nxrm -Dnexus.token=123"); + } + SupportBundle supportBundle = new SupportBundle(); + underTest.customize(supportBundle); + + List list = supportBundle.getSources(); + assertThat(list.size(), equalTo(1)); + + ContentSource contentSource = list.get(0); + contentSource.prepare(); + + try(BufferedReader reader = new BufferedReader(new InputStreamReader(contentSource.getContent()))) { + assertThat(reader.readLine(), equalTo("-Dnexus.password=**** -Dnexus.token=****")); + } + } + + + private File createLogFile() throws IOException { + File file = new File(temporaryWorkDirectory.getRoot(), "/log/jvm.log"); + file.createNewFile(); + file.setLastModified(ZonedDateTime.now().minusMinutes(0L).toInstant().toEpochMilli()); + return file; + } + +} diff --git a/components/nexus-repository-services/src/main/java/org/sonatype/nexus/repository/proxy/ProxyFacetSupport.java b/components/nexus-repository-services/src/main/java/org/sonatype/nexus/repository/proxy/ProxyFacetSupport.java index 89d47769b9..fc60045c16 100644 --- a/components/nexus-repository-services/src/main/java/org/sonatype/nexus/repository/proxy/ProxyFacetSupport.java +++ b/components/nexus-repository-services/src/main/java/org/sonatype/nexus/repository/proxy/ProxyFacetSupport.java @@ -17,6 +17,7 @@ import java.io.UnsupportedEncodingException; import java.net.URI; import java.time.Duration; +import java.util.Arrays; import java.util.Map; import java.util.Optional; import javax.annotation.Nonnull; @@ -477,6 +478,7 @@ protected Content fetch(final String url, final Context context, @Nullable final } } log.debug("Fetching: {}", request); + log.debug("Fetching Request Headers: {}", Arrays.toString(request.getAllHeaders())); HttpResponse response = execute(context, client, request); log.debug("Response: {}", response); diff --git a/components/nexus-repository-view/src/main/java/org/sonatype/nexus/repository/http/HttpResponses.java b/components/nexus-repository-view/src/main/java/org/sonatype/nexus/repository/http/HttpResponses.java index 408f2844ff..fce2ca4f3a 100644 --- a/components/nexus-repository-view/src/main/java/org/sonatype/nexus/repository/http/HttpResponses.java +++ b/components/nexus-repository-view/src/main/java/org/sonatype/nexus/repository/http/HttpResponses.java @@ -199,6 +199,12 @@ public static Response badGateway() { return badGateway(null); } + public static Response gatewayTimeout(@Nullable final String message) { + return new Response.Builder() + .status(Status.failure(GATEWAY_TIMEOUT, message)) + .build(); + } + public static Response notImplemented(@Nullable final String message) { return new Response.Builder() .status(Status.failure(NOT_IMPLEMENTED, message)) diff --git a/components/nexus-security/src/main/java/org/sonatype/nexus/security/token/BearerTokenRealm.java b/components/nexus-security/src/main/java/org/sonatype/nexus/security/token/BearerTokenRealm.java index ccf1dc9825..8f4d4c017f 100644 --- a/components/nexus-security/src/main/java/org/sonatype/nexus/security/token/BearerTokenRealm.java +++ b/components/nexus-security/src/main/java/org/sonatype/nexus/security/token/BearerTokenRealm.java @@ -66,7 +66,7 @@ public BearerTokenRealm(final ApiKeyStore keyStore, this.principalsHelper = checkNotNull(principalsHelper); this.format = checkNotNull(format); setName(format); - setAuthenticationCachingEnabled(false); + setAuthenticationCachingEnabled(true); } @Inject diff --git a/components/nexus-security/src/test/java/org/sonatype/nexus/security/token/BearerTokenRealmTest.java b/components/nexus-security/src/test/java/org/sonatype/nexus/security/token/BearerTokenRealmTest.java index d3c0522723..ebe681c7d5 100644 --- a/components/nexus-security/src/test/java/org/sonatype/nexus/security/token/BearerTokenRealmTest.java +++ b/components/nexus-security/src/test/java/org/sonatype/nexus/security/token/BearerTokenRealmTest.java @@ -168,6 +168,6 @@ public void anonymousAccessNotSupportedByDefault() throws Exception { @Test public void cachingEnabled() { - assertThat(underTest.isAuthenticationCachingEnabled(), is(false)); + assertThat(underTest.isAuthenticationCachingEnabled(), is(true)); } } diff --git a/components/nexus-validation/src/main/java/org/sonatype/nexus/validation/constraint/AnyOf.java b/components/nexus-validation/src/main/java/org/sonatype/nexus/validation/constraint/AnyOf.java new file mode 100644 index 0000000000..d2cb50e4b2 --- /dev/null +++ b/components/nexus-validation/src/main/java/org/sonatype/nexus/validation/constraint/AnyOf.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.validation.constraint; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import javax.validation.Constraint; +import javax.validation.Payload; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE}) +@Retention(RUNTIME) +@Constraint(validatedBy = AnyOfValidator.class) +public @interface AnyOf +{ + + String message() default "%s must be one of the following : [%s]"; + + String parameterName() default "value"; + + Class[] groups() default { }; + + Class[] payload() default { }; + + Class> enumClass(); +} diff --git a/components/nexus-validation/src/main/java/org/sonatype/nexus/validation/constraint/AnyOfValidator.java b/components/nexus-validation/src/main/java/org/sonatype/nexus/validation/constraint/AnyOfValidator.java new file mode 100644 index 0000000000..c0875e6df5 --- /dev/null +++ b/components/nexus-validation/src/main/java/org/sonatype/nexus/validation/constraint/AnyOfValidator.java @@ -0,0 +1,68 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.validation.constraint; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.inject.Named; +import javax.validation.ConstraintValidatorContext; + +import org.sonatype.nexus.validation.ConstraintValidatorSupport; + +@Named +public class AnyOfValidator + extends ConstraintValidatorSupport +{ + Set values; + + String paramName; + + String message; + + public AnyOfValidator() { + } + + @Override + public void initialize(final AnyOf annotation) { + Class> enumClass = annotation.enumClass(); + + paramName = annotation.parameterName(); + message = annotation.message(); + values = Stream.of(enumClass.getEnumConstants()) + .map(Enum::name) + .map(String::toLowerCase) + .collect(Collectors.toSet()); + } + + @Override + public boolean isValid(final String value, final ConstraintValidatorContext context) { + if (value == null) { + return true; + } + + boolean isValid = values.contains(value.toLowerCase()); + + if (!isValid) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(getEscapeHelper().stripJavaEl(buildMessage())) + .addConstraintViolation(); + } + + return isValid; + } + + private String buildMessage() { + return String.format(message, paramName, String.join(",", values)); + } +} diff --git a/nxrm.groovy b/nxrm.groovy index 71a5e3c7e4..ef3d0633b9 100644 --- a/nxrm.groovy +++ b/nxrm.groovy @@ -862,8 +862,8 @@ def runDeploy() { def deploy() { extract("./assemblies/nexus-base-template/target/", "nexus-base-template-*.zip") - extract("./private/assemblies/nexus-oss/target/", "nexus-*-bundle.zip") - extract("./private/assemblies/nexus-pro/target/", "nexus-professional-*-bundle.zip") + extract("./private/assemblies/distributions/nexus-oss/target/", "nexus-*-bundle.zip") + extract("./private/assemblies/distributions/nexus-pro/target/", "nexus-professional-*-bundle.zip") // Tell Karaf to load bundles from local .m2 folder def files = new FileNameFinder().getFileNames("$TARGET_DIR", "nexus*/**/org.ops4j.pax.url.mvn.cfg") diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/CleanupPolicies/CleanupPoliciesDryRun.jsx b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/CleanupPolicies/CleanupPoliciesDryRun.jsx index 2504052b60..30bd487ebd 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/CleanupPolicies/CleanupPoliciesDryRun.jsx +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/CleanupPolicies/CleanupPoliciesDryRun.jsx @@ -14,7 +14,6 @@ import React, {useEffect} from 'react'; import {useMachine} from '@xstate/react'; import { - NxButton, NxButtonBar, NxFormRow, NxFormSelect, @@ -22,7 +21,7 @@ import { NxLoadWrapper, NxP } from '@sonatype/react-shared-components'; -import {FormUtils} from '@sonatype/nexus-ui-plugin'; +import {ExtJS, FormUtils} from '@sonatype/nexus-ui-plugin'; import UIStrings from '../../../../constants/UIStrings'; import cleanupPoliciesDryRunMachine from './CleanupPoliciesDryRunMachine'; @@ -33,6 +32,13 @@ export default function CleanupPoliciesDryRun({policyData}) { const [state, send] = useMachine(cleanupPoliciesDryRunMachine, {devTools: true}); const {loadError, repository, repositories} = state.context; const isLoading = state.matches('loading'); + const generateCSVUrl = ExtJS.urlOf('/service/rest/internal/cleanup-policies/preview/components/csv' + + '?repository=' + repository + + (policyData.name ? `&name=${policyData.name}` : '') + + (policyData.criteriaLastBlobUpdated ? `&criteriaLastBlobUpdated=${policyData.criteriaLastBlobUpdated}` : '') + + (policyData.criteriaLastDownloaded ? `&criteriaLastDownloaded=${policyData.criteriaLastDownloaded}` : '') + + (policyData.criteriaReleaseType ? `&criteriaReleaseType=${policyData.criteriaReleaseType}` : '') + + (policyData.criteriaAssetRegex ? `&criteriaAssetRegex=${policyData.criteriaAssetRegex}` : '')) useEffect(() => { send({type: 'LOAD_REPOSITORIES', format: policyData.format}); @@ -46,10 +52,6 @@ export default function CleanupPoliciesDryRun({policyData}) { send({type: 'RETRY', format: policyData.format}); } - function createCSVReport() { - send({type: 'CREATE_CSV_REPORT', policyData}); - } - return
{() => @@ -68,7 +70,13 @@ export default function CleanupPoliciesDryRun({policyData}) { )} - {BUTTON} + + {BUTTON} + diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/CleanupPolicies/CleanupPoliciesDryRunMachine.js b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/CleanupPolicies/CleanupPoliciesDryRunMachine.js index 4c567e9bc8..006f03f879 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/CleanupPolicies/CleanupPoliciesDryRunMachine.js +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/CleanupPolicies/CleanupPoliciesDryRunMachine.js @@ -18,8 +18,6 @@ import {assign, createMachine} from 'xstate'; import Axios from 'axios'; const INITIAL_DATA = { - CSVContent: null, - filename: '', loadError: null, repositories: [], repository: '' @@ -36,9 +34,6 @@ const cleanupPoliciesDryRunMachine = createMachine({ on: { SET_REPOSITORY: { actions: 'setRepository' - }, - CREATE_CSV_REPORT: { - target: 'creatingCSVReport' } } }, @@ -64,15 +59,6 @@ const cleanupPoliciesDryRunMachine = createMachine({ actions: 'clearError' } } - }, - creatingCSVReport: { - invoke: { - src: 'create', - onDone: { - target: 'loaded', - actions: ['setResData', 'downloadCSV'] - } - } } }, @@ -85,8 +71,6 @@ const cleanupPoliciesDryRunMachine = createMachine({ }, { actions: { resetData: assign({ - CSVContent: INITIAL_DATA.CSVContent, - filename: INITIAL_DATA.filename, loadError: INITIAL_DATA.loadError, repositories: INITIAL_DATA.repositories, repository: INITIAL_DATA.repository @@ -106,36 +90,13 @@ const cleanupPoliciesDryRunMachine = createMachine({ clearError: assign({ loadError: () => null - }), - - setResData: assign({ - filename: (_, event) => event.data?.headers['content-disposition'].split('filename=')[1], - CSVContent: (_, event) => event.data?.data - }), - - downloadCSV: ({CSVContent, filename}) => { - const link = document.createElement('a'); - const blob = new Blob([CSVContent]); - link.href = URL.createObjectURL(blob); - link.download = filename; - link.click(); - URL.revokeObjectURL(link.href); - } + }) }, guards: { canLoadRepositories: (_, {format}) => Boolean(format) }, services: { - fetchRepositories: (_, {format}) => Axios.get('/service/rest/internal/ui/repositories', {params: {format}}), - create: ({repository}, {policyData}) => Axios.post( - '/service/rest/internal/cleanup-policies/preview/components/csv', { - repository, - criteriaLastBlobUpdated: policyData.criteriaLastBlobUpdated, - criteriaLastDownloaded: policyData.criteriaLastDownloaded, - criteriaReleaseType: policyData.criteriaReleaseType, - criteriaAssetRegex: policyData.criteriaAssetRegex, - name: policyData.name - }) + fetchRepositories: (_, {format}) => Axios.get('/service/rest/internal/ui/repositories', {params: {format}}) } }); diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/CleanupPolicies/CleanupPoliciesForm.test.jsx b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/CleanupPolicies/CleanupPoliciesForm.test.jsx index b7aca2998c..9d2f3b2e55 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/CleanupPolicies/CleanupPoliciesForm.test.jsx +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/CleanupPolicies/CleanupPoliciesForm.test.jsx @@ -42,6 +42,7 @@ jest.mock('@sonatype/nexus-ui-plugin', () => ({ ...jest.requireActual('@sonatype/nexus-ui-plugin'), ExtJS: { requestConfirmation: jest.fn(), + urlOf: jest.fn(), }, })); @@ -92,7 +93,7 @@ const selectors = { description: UIStrings.CLEANUP_POLICIES.DRY_RUN.REPOSITORY_DESCRIPTION, }), dryRunCreateCSVButton: () => - screen.getByRole('button', {name: LABELS.DRY_RUN.BUTTON}), + screen.getByRole('link', {name: LABELS.DRY_RUN.BUTTON}), }; describe('CleanupPoliciesForm', function () { @@ -610,17 +611,17 @@ describe('CleanupPoliciesForm', function () { createButton = dryRunCreateCSVButton(); expect(selectDropdown).toHaveValue(''); - expect(createButton).toBeDisabled(); + expect(createButton).toHaveAttribute('aria-disabled', 'true'); userEvent.selectOptions(selectDropdown, 'maven-central'); expect(selectDropdown).toHaveValue('maven-central'); - expect(createButton).not.toBeDisabled(); + expect(createButton).toHaveAttribute('aria-disabled', 'false'); userEvent.selectOptions(selectDropdown, ''); expect(selectDropdown).toHaveValue(''); - expect(createButton).toBeDisabled(); + expect(createButton).toHaveAttribute('aria-disabled', 'true'); }); }); }); diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/NodeCard.jsx b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/NodeCard.jsx index dad8e4e898..25b2c6bcda 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/NodeCard.jsx +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/NodeCard.jsx @@ -29,12 +29,13 @@ import {faCheckCircle, faTimesCircle} from '@fortawesome/free-solid-svg-icons'; import './SupportZipHa.scss'; import NodeCardMachine from './NodeCardMachine'; +import {URL} from './NodeCardHelper' const {SUPPORT_ZIP: LABELS} = UIStrings; const NODE_UNAVAILABLE = 'NODE_UNAVAILABLE'; -export default function NodeCard({initial, createZip, downloadZip}) { +export default function NodeCard({initial, createZip}) { const [current] = useMachine(NodeCardMachine, { context: { node: initial @@ -98,7 +99,9 @@ export default function NodeCard({initial, createZip, downloadZip}) { {zipNotCreated && {statusActionLabel}} {zipCreated && ( - downloadZip(node)}>{statusActionLabel} + + {statusActionLabel} + )} {zipCreating && ( diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/NodeCard.test.jsx b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/NodeCard.test.jsx index 0199dc9c8b..0568ab4e62 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/NodeCard.test.jsx +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/NodeCard.test.jsx @@ -19,8 +19,6 @@ import NodeCardTestData from './NodeCard.testdata'; describe('NodeCard', function () { const testNodes = NodeCardTestData; - const NODE_ACTIVE_INDICATOR = 'Node Active'; - const DOWNLOAD_ZIP_STATUS = 'Download Zip'; const CREATE_ZIP_STATUS = 'Create Support zip'; const CREATING_ZIP_STATUS = 'Creating Zip...'; diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/NodeCardHelper.js b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/NodeCardHelper.js new file mode 100644 index 0000000000..fbd3e5bc15 --- /dev/null +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/NodeCardHelper.js @@ -0,0 +1,21 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Open Source Version is distributed with Sencha Ext JS pursuant to a FLOSS Exception agreed upon + * between Sonatype, Inc. and Sencha Inc. Sencha Ext JS is licensed under GPL v3 and cannot be redistributed as part of a + * closed source work. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +const downloadZipFile = (blobRef) => + `/service/rest/wonderland/download/${blobRef}`; + +export const URL = {downloadZipFile}; + diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/SupportZipHa.jsx b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/SupportZipHa.jsx index 531ca618eb..73ccc58f19 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/SupportZipHa.jsx +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/SupportZipHa.jsx @@ -68,15 +68,6 @@ export default function SupportZipHa() { }); } - function downloadZip(node) { - sendToNxrmNodes({ - type: 'DOWNLOAD_ZIP', - data: { - node: node - } - }); - } - function submitCreateNodeSupportZip() { closeSupportZipFormModal(); @@ -106,8 +97,7 @@ export default function SupportZipHa() { showCreateZipModalForm(node)} - downloadZip={downloadZip}/> + createZip={() => showCreateZipModalForm(node)}/> )} diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/SupportZipHaMachine.js b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/SupportZipHaMachine.js index ea4b088242..f0c3b06a17 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/SupportZipHaMachine.js +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/SupportZip/HA/SupportZipHaMachine.js @@ -17,7 +17,7 @@ import Axios from 'axios'; import {assign} from 'xstate'; -import {APIConstants, ExtJS, FormUtils, UIStrings} from "@sonatype/nexus-ui-plugin"; +import {APIConstants, FormUtils} from "@sonatype/nexus-ui-plugin"; export default FormUtils.buildFormMachine({ id: 'SupportZipHaMachine', @@ -50,17 +50,6 @@ export default FormUtils.buildFormMachine({ CREATE_SUPPORT_ZIP_FOR_NODE: { target: 'creatingNodeSupportZip' }, - DOWNLOAD_ZIP: { - target: 'initSupportZipDownload', - actions: 'setTargetNode' - } - } - }, - - downloadingZip: { - entry: 'downloadZip', - always: { - target: 'loaded' } }, @@ -79,19 +68,6 @@ export default FormUtils.buildFormMachine({ target: 'loaded' } }, - - initSupportZipDownload: { - invoke: { - src: 'verifyIsZipCanBeDownloaded', - onDone: { - target: 'downloadingZip' - }, - onError: { - target: 'loaded', - actions: ['setCreateError'] - } - } - } } }) }).withConfig({ @@ -109,23 +85,6 @@ export default FormUtils.buildFormMachine({ showCreateZipModal: false, selectedNode: null }), - - downloadZip: (ctx) => { - const { targetNode } = ctx; - - if (targetNode) { - const url = ExtJS.urlOf(`service/rest/wonderland/download/${targetNode.blobRef}`); - ExtJS.downloadUrl(url); - } - }, - - setCreateError: () => { - ExtJS.showErrorMessage(UIStrings.ERROR.NOT_FOUND_ERROR("Support zip")) - }, - - setTargetNode: assign({ - targetNode: (_, event) => event?.data?.node - }) }, services: { @@ -145,15 +104,5 @@ export default FormUtils.buildFormMachine({ } return Promise.reject(); }, - - verifyIsZipCanBeDownloaded: async (ctx) => { - const { targetNode } = ctx; - - const { data } = await axios.get(APIConstants.REST.PUBLIC.NODE_ID) - if (targetNode && data.nodeId === targetNode.nodeId) { - return Promise.resolve(); - } - return Promise.reject(); - } } }); diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/OrientAptUploadHandler.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/OrientAptUploadHandler.java index c145088260..887f92ff7d 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/OrientAptUploadHandler.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/OrientAptUploadHandler.java @@ -67,6 +67,7 @@ public UploadResponse handle(final Repository repository, final ComponentUpload UnitOfWork.begin(storageFacet.txSupplier()); try { Asset asset = hostedFacet.ingestAsset(upload.getAssetUploads().get(0).getPayload()); + hostedFacet.invalidateMetadata(); return new UploadResponse(asset); } finally { diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/OrientAptComponentDirector.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/OrientAptComponentDirector.java index 9de4cd954f..9a3524bd8f 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/OrientAptComponentDirector.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/OrientAptComponentDirector.java @@ -74,15 +74,9 @@ public boolean allowMoveFrom(final Repository source) { @Override public void afterMove(final List> components, final Repository destination) { - destination.facet(StorageFacet.class).txSupplier(); UnitOfWork.begin(destination.facet(StorageFacet.class).txSupplier()); try { - OrientAptHostedFacet hostedFacet = destination.facet(OrientAptHostedFacet.class); - hostedFacet.rebuildIndexes(); - } - catch (IOException e) { - log.error("Failed to update metadata, repository: {}", destination.getName(), e); - throw new UncheckedIOException(e); + destination.facet(OrientAptHostedFacet.class).invalidateMetadata(); } finally { UnitOfWork.end(); diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedComponentMaintenanceFacet.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedComponentMaintenanceFacet.java index c2c59b9461..22e3081d37 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedComponentMaintenanceFacet.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedComponentMaintenanceFacet.java @@ -12,8 +12,6 @@ */ package org.sonatype.nexus.repository.apt.orient.internal.hosted; -import java.io.IOException; -import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -58,13 +56,7 @@ protected Set deleteAssetTx(final EntityId assetId, final boolean delete String assetKind = asset.formatAttributes().get(P_ASSET_KIND, String.class); Set result = super.deleteAssetTx(assetId, deleteBlobs); if ("DEB".equals(assetKind)) { - try { - getRepository().facet(OrientAptHostedFacet.class) - .rebuildIndexes(Collections.singletonList(new OrientAptHostedFacet.AssetChange(AssetAction.REMOVED, asset))); - } - catch (IOException e) { - throw new UncheckedIOException(e); - } + getRepository().facet(OrientAptHostedFacet.class).invalidateMetadata(); } if (asset.componentId() != null) { @@ -97,12 +89,7 @@ protected DeletionResult deleteComponentTx(final EntityId componentId, final boo log.debug("Deleting component: {}", component.toStringExternal()); DeletionResult result = new DeletionResult(component, tx.deleteComponent(component, deleteBlobs)); - try { - getRepository().facet(OrientAptHostedFacet.class).rebuildIndexes(changes); - } - catch (IOException e) { - throw new UncheckedIOException(e); - } + getRepository().facet(OrientAptHostedFacet.class).invalidateMetadata(); return result; } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedFacet.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedFacet.java index 27a2690d13..d432b73abc 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedFacet.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedFacet.java @@ -13,65 +13,40 @@ package org.sonatype.nexus.repository.apt.orient.internal.hosted; import java.io.IOException; -import java.io.Writer; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.stream.Collectors; - import javax.inject.Named; import org.sonatype.nexus.common.hash.HashAlgorithm; -import org.sonatype.nexus.orient.entity.AttachedEntityHelper; import org.sonatype.nexus.repository.Facet; import org.sonatype.nexus.repository.FacetSupport; import org.sonatype.nexus.repository.IllegalOperationException; -import org.sonatype.nexus.repository.apt.internal.hosted.AssetAction; -import org.sonatype.nexus.repository.apt.orient.OrientAptFacet; import org.sonatype.nexus.repository.apt.internal.AptFacetHelper; -import org.sonatype.nexus.repository.apt.internal.AptMimeTypes; import org.sonatype.nexus.repository.apt.internal.AptPackageParser; import org.sonatype.nexus.repository.apt.internal.debian.ControlFile; import org.sonatype.nexus.repository.apt.internal.debian.ControlFile.Paragraph; import org.sonatype.nexus.repository.apt.internal.debian.PackageInfo; -import org.sonatype.nexus.repository.apt.internal.gpg.AptSigningFacet; -import org.sonatype.nexus.repository.apt.internal.hosted.CompressingTempFileStore; +import org.sonatype.nexus.repository.apt.internal.hosted.AssetAction; +import org.sonatype.nexus.repository.apt.orient.OrientAptFacet; +import org.sonatype.nexus.repository.apt.orient.internal.hosted.metadata.OrientAptHostedMetadataFacet; import org.sonatype.nexus.repository.storage.Asset; import org.sonatype.nexus.repository.storage.Bucket; import org.sonatype.nexus.repository.storage.StorageFacet; import org.sonatype.nexus.repository.storage.StorageTx; import org.sonatype.nexus.repository.transaction.TransactionalStoreBlob; -import org.sonatype.nexus.repository.transaction.TransactionalStoreMetadata; import org.sonatype.nexus.repository.view.Content; import org.sonatype.nexus.repository.view.Payload; -import org.sonatype.nexus.repository.view.payloads.BytesPayload; import org.sonatype.nexus.repository.view.payloads.StreamPayload; import org.sonatype.nexus.repository.view.payloads.TempBlob; import org.sonatype.nexus.transaction.UnitOfWork; -import com.google.common.base.Charsets; import com.google.common.hash.HashCode; -import com.orientechnologies.orient.core.record.impl.ODocument; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.time.DateFormatUtils; -import static java.util.Collections.singletonList; -import static org.apache.http.protocol.HttpDateGenerator.PATTERN_RFC1123; import static org.sonatype.nexus.common.hash.HashAlgorithm.MD5; import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA1; import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA256; -import static org.sonatype.nexus.repository.apt.internal.ReleaseName.INRELEASE; -import static org.sonatype.nexus.repository.apt.internal.ReleaseName.RELEASE; -import static org.sonatype.nexus.repository.apt.internal.ReleaseName.RELEASE_GPG; import static org.sonatype.nexus.repository.storage.AssetEntityAdapter.P_ASSET_KIND; -import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_BUCKET; -import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_NAME; /** * @since 3.17 @@ -82,18 +57,12 @@ public class OrientAptHostedFacet extends FacetSupport { private static final String P_INDEX_SECTION = "index_section"; + private static final String P_ARCHITECTURE = "architecture"; + private static final String P_PACKAGE_NAME = "package_name"; - private static final String P_PACKAGE_VERSION = "package_version"; - private static final String SELECT_HOSTED_ASSETS = - "SELECT " + - "name, " + - "attributes.apt.index_section AS index_section, " + - "attributes.apt.architecture AS architecture " + - "FROM asset " + - "WHERE bucket=:bucket " + - "AND attributes.apt.asset_kind=:asset_kind"; + private static final String P_PACKAGE_VERSION = "package_version"; public Asset ingestAsset(final Payload body) throws IOException { StorageFacet storageFacet = facet(StorageFacet.class); @@ -109,7 +78,9 @@ public Asset ingestAsset(final Payload body) throws IOException { } @TransactionalStoreBlob - protected Asset ingestAsset(final ControlFile control, final TempBlob body, final long size, final String contentType) throws IOException { + protected Asset ingestAsset(final ControlFile control, final TempBlob body, final long size, final String contentType) + throws IOException + { OrientAptFacet aptFacet = getRepository().facet(OrientAptFacet.class); StorageTx tx = UnitOfWork.currentTx(); Bucket bucket = tx.findBucket(getRepository()); @@ -136,125 +107,23 @@ protected Asset ingestAsset(final ControlFile control, final TempBlob body, fina asset.formatAttributes().set(P_ASSET_KIND, "DEB"); tx.saveAsset(asset); - rebuildIndexes(singletonList(new AssetChange(AssetAction.ADDED, asset))); return asset; } - public void rebuildIndexes() throws IOException { - rebuildIndexes(Collections.emptyList()); + public void rebuildMetadata() throws IOException { + metadata().rebuildMetadata(Collections.emptyList()); } - @TransactionalStoreMetadata - public void rebuildIndexes(final List changes) throws IOException { - StorageTx tx = UnitOfWork.currentTx(); - OrientAptFacet aptFacet = getRepository().facet(OrientAptFacet.class); - AptSigningFacet signingFacet = getRepository().facet(AptSigningFacet.class); - Bucket bucket = tx.findBucket(getRepository()); - - StringBuilder sha256Builder = new StringBuilder(); - StringBuilder md5Builder = new StringBuilder(); - String releaseFile; - try (CompressingTempFileStore store = buildPackageIndexes(tx, bucket, changes)) { - for (Map.Entry entry : store.getFiles().entrySet()) { - Content plainContent = aptFacet.put( - packageIndexName(entry.getKey(), ""), - new StreamPayload(entry.getValue().plainSupplier(), entry.getValue().plainSize(), AptMimeTypes.TEXT)); - addSignatureItem(md5Builder, MD5, plainContent, packageRelativeIndexName(entry.getKey(), "")); - addSignatureItem(sha256Builder, SHA256, plainContent, packageRelativeIndexName(entry.getKey(), "")); - - Content gzContent = aptFacet.put( - packageIndexName(entry.getKey(), ".gz"), - new StreamPayload(entry.getValue().gzSupplier(), entry.getValue().bzSize(), AptMimeTypes.GZIP)); - addSignatureItem(md5Builder, MD5, gzContent, packageRelativeIndexName(entry.getKey(), ".gz")); - addSignatureItem(sha256Builder, SHA256, gzContent, packageRelativeIndexName(entry.getKey(), ".gz")); - - Content bzContent = aptFacet.put( - packageIndexName(entry.getKey(), ".bz2"), - new StreamPayload(entry.getValue().bzSupplier(), entry.getValue().bzSize(), AptMimeTypes.BZIP)); - addSignatureItem(md5Builder, MD5, bzContent, packageRelativeIndexName(entry.getKey(), ".bz2")); - addSignatureItem(sha256Builder, SHA256, bzContent, packageRelativeIndexName(entry.getKey(), ".bz2")); - } - - releaseFile = buildReleaseFile(aptFacet.getDistribution(), store.getFiles().keySet(), md5Builder.toString(), - sha256Builder.toString()); - } - - aptFacet.put(releaseIndexName(RELEASE), new BytesPayload(releaseFile.getBytes(Charsets.UTF_8), AptMimeTypes.TEXT)); - byte[] inRelease = signingFacet.signInline(releaseFile); - aptFacet.put(releaseIndexName(INRELEASE), new BytesPayload(inRelease, AptMimeTypes.TEXT)); - byte[] releaseGpg = signingFacet.signExternal(releaseFile); - aptFacet.put(releaseIndexName(RELEASE_GPG), new BytesPayload(releaseGpg, AptMimeTypes.SIGNATURE)); + public void invalidateMetadata() { + metadata().invalidateMetadata(); } - private String buildReleaseFile( - final String distribution, - final Collection architectures, - final String md5, - final String sha256) + private String buildIndexSection( + final ControlFile cf, + final long size, + final Map hashes, + final String assetPath) { - String date = DateFormatUtils.format(new Date(), PATTERN_RFC1123, TimeZone.getTimeZone("GMT")); - Paragraph p = new Paragraph(Arrays.asList( - new ControlFile.ControlField("Suite", distribution), - new ControlFile.ControlField("Codename", distribution), new ControlFile.ControlField("Components", "main"), - new ControlFile.ControlField("Date", date), - new ControlFile.ControlField("Architectures", String.join(" ", architectures)), - new ControlFile.ControlField("SHA256", sha256), new ControlFile.ControlField("MD5Sum", md5))); - return p.toString(); - } - - private CompressingTempFileStore buildPackageIndexes(final StorageTx tx, final Bucket bucket, final List changes) - throws IOException - { - CompressingTempFileStore result = new CompressingTempFileStore(); - Map streams = new HashMap<>(); - boolean ok = false; - try { - Map sqlParams = new HashMap<>(); - sqlParams.put(P_BUCKET, AttachedEntityHelper.id(bucket)); - sqlParams.put(P_ASSET_KIND, "DEB"); - - // NOTE: We exclude added assets as well to account for the case where we are replacing an asset - Set excludeNames = changes.stream().map(change -> change.asset.name()).collect(Collectors.toSet()); - - Iterable browse = tx.browse(SELECT_HOSTED_ASSETS, sqlParams); - - for (ODocument document : browse) { - String name = document.field(P_NAME, String.class); - String arch = document.field(P_ARCHITECTURE, String.class); - Writer outWriter = streams.computeIfAbsent(arch, result::openOutput); - if (!excludeNames.contains(name)) { - String indexSection = document.field(P_INDEX_SECTION, String.class); - outWriter.write(indexSection); - outWriter.write("\n\n"); - } - } - - List addAssets = changes.stream().filter(change -> change.action == AssetAction.ADDED) - .map(change -> change.asset).collect(Collectors.toList()); - - // HACK: tx.browse won't see changes in the current transaction, so we have to manually add these in here - for (Asset asset : addAssets) { - String arch = asset.formatAttributes().get(P_ARCHITECTURE, String.class); - String indexSection = asset.formatAttributes().get(P_INDEX_SECTION, String.class); - Writer outWriter = streams.computeIfAbsent(arch, result::openOutput); - outWriter.write(indexSection); - outWriter.write("\n\n"); - } - ok = true; - } - finally { - for (Writer writer : streams.values()) { - IOUtils.closeQuietly(writer); - } - - if (!ok) { - result.close(); - } - } - return result; - } - - private String buildIndexSection(final ControlFile cf, final long size, final Map hashes, final String assetPath) { Paragraph modified = cf.getParagraphs().get(0) .withFields(Arrays.asList( new ControlFile.ControlField("Filename", assetPath), @@ -265,36 +134,13 @@ private String buildIndexSection(final ControlFile cf, final long size, final Ma return modified.toString(); } - private String releaseIndexName(final String name) { - OrientAptFacet aptFacet = getRepository().facet(OrientAptFacet.class); - String dist = aptFacet.getDistribution(); - return "dists/" + dist + "/" + name; - } - - private String packageIndexName(final String arch, final String ext) { - OrientAptFacet aptFacet = getRepository().facet(OrientAptFacet.class); - String dist = aptFacet.getDistribution(); - return "dists/" + dist + "/main/binary-" + arch + "/Packages" + ext; - } - - private String packageRelativeIndexName(final String arch, final String ext) { - return "main/binary-" + arch + "/Packages" + ext; - } - - private void addSignatureItem(final StringBuilder builder, final HashAlgorithm algo, final Content content, final String filename) { - Map hashMap = content.getAttributes().get(Content.CONTENT_HASH_CODES_MAP, - Content.T_CONTENT_HASH_CODES_MAP); - builder.append("\n "); - builder.append(hashMap.get(algo).toString()); - builder.append(" "); - builder.append(content.getSize()); - builder.append(" "); - builder.append(filename); + private OrientAptHostedMetadataFacet metadata() { + return facet(OrientAptHostedMetadataFacet.class); } public static class AssetChange { - final AssetAction action; + public final AssetAction action; public final Asset asset; diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedHandler.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedHandler.java index 2d7ab6b801..257b487191 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedHandler.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedHandler.java @@ -27,6 +27,11 @@ import org.sonatype.nexus.repository.view.Handler; import org.sonatype.nexus.repository.view.Response; +import org.apache.commons.lang3.StringUtils; + +import static org.sonatype.nexus.repository.apt.internal.ReleaseName.INRELEASE; +import static org.sonatype.nexus.repository.apt.internal.ReleaseName.RELEASE; +import static org.sonatype.nexus.repository.apt.internal.ReleaseName.RELEASE_GPG; import static org.sonatype.nexus.repository.http.HttpMethods.GET; import static org.sonatype.nexus.repository.http.HttpMethods.HEAD; import static org.sonatype.nexus.repository.http.HttpMethods.POST; @@ -45,31 +50,27 @@ public Response handle(final Context context) throws Exception { String path = assetPath(context); String method = context.getRequest().getAction(); - OrientAptFacet aptFacet = context.getRepository().facet(OrientAptFacet.class); - OrientAptHostedFacet hostedFacet = context.getRepository().facet(OrientAptHostedFacet.class); - switch (method) { case GET: case HEAD: - return doGet(path, aptFacet); + return doGet(context, path); case POST: - return doPost(context, path, method, hostedFacet); + return doPost(context, path, method); default: return HttpResponses.methodNotAllowed(method, GET, HEAD, POST); } } - private Response doPost(final Context context, - final String path, - final String method, - final OrientAptHostedFacet hostedFacet) throws IOException + private Response doPost(final Context context, final String path, final String method) throws IOException { + OrientAptHostedFacet hostedFacet = context.getRepository().facet(OrientAptHostedFacet.class); if ("rebuild-indexes".equals(path)) { - hostedFacet.rebuildIndexes(); + hostedFacet.rebuildMetadata(); return HttpResponses.ok(); } else if ("".equals(path)) { hostedFacet.ingestAsset(context.getRequest().getPayload()); + hostedFacet.invalidateMetadata(); return HttpResponses.created(); } else { @@ -77,11 +78,25 @@ else if ("".equals(path)) { } } - private Response doGet(final String path, final OrientAptFacet aptFacet) throws IOException { + private Response doGet(final Context context, final String path) throws IOException { + OrientAptFacet aptFacet = context.getRepository().facet(OrientAptFacet.class); + if (isMetadataRebuildRequired(path, aptFacet)) { + context.getRepository().facet(OrientAptHostedFacet.class).rebuildMetadata(); + } Optional content = aptFacet.get(path); return content.map(HttpResponses::ok).orElseGet(() -> HttpResponses.notFound(path)); } + private boolean isMetadataRebuildRequired(final String path, final OrientAptFacet aptFacet) throws IOException + { + if (StringUtils.startsWith(path, "dists") + && StringUtils.endsWithAny(path, INRELEASE, RELEASE, RELEASE_GPG, "/Packages")) { + String inReleasePath = "dists/" + aptFacet.getDistribution() + "/" + INRELEASE; + return !aptFacet.get(inReleasePath).isPresent(); + } + return false; + } + private String assetPath(final Context context) { return context.getAttributes().require(AptSnapshotHandler.State.class).assetPath; } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedRecipe.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedRecipe.java index 2d653ef064..41f16057b8 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedRecipe.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/OrientAptHostedRecipe.java @@ -28,6 +28,7 @@ import org.sonatype.nexus.repository.apt.internal.snapshot.AptSnapshotHandler; import org.sonatype.nexus.repository.apt.orient.AptRestoreFacet; import org.sonatype.nexus.repository.apt.orient.internal.OrientAptFacetImpl; +import org.sonatype.nexus.repository.apt.orient.internal.hosted.metadata.OrientAptHostedMetadataFacet; import org.sonatype.nexus.repository.attributes.AttributesFacet; import org.sonatype.nexus.repository.http.PartialFetchHandler; import org.sonatype.nexus.repository.search.ElasticSearchFacet; @@ -97,6 +98,9 @@ public class OrientAptHostedRecipe @Inject Provider searchFacet; + @Inject + Provider aptHostedMetadataFacet; + @Inject ExceptionHandler exceptionHandler; @@ -153,6 +157,7 @@ public void apply(final Repository repository) throws Exception { repository.attach(attributesFacet.get()); repository.attach(componentMaintenance.get()); repository.attach(searchFacet.get()); + repository.attach(aptHostedMetadataFacet.get()); } private ViewFacet configure(final ConfigurableViewFacet facet) { diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/metadata/OrientAptHostedMetadataFacet.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/metadata/OrientAptHostedMetadataFacet.java new file mode 100644 index 0000000000..7e9357d912 --- /dev/null +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/orient/internal/hosted/metadata/OrientAptHostedMetadataFacet.java @@ -0,0 +1,300 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.apt.orient.internal.hosted.metadata; + +import java.io.IOException; +import java.io.Writer; +import java.time.Duration; +import java.time.OffsetDateTime; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TimeZone; +import java.util.stream.Collectors; +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.nexus.common.cooperation2.Cooperation2; +import org.sonatype.nexus.common.cooperation2.Cooperation2Factory; +import org.sonatype.nexus.common.hash.HashAlgorithm; +import org.sonatype.nexus.common.time.Clock; +import org.sonatype.nexus.orient.entity.AttachedEntityHelper; +import org.sonatype.nexus.repository.Facet.Exposed; +import org.sonatype.nexus.repository.FacetSupport; +import org.sonatype.nexus.repository.apt.AptFormat; +import org.sonatype.nexus.repository.apt.internal.AptMimeTypes; +import org.sonatype.nexus.repository.apt.internal.debian.ControlFile; +import org.sonatype.nexus.repository.apt.internal.debian.ControlFile.Paragraph; +import org.sonatype.nexus.repository.apt.internal.gpg.AptSigningFacet; +import org.sonatype.nexus.repository.apt.internal.hosted.AssetAction; +import org.sonatype.nexus.repository.apt.internal.hosted.CompressingTempFileStore; +import org.sonatype.nexus.repository.apt.orient.OrientAptFacet; +import org.sonatype.nexus.repository.apt.orient.internal.hosted.OrientAptHostedFacet.AssetChange; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.StorageTx; +import org.sonatype.nexus.repository.transaction.TransactionalStoreMetadata; +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.repository.view.payloads.BytesPayload; +import org.sonatype.nexus.repository.view.payloads.StreamPayload; +import org.sonatype.nexus.transaction.UnitOfWork; + +import com.google.common.base.Charsets; +import com.google.common.hash.HashCode; +import com.orientechnologies.orient.core.record.impl.ODocument; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.time.DateFormatUtils; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.apache.http.protocol.HttpDateGenerator.PATTERN_RFC1123; +import static org.sonatype.nexus.common.hash.HashAlgorithm.MD5; +import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA256; +import static org.sonatype.nexus.repository.apt.internal.ReleaseName.INRELEASE; +import static org.sonatype.nexus.repository.apt.internal.ReleaseName.RELEASE; +import static org.sonatype.nexus.repository.apt.internal.ReleaseName.RELEASE_GPG; +import static org.sonatype.nexus.repository.storage.AssetEntityAdapter.P_ASSET_KIND; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_BUCKET; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_NAME; + +/** + * Apt metadata facet. Holds the logic for metadata recalculation. + */ +@Named(AptFormat.NAME) +@Exposed +public class OrientAptHostedMetadataFacet + extends FacetSupport +{ + private final Clock clock; + + private final Cooperation2Factory.Builder cooperationBuilder; + + private Cooperation2 cooperation; + + private static final String REBUILD_COOPERATION_KEY = "rebuild-apt-metadata"; + + private static final String P_INDEX_SECTION = "index_section"; + + private static final String P_ARCHITECTURE = "architecture"; + + private static final String SELECT_HOSTED_ASSETS = + "SELECT " + + "name, " + + "attributes.apt.index_section AS index_section, " + + "attributes.apt.architecture AS architecture " + + "FROM asset " + + "WHERE bucket=:bucket " + + "AND attributes.apt.asset_kind=:asset_kind"; + + @Inject + public OrientAptHostedMetadataFacet( + final Clock clock, + final Cooperation2Factory cooperationFactory, + @Named("${nexus.apt.metadata.cooperation.enabled:-true}") final boolean cooperationEnabled, + @Named("${nexus.apt.metadata.cooperation.majorTimeout:-0s}") final Duration majorTimeout, + @Named("${nexus.apt.metadata.cooperation.minorTimeout:-30s}") final Duration minorTimeout, + @Named("${nexus.apt.metadata.cooperation.threadsPerKey:-100}") final int threadsPerKey) + { + this.clock = checkNotNull(clock); + this.cooperationBuilder = checkNotNull(cooperationFactory).configure() + .enabled(cooperationEnabled) + .majorTimeout(majorTimeout) + .minorTimeout(minorTimeout) + .threadsPerKey(threadsPerKey); + } + + @Override + protected void doInit(final Configuration configuration) throws Exception { + super.doInit(configuration); + this.cooperation = cooperationBuilder.build(getRepository().getName() + ":repomd"); + } + + public void invalidateMetadata() { + log.debug("Invalidating repository metadata: {}", getRepository().getName()); + try { + getRepository().facet(OrientAptFacet.class).delete(releaseIndexName(INRELEASE)); + } + catch (IOException e) { + log.error("Failed to invalidate repository metadata: {}", getRepository().getName(), e); + } + } + + public Optional rebuildMetadata(final List changeList) throws IOException { + return Optional.ofNullable( + cooperation + .on(() -> doRebuildMetadata(changeList)) + .cooperate(REBUILD_COOPERATION_KEY) + ); + } + + @TransactionalStoreMetadata + Content doRebuildMetadata(final List changes) throws IOException { + log.debug("Starting rebuilding metadata at {}", getRepository().getName()); + OffsetDateTime rebuildStart = clock.clusterTime(); + + StorageTx tx = UnitOfWork.currentTx(); + OrientAptFacet aptFacet = getRepository().facet(OrientAptFacet.class); + AptSigningFacet signingFacet = getRepository().facet(AptSigningFacet.class); + Bucket bucket = tx.findBucket(getRepository()); + + StringBuilder sha256Builder = new StringBuilder(); + StringBuilder md5Builder = new StringBuilder(); + String releaseFile; + try (CompressingTempFileStore store = buildPackageIndexes(tx, bucket, changes)) { + for (Map.Entry entry : store.getFiles().entrySet()) { + Content plainContent = aptFacet.put( + packageIndexName(entry.getKey(), ""), + new StreamPayload(entry.getValue().plainSupplier(), entry.getValue().plainSize(), AptMimeTypes.TEXT)); + addSignatureItem(md5Builder, MD5, plainContent, packageRelativeIndexName(entry.getKey(), "")); + addSignatureItem(sha256Builder, SHA256, plainContent, packageRelativeIndexName(entry.getKey(), "")); + + Content gzContent = aptFacet.put( + packageIndexName(entry.getKey(), ".gz"), + new StreamPayload(entry.getValue().gzSupplier(), entry.getValue().bzSize(), AptMimeTypes.GZIP)); + addSignatureItem(md5Builder, MD5, gzContent, packageRelativeIndexName(entry.getKey(), ".gz")); + addSignatureItem(sha256Builder, SHA256, gzContent, packageRelativeIndexName(entry.getKey(), ".gz")); + + Content bzContent = aptFacet.put( + packageIndexName(entry.getKey(), ".bz2"), + new StreamPayload(entry.getValue().bzSupplier(), entry.getValue().bzSize(), AptMimeTypes.BZIP)); + addSignatureItem(md5Builder, MD5, bzContent, packageRelativeIndexName(entry.getKey(), ".bz2")); + addSignatureItem(sha256Builder, SHA256, bzContent, packageRelativeIndexName(entry.getKey(), ".bz2")); + } + + releaseFile = buildReleaseFile(aptFacet.getDistribution(), store.getFiles().keySet(), md5Builder.toString(), + sha256Builder.toString()); + } + + Content releaseFileContent = aptFacet.put(releaseIndexName(RELEASE), + new BytesPayload(releaseFile.getBytes(Charsets.UTF_8), AptMimeTypes.TEXT)); + byte[] inRelease = signingFacet.signInline(releaseFile); + aptFacet.put(releaseIndexName(INRELEASE), new BytesPayload(inRelease, AptMimeTypes.TEXT)); + byte[] releaseGpg = signingFacet.signExternal(releaseFile); + aptFacet.put(releaseIndexName(RELEASE_GPG), new BytesPayload(releaseGpg, AptMimeTypes.SIGNATURE)); + + if (log.isDebugEnabled()) { + long finishTime = System.currentTimeMillis(); + log.debug("Completed metadata rebuild in {}", finishTime - rebuildStart.toInstant().toEpochMilli()); + } + + return releaseFileContent; + } + + private CompressingTempFileStore buildPackageIndexes( + final StorageTx tx, + final Bucket bucket, + final List changes) + throws IOException + { + CompressingTempFileStore result = new CompressingTempFileStore(); + Map streams = new HashMap<>(); + boolean ok = false; + try { + Map sqlParams = new HashMap<>(); + sqlParams.put(P_BUCKET, AttachedEntityHelper.id(bucket)); + sqlParams.put(P_ASSET_KIND, "DEB"); + + // NOTE: We exclude added assets as well to account for the case where we are replacing an asset + Set excludeNames = changes.stream().map(change -> change.asset.name()).collect(Collectors.toSet()); + + Iterable browse = tx.browse(SELECT_HOSTED_ASSETS, sqlParams); + + for (ODocument document : browse) { + String name = document.field(P_NAME, String.class); + String arch = document.field(P_ARCHITECTURE, String.class); + Writer outWriter = streams.computeIfAbsent(arch, result::openOutput); + if (!excludeNames.contains(name)) { + String indexSection = document.field(P_INDEX_SECTION, String.class); + outWriter.write(indexSection); + outWriter.write("\n\n"); + } + } + + List addAssets = changes.stream().filter(change -> change.action == AssetAction.ADDED) + .map(change -> change.asset).collect(Collectors.toList()); + + // HACK: tx.browse won't see changes in the current transaction, so we have to manually add these in here + for (Asset asset : addAssets) { + String arch = asset.formatAttributes().get(P_ARCHITECTURE, String.class); + String indexSection = asset.formatAttributes().get(P_INDEX_SECTION, String.class); + Writer outWriter = streams.computeIfAbsent(arch, result::openOutput); + outWriter.write(indexSection); + outWriter.write("\n\n"); + } + ok = true; + } + finally { + for (Writer writer : streams.values()) { + IOUtils.closeQuietly(writer); + } + + if (!ok) { + result.close(); + } + } + return result; + } + + private void addSignatureItem( + final StringBuilder builder, + final HashAlgorithm algo, + final Content content, + final String filename) + { + Map hashMap = content.getAttributes().get(Content.CONTENT_HASH_CODES_MAP, + Content.T_CONTENT_HASH_CODES_MAP); + builder.append("\n "); + builder.append(hashMap.get(algo).toString()); + builder.append(" "); + builder.append(content.getSize()); + builder.append(" "); + builder.append(filename); + } + + private String buildReleaseFile( + final String distribution, + final Collection architectures, + final String md5, + final String sha256) + { + String date = DateFormatUtils.format(new Date(), PATTERN_RFC1123, TimeZone.getTimeZone("GMT")); + Paragraph p = new Paragraph(Arrays.asList( + new ControlFile.ControlField("Suite", distribution), + new ControlFile.ControlField("Codename", distribution), new ControlFile.ControlField("Components", "main"), + new ControlFile.ControlField("Date", date), + new ControlFile.ControlField("Architectures", String.join(" ", architectures)), + new ControlFile.ControlField("SHA256", sha256), new ControlFile.ControlField("MD5Sum", md5))); + return p.toString(); + } + + private String packageIndexName(final String arch, final String ext) { + OrientAptFacet aptFacet = getRepository().facet(OrientAptFacet.class); + String dist = aptFacet.getDistribution(); + return "dists/" + dist + "/main/binary-" + arch + "/Packages" + ext; + } + + private String packageRelativeIndexName(final String arch, final String ext) { + return "main/binary-" + arch + "/Packages" + ext; + } + + private String releaseIndexName(final String name) { + OrientAptFacet aptFacet = getRepository().facet(OrientAptFacet.class); + String dist = aptFacet.getDistribution(); + return "dists/" + dist + "/" + name; + } +} diff --git a/plugins/nexus-repository-apt/src/test/java/org/sonatype/nexus/repository/apt/orient/internal/OrientAptComponentDirectorTest.java b/plugins/nexus-repository-apt/src/test/java/org/sonatype/nexus/repository/apt/orient/internal/OrientAptComponentDirectorTest.java index 110249dc04..134de221ed 100644 --- a/plugins/nexus-repository-apt/src/test/java/org/sonatype/nexus/repository/apt/orient/internal/OrientAptComponentDirectorTest.java +++ b/plugins/nexus-repository-apt/src/test/java/org/sonatype/nexus/repository/apt/orient/internal/OrientAptComponentDirectorTest.java @@ -91,18 +91,6 @@ public void refreshMetadataAfterMoveTest() throws IOException OrientAptComponentDirector director = new OrientAptComponentDirector(bucketStore, repositoryManager); director.afterMove(Collections.emptyList(), destination); - verify(sourceFacet).rebuildIndexes(); - } - - @Test(expected = UncheckedIOException.class) - public void afterMoveHookIoExceptionTest() throws IOException - { - when(destination.facet(StorageFacet.class)).thenReturn(storageFacet); - when(storageFacet.txSupplier()).thenReturn(() -> tx); - when(destination.facet(OrientAptHostedFacet.class)).thenReturn(sourceFacet); - - OrientAptComponentDirector director = new OrientAptComponentDirector(bucketStore, repositoryManager); - doThrow(new IOException()).when(sourceFacet).rebuildIndexes(); - director.afterMove(Collections.emptyList(), destination); + verify(sourceFacet).invalidateMetadata(); } } diff --git a/plugins/nexus-repository-maven/pom.xml b/plugins/nexus-repository-maven/pom.xml index cad4316aa2..8deef13754 100644 --- a/plugins/nexus-repository-maven/pom.xml +++ b/plugins/nexus-repository-maven/pom.xml @@ -79,6 +79,12 @@ provided + + org.sonatype.nexus + nexus-core + provided + + org.apache.maven maven-model diff --git a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/content/maven/internal/recipe/MavenProxyFacet.java b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/content/maven/internal/recipe/MavenProxyFacet.java index 55cbc62466..c449302194 100644 --- a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/content/maven/internal/recipe/MavenProxyFacet.java +++ b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/content/maven/internal/recipe/MavenProxyFacet.java @@ -13,8 +13,8 @@ package org.sonatype.nexus.content.maven.internal.recipe; import java.io.IOException; +import java.net.URI; import java.util.Objects; - import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.inject.Inject; @@ -29,6 +29,7 @@ import org.sonatype.nexus.repository.content.facet.ContentProxyFacetSupport; import org.sonatype.nexus.repository.maven.LayoutPolicy; import org.sonatype.nexus.repository.maven.MavenPath; +import org.sonatype.nexus.repository.maven.MavenProxyRequestHeaderSupport; import org.sonatype.nexus.repository.maven.internal.Constants; import org.sonatype.nexus.repository.proxy.ProxyFacetSupport; import org.sonatype.nexus.repository.view.Content; @@ -36,6 +37,8 @@ import org.sonatype.nexus.validation.ConstraintViolationFactory; import com.google.common.collect.ImmutableSet; +import com.google.common.net.HttpHeaders; +import org.apache.http.client.methods.HttpRequestBase; import static com.google.common.base.Preconditions.checkNotNull; import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_ATTRIBUTES; @@ -49,11 +52,19 @@ public class MavenProxyFacet extends ContentProxyFacetSupport { + private static final String MAVEN_CENTRAL_HOST = "repo1.maven.org"; + final ConstraintViolationFactory constraintViolationFactory; + private final MavenProxyRequestHeaderSupport mavenProxyRequestHeaderSupport; + @Inject - public MavenProxyFacet(final ConstraintViolationFactory constraintViolationFactory) { + public MavenProxyFacet( + final ConstraintViolationFactory constraintViolationFactory, + final MavenProxyRequestHeaderSupport mavenProxyRequestHeaderSupport) + { this.constraintViolationFactory = checkNotNull(constraintViolationFactory); + this.mavenProxyRequestHeaderSupport = checkNotNull(mavenProxyRequestHeaderSupport); } @Override @@ -126,6 +137,17 @@ protected String getUrl(@Nonnull final Context context) { return removePrefixingSlash(context.getRequest().getPath()); } + @Override + protected HttpRequestBase buildFetchHttpRequest(final URI uri, final Context context) { + HttpRequestBase request = super.buildFetchHttpRequest(uri, context); + String augmentedUserAgent; + if (MAVEN_CENTRAL_HOST.equals(uri.getHost())) { + augmentedUserAgent = mavenProxyRequestHeaderSupport.getUserAgentForAnalytics(); + request.setHeader(HttpHeaders.USER_AGENT, augmentedUserAgent); + } + return request; + } + @Nonnull private MavenPath mavenPath(@Nonnull final Context context) { return context.getAttributes().require(MavenPath.class); diff --git a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/MavenProxyRequestHeaderSupport.java b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/MavenProxyRequestHeaderSupport.java new file mode 100644 index 0000000000..93fe854029 --- /dev/null +++ b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/MavenProxyRequestHeaderSupport.java @@ -0,0 +1,64 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +package org.sonatype.nexus.repository.maven; + +import java.util.Collection; +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.nexus.capability.CapabilityReference; +import org.sonatype.nexus.capability.CapabilityReferenceFilterBuilder; +import org.sonatype.nexus.capability.CapabilityRegistry; +import org.sonatype.nexus.capability.CapabilityType; +import org.sonatype.nexus.utils.httpclient.UserAgentGenerator; + +import static com.google.common.base.Preconditions.checkNotNull; + +@Named +public class MavenProxyRequestHeaderSupport +{ + private static final String ANALYTICS_CAPABILITY = "analytics-configuration"; + + private final CapabilityRegistry capabilityRegistry; + private final UserAgentGenerator userAgentGenerator; + + @Inject + public MavenProxyRequestHeaderSupport( + final CapabilityRegistry capabilityRegistry, + final UserAgentGenerator userAgentGenerator) + { + this.capabilityRegistry = checkNotNull(capabilityRegistry); + this.userAgentGenerator = checkNotNull(userAgentGenerator); + } + + public String getUserAgentForAnalytics() { + CapabilityReference capabilityReference = getCapabilityReference(); + return userAgentGenerator.buildUserAgentForAnalytics(capabilityReference); + } + + @Nullable + public CapabilityReference getCapabilityReference() + { + CapabilityType capabilityType = CapabilityType.capabilityType(ANALYTICS_CAPABILITY); + Collection refs = capabilityRegistry.get( + CapabilityReferenceFilterBuilder.capabilities().withType(capabilityType).includeNotExposed()); + CapabilityReference capabilityReference = null; + if (!refs.isEmpty()) { + capabilityReference = refs.iterator().next(); + } + return capabilityReference; + } + +} diff --git a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/cleanup/Maven2CleanupPolicyConfiguration.java b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/cleanup/Maven2CleanupPolicyConfiguration.java index e1e3a3a6a8..ce354c2b46 100644 --- a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/cleanup/Maven2CleanupPolicyConfiguration.java +++ b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/cleanup/Maven2CleanupPolicyConfiguration.java @@ -22,6 +22,8 @@ import com.google.common.collect.ImmutableMap; +import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.RETAIN_KEY; +import static org.sonatype.nexus.cleanup.config.CleanupPolicyConstants.RETAIN_SORT_BY_KEY; import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.IS_PRERELEASE_KEY; import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.LAST_BLOB_UPDATED_KEY; import static org.sonatype.nexus.repository.search.DefaultComponentMetadataProducer.LAST_DOWNLOADED_KEY; @@ -39,9 +41,13 @@ public class Maven2CleanupPolicyConfiguration { @Override public Map getConfiguration() { - return ImmutableMap.of(LAST_BLOB_UPDATED_KEY, true, - LAST_DOWNLOADED_KEY, true, - IS_PRERELEASE_KEY, true, - REGEX_KEY, true); + return ImmutableMap.builder() + .put(LAST_BLOB_UPDATED_KEY, true) + .put(LAST_DOWNLOADED_KEY, true) + .put(IS_PRERELEASE_KEY, true) + .put(REGEX_KEY, true) + .put(RETAIN_KEY, true) + .put(RETAIN_SORT_BY_KEY, true) + .build(); } } diff --git a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/orient/MavenProxyFacet.java b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/orient/MavenProxyFacet.java index bbfde6a31f..be3a03249b 100644 --- a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/orient/MavenProxyFacet.java +++ b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/orient/MavenProxyFacet.java @@ -13,8 +13,8 @@ package org.sonatype.nexus.repository.maven.internal.orient; import java.io.IOException; +import java.net.URI; import java.util.Objects; - import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.inject.Inject; @@ -23,13 +23,14 @@ import javax.validation.ConstraintViolationException; import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.orient.maven.OrientMavenFacet; import org.sonatype.nexus.repository.cache.CacheController; import org.sonatype.nexus.repository.cache.CacheInfo; import org.sonatype.nexus.repository.config.Configuration; -import org.sonatype.nexus.orient.maven.OrientMavenFacet; +import org.sonatype.nexus.repository.maven.LayoutPolicy; import org.sonatype.nexus.repository.maven.MavenPath; +import org.sonatype.nexus.repository.maven.MavenProxyRequestHeaderSupport; import org.sonatype.nexus.repository.maven.internal.Constants; -import org.sonatype.nexus.repository.maven.LayoutPolicy; import org.sonatype.nexus.repository.proxy.ProxyFacetSupport; import org.sonatype.nexus.repository.storage.Asset; import org.sonatype.nexus.repository.storage.Bucket; @@ -41,6 +42,8 @@ import org.sonatype.nexus.validation.ConstraintViolationFactory; import com.google.common.collect.ImmutableSet; +import com.google.common.net.HttpHeaders; +import org.apache.http.client.methods.HttpRequestBase; import static com.google.common.base.Preconditions.checkNotNull; import static org.sonatype.nexus.repository.maven.internal.orient.MavenFacetUtils.findAsset; @@ -55,13 +58,19 @@ public class MavenProxyFacet extends ProxyFacetSupport { - private final ConstraintViolationFactory constraintViolationFactory; + private static final String MAVEN_CENTRAL_HOST = "repo1.maven.org"; + private final ConstraintViolationFactory constraintViolationFactory; private OrientMavenFacet mavenFacet; + private final MavenProxyRequestHeaderSupport mavenProxyRequestHeaderSupport; @Inject - public MavenProxyFacet(final ConstraintViolationFactory constraintViolationFactory) { + public MavenProxyFacet( + final ConstraintViolationFactory constraintViolationFactory, + final MavenProxyRequestHeaderSupport mavenProxyRequestHeaderSupport) + { this.constraintViolationFactory = checkNotNull(constraintViolationFactory); + this.mavenProxyRequestHeaderSupport = mavenProxyRequestHeaderSupport; } @Override @@ -162,6 +171,17 @@ protected String getUrl(@Nonnull final Context context) { return context.getRequest().getPath().substring(1); // omit leading slash } + @Override + protected HttpRequestBase buildFetchHttpRequest(final URI uri, final Context context) { + HttpRequestBase request = super.buildFetchHttpRequest(uri, context); + String augmentedUserAgent; + if (MAVEN_CENTRAL_HOST.equals(uri.getHost())) { + augmentedUserAgent = mavenProxyRequestHeaderSupport.getUserAgentForAnalytics(); + request.setHeader(HttpHeaders.USER_AGENT, augmentedUserAgent); + } + return request; + } + @Nonnull private MavenPath mavenPath(@Nonnull final Context context) { return context.getAttributes().require(MavenPath.class); diff --git a/plugins/nexus-repository-maven/src/main/resources/org/sonatype/nexus/content/maven/store/Maven2ComponentDAO.xml b/plugins/nexus-repository-maven/src/main/resources/org/sonatype/nexus/content/maven/store/Maven2ComponentDAO.xml index 1ada894105..b71da7ee94 100644 --- a/plugins/nexus-repository-maven/src/main/resources/org/sonatype/nexus/content/maven/store/Maven2ComponentDAO.xml +++ b/plugins/nexus-repository-maven/src/main/resources/org/sonatype/nexus/content/maven/store/Maven2ComponentDAO.xml @@ -78,25 +78,41 @@