Skip to content

Commit

Permalink
upstream: b=master,r=d6a18e2c3d6731d5c7d56ecde7710a69ea4d0c6f,t=2019-…
Browse files Browse the repository at this point in the history
…06-07-1023-08730
  • Loading branch information
sonatype-zion committed Jun 10, 2019
1 parent 78be39a commit 0593d6f
Show file tree
Hide file tree
Showing 73 changed files with 1,145 additions and 468 deletions.
7 changes: 7 additions & 0 deletions assemblies/nexus-core-feature/pom.xml
Expand Up @@ -144,6 +144,13 @@
<type>xml</type>
</dependency>

<dependency>
<groupId>org.sonatype.nexus.plugins</groupId>
<artifactId>nexus-restore-apt</artifactId>
<classifier>features</classifier>
<type>xml</type>
</dependency>

<dependency>
<groupId>org.sonatype.nexus.plugins</groupId>
<artifactId>nexus-restore-maven</artifactId>
Expand Down
1 change: 1 addition & 0 deletions assemblies/nexus-core-feature/src/main/feature/feature.xml
Expand Up @@ -28,6 +28,7 @@
<feature>nexus-repository-raw</feature>
<feature>nexus-restore-maven</feature>
<feature>nexus-blobstore-s3</feature>
<feature>nexus-restore-apt</feature>
<feature>nexus-restore-npm</feature>
<feature>nexus-restore-pypi</feature>
<feature>nexus-restore-raw</feature>
Expand Down
Expand Up @@ -85,7 +85,7 @@ public void testOnlyOurDatabasesAreReported() {

candidates.forEach(this::createDatabase);

assertThat(underTest.databases(), containsInAnyOrder("component", "config", "security", "accesslog"));
assertThat(underTest.databases(), containsInAnyOrder("component", "config", "security"));
}

@Test
Expand All @@ -109,13 +109,9 @@ public void testDatabasesAreReportedAsTheyAppear() {

assertThat(underTest.databases(), containsInAnyOrder("component", "config"));

createDatabase("accesslog");

assertThat(underTest.databases(), containsInAnyOrder("component", "config", "accesslog"));

createDatabase("security");

assertThat(underTest.databases(), containsInAnyOrder("component", "config", "security", "accesslog"));
assertThat(underTest.databases(), containsInAnyOrder("component", "config", "security"));
}

private void createDatabase(final String name) {
Expand Down
Expand Up @@ -262,4 +262,12 @@ default boolean isWritable() {
* @since 3.15
*/
boolean isStarted();

/**
* Returns true if the blobstore has no blobs within it.
*
* @return {@code true} if the blobstore has no blobs within it.
* @since 3.next
*/
boolean isEmpty();
}
Expand Up @@ -249,4 +249,9 @@ private void updateTimer(final String name, final long value) {
timer.update(value, TimeUnit.NANOSECONDS);
}
}

@Override
public boolean isEmpty() {
return !getBlobIdStream().findAny().isPresent();
}
}
Expand Up @@ -299,6 +299,11 @@ public boolean isWritable() {
return false;
}

@Override
public boolean isEmpty() {
return members.get().stream().map(BlobStore::isEmpty).reduce(true, Boolean::logicalAnd);
}

@Override
public boolean exists(final BlobId blobId) {
return members.get().stream()
Expand Down
Expand Up @@ -183,7 +183,7 @@ private void validateOnlyEmptyOrNotWritableExistingMembersRemoved(final String n
for (String existingMemberName : memberNames(currentConfiguration)) {
if (!memberNames.contains(existingMemberName)) {
BlobStore existingMember = blobStoreManager.get(existingMemberName);
if (existingMember.isWritable() || existingMember.getMetrics().getBlobCount() > 0L) {
if (existingMember.isWritable() || !existingMember.isEmpty()) {
throw new ValidationException(
format("Blob Store '%s' cannot be removed from Blob Store Group '%s', " +
"use 'Admin - Remove a member from a blob store group' task instead",
Expand Down
Expand Up @@ -15,6 +15,7 @@ package org.sonatype.nexus.blobstore.group.internal
import javax.validation.ValidationException

import org.sonatype.nexus.blobstore.BlobStoreUtil
import org.sonatype.nexus.blobstore.api.BlobId
import org.sonatype.nexus.blobstore.api.BlobStore
import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration
import org.sonatype.nexus.blobstore.api.BlobStoreManager
Expand Down Expand Up @@ -127,9 +128,8 @@ class BlobStoreGroupDescriptorTest
def 'members cant be removed directly unless read only and empty'() {
given: 'an existing blob store group with two members'
blobStores.store1 = mockBlobStore('store1', FILE)
def nonEmptyMetrics = Mock(BlobStoreMetrics)
nonEmptyMetrics.getBlobCount() >> 1L
blobStores.nonEmptyStore = mockBlobStore('nonEmptyStore', FILE, [:], true, nonEmptyMetrics)
blobStores.nonEmptyStore = mockBlobStore('nonEmptyStore', FILE, [:], true)
blobStores.nonEmptyStore.getBlobIdStream() >> [Mock(BlobId)].stream()
blobStores.group1 = mockBlobStoreGroup('group1', [blobStores.store1, blobStores.nonEmptyStore])

when: 'the member is removed'
Expand Down
Expand Up @@ -38,13 +38,8 @@ public class DatabaseInstanceNames
*/
public static final String SECURITY = "security";

/**
* Name of the database storing access log data for licensing.
*/
public static final String ACCESSLOG = "accesslog";

public static final Set<String> DATABASE_NAMES = ImmutableSet.of(
ACCESSLOG, CONFIG, COMPONENT, SECURITY);
CONFIG, COMPONENT, SECURITY);

private DatabaseInstanceNames() {
// no construction
Expand Down
Expand Up @@ -32,8 +32,8 @@
* </pre>
* Examples:
* <pre>
* accesslog-2017-07-06-11-16-49-3.4.1.bak
* accesslog-2017-07-06-11-16-49.bak
* component-2017-07-06-11-16-49-3.4.1.bak
* component-2017-07-06-11-16-49.bak
* </pre>
*
* The nxrm_version will not be present for files generated pre-3.4.1.
Expand Down
Expand Up @@ -67,7 +67,6 @@ class RestoreServiceImplTest
restorer.getPendingRestore(names.next()) >> mockRestoreFile(_, '2017-07-06-11-16-49', null)
restorer.getPendingRestore(names.next()) >> mockRestoreFile(_, '2017-07-06-11-16-50', null)
restorer.getPendingRestore(names.next()) >> mockRestoreFile(_, '2017-07-06-11-16-51', null)
restorer.getPendingRestore(names.next()) >> mockRestoreFile(_, '2017-07-06-11-16-52', null)

when: 'start is executed'
restoreService.start()
Expand Down
Expand Up @@ -78,6 +78,32 @@ Ext.define('NX.view.SignIn', {
});

me.callParent();
},

addMessage: function(message) {
var me = this,
htmlMessage = '<div id="signin-message">' + message + '</div><br>',
messageCmp = me.down('#signinMessage');

if (messageCmp) {
messageCmp.html(htmlMessage);
}
else {
me.down('form').insert(0, {
xtype: 'component',
itemId: 'signinMessage',
html: htmlMessage
});
}
},

clearMessage: function() {
var me = this,
messageCmp = me.down('#signinMessage');

if (messageCmp) {
me.down('form').remove(messageCmp);
}
}

});
@@ -0,0 +1,46 @@
/*
* 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;

import static org.apache.commons.lang.StringUtils.isEmpty;

public class ETagHeaderUtils
{
public static final String WEAK_DESIGNATOR = "W/";

private ETagHeaderUtils() {
}

/**
* Adds quotes to etag header per spec.
* https://tools.ietf.org/html/rfc7232#section-2.3
*/
public static String quote(final String etag) {
if (etag.startsWith(WEAK_DESIGNATOR)) {
return etag;
} else {
return "\"" + etag + "\"";
}
}

/**
* Removes quotes from etag header.
*/
public static String extract(final String etag) {
if (!isEmpty(etag) && etag.startsWith("\"") && etag.endsWith("\"")) {
return etag.substring(1, etag.length() - 1);
} else {
return etag;
}
}
}
Expand Up @@ -27,6 +27,7 @@
import org.sonatype.nexus.common.io.Cooperation;
import org.sonatype.nexus.common.io.CooperationFactory;
import org.sonatype.nexus.repository.BadRequestException;
import org.sonatype.nexus.repository.ETagHeaderUtils;
import org.sonatype.nexus.repository.FacetSupport;
import org.sonatype.nexus.repository.InvalidContentException;
import org.sonatype.nexus.repository.cache.CacheController;
Expand All @@ -45,7 +46,6 @@
import org.sonatype.nexus.validation.constraint.Url;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.io.Closeables;
import com.google.common.net.HttpHeaders;
import org.apache.http.Header;
Expand Down Expand Up @@ -73,6 +73,7 @@ public abstract class ProxyFacetSupport
extends FacetSupport
implements ProxyFacet
{

@VisibleForTesting
static final String CONFIG_KEY = "proxy";

Expand Down Expand Up @@ -416,7 +417,7 @@ protected Content fetch(String url, Context context, @Nullable Content stale) th
}
final String etag = stale.getAttributes().get(Content.CONTENT_ETAG, String.class);
if (etag != null) {
request.addHeader(HttpHeaders.IF_NONE_MATCH, "\"" + etag + "\"");
request.addHeader(HttpHeaders.IF_NONE_MATCH, ETagHeaderUtils.quote(etag));
}
}
log.debug("Fetching: {}", request);
Expand All @@ -435,7 +436,9 @@ protected Content fetch(String url, Context context, @Nullable Content stale) th

final Content result = createContent(context, response);
result.getAttributes().set(Content.CONTENT_LAST_MODIFIED, extractLastModified(request, response));
result.getAttributes().set(Content.CONTENT_ETAG, extractETag(response));
final Header etagHeader = response.getLastHeader(HttpHeaders.ETAG);
result.getAttributes().set(Content.CONTENT_ETAG, etagHeader == null ? null : ETagHeaderUtils.extract(etagHeader.getValue()));

result.getAttributes().set(CacheInfo.class, cacheInfo);
return result;
}
Expand Down Expand Up @@ -509,26 +512,6 @@ private DateTime extractLastModified(final HttpRequestBase request, final HttpRe
return null;
}

/**
* Extract ETag from response if possible, or {@code null}.
*/
@Nullable
private String extractETag(final HttpResponse response) {
final Header etagHeader = response.getLastHeader(HttpHeaders.ETAG);
if (etagHeader != null) {
final String etag = etagHeader.getValue();
if (!Strings.isNullOrEmpty(etag)) {
if (etag.startsWith("\"") && etag.endsWith("\"")) {
return etag.substring(1, etag.length() - 1);
}
else {
return etag;
}
}
}
return null;
}

/**
* For whatever component/asset
*/
Expand Down
Expand Up @@ -17,6 +17,7 @@
import javax.inject.Singleton;

import org.sonatype.goodies.common.ComponentSupport;
import org.sonatype.nexus.repository.ETagHeaderUtils;
import org.sonatype.nexus.repository.view.Content;
import org.sonatype.nexus.repository.view.Context;
import org.sonatype.nexus.repository.view.Handler;
Expand Down Expand Up @@ -53,7 +54,7 @@ public Response handle(@Nonnull final Context context) throws Exception {
}
final String etag = content.getAttributes().get(Content.CONTENT_ETAG, String.class);
if (etag != null) {
response.getHeaders().set(HttpHeaders.ETAG, "\"" + etag + "\"");
response.getHeaders().set(HttpHeaders.ETAG, ETagHeaderUtils.quote(etag));
}
}
return response;
Expand Down
@@ -0,0 +1,51 @@
/*
* 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;

import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

public class ETagHeaderUtilsTest
{
@Test
public void quoteStrong() {
assertEquals("\"foobar\"", ETagHeaderUtils.quote("foobar"));
}

@Test
public void quoteWeak() {
assertEquals("W/\"foobar\"", ETagHeaderUtils.quote("W/\"foobar\""));
}

@Test
public void extractNull() {
assertNull(ETagHeaderUtils.extract(null));
}

@Test
public void extractEmpty() {
assertEquals("", ETagHeaderUtils.extract(""));
}

@Test
public void extractStrong() {
assertEquals("foobar", ETagHeaderUtils.extract("\"foobar\""));
}

@Test
public void extractWeak() {
assertEquals("W/\"foobar\"", ETagHeaderUtils.extract("W/\"foobar\""));
}
}
Expand Up @@ -70,6 +70,18 @@ public void okResponse() throws Exception {
assertThat(r.getHeaders().get(HttpHeaders.ETAG), equalTo("\"etag\""));
}

@Test
public void okResponseWithWeakEtag() throws Exception {
final Content content = new Content(new StringPayload(payloadString, "text/plain"));
content.getAttributes().set(Content.CONTENT_LAST_MODIFIED, now);
content.getAttributes().set(Content.CONTENT_ETAG, "W/\"etag\"");
when(context.proceed()).thenReturn(HttpResponses.ok(content));
final Response r = subject.handle(context);
assertThat(r.getStatus().isSuccessful(), is(true));
assertThat(r.getHeaders().get(HttpHeaders.LAST_MODIFIED), equalTo(DateTimeUtils.formatDateTime(now)));
assertThat(r.getHeaders().get(HttpHeaders.ETAG), equalTo("W/\"etag\""));
}

@Test
public void okResponseNoExtraData() throws Exception {
when(context.proceed()).thenReturn(
Expand Down
Expand Up @@ -21,6 +21,8 @@ public interface AdminPasswordFileManager
{
boolean exists();

String getPath();

boolean writeFile(String password) throws IOException;

String readFile() throws IOException;
Expand Down

0 comments on commit 0593d6f

Please sign in to comment.