Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ public enum OzoneManagerVersion implements ComponentVersion {

ATOMIC_CREATE_IF_NOT_EXISTS(12,
"OzoneManager version that supports explicit create-if-not-exists key semantics"),


S3_BUCKET_CORS(13,
"OzoneManager version that supports bucket CORS configuration"),

FUTURE_VERSION(-1, "Used internally in the client when the server side is "
+ " newer and an unknown server version has arrived to the client.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.apache.hadoop.ozone.OzoneAcl;
import org.apache.hadoop.ozone.OzoneConsts;
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.helpers.CorsConfiguration;

/**
* This class encapsulates the arguments that are
Expand Down Expand Up @@ -68,6 +69,7 @@ public final class BucketArgs {
private final long quotaInNamespace;

private final String owner;
private final CorsConfiguration corsConfiguration;

/**
* Bucket Layout.
Expand All @@ -87,6 +89,7 @@ private BucketArgs(Builder b) {
bucketLayout = b.bucketLayout;
owner = b.owner;
defaultReplicationConfig = b.defaultReplicationConfig;
corsConfiguration = b.corsConfiguration;
}

/**
Expand Down Expand Up @@ -185,6 +188,10 @@ public String getOwner() {
return owner;
}

public CorsConfiguration getCorsConfiguration() {
return corsConfiguration;
}

/**
* Builder for OmBucketInfo.
*/
Expand All @@ -201,6 +208,7 @@ public static class Builder {
private BucketLayout bucketLayout;
private String owner;
private DefaultReplicationConfig defaultReplicationConfig;
private CorsConfiguration corsConfiguration;

public Builder() {
quotaInBytes = OzoneConsts.QUOTA_RESET;
Expand Down Expand Up @@ -274,6 +282,12 @@ public BucketArgs.Builder setDefaultReplicationConfig(
return this;
}

public BucketArgs.Builder setCorsConfiguration(
CorsConfiguration corsConfig) {
corsConfiguration = corsConfig;
return this;
}

/**
* Constructs the BucketArgs.
* @return instance of BucketArgs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.BasicOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.helpers.CorsConfiguration;
import org.apache.hadoop.ozone.om.helpers.ErrorInfo;
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.OmMultipartInfo;
Expand Down Expand Up @@ -151,6 +152,7 @@ public class OzoneBucket extends WithMetadata {
* Bucket Owner.
*/
private String owner;
private CorsConfiguration corsConfiguration;
/**
* Pending deletion bytes (Includes bytes retained by snapshots).
*/
Expand Down Expand Up @@ -201,6 +203,7 @@ protected OzoneBucket(Builder builder) {
this.bucketLayout = builder.bucketLayout;
}
this.owner = builder.owner;
this.corsConfiguration = builder.corsConfiguration;
}

/**
Expand Down Expand Up @@ -1148,6 +1151,21 @@ public boolean setOwner(String userName) throws IOException {
return result;
}

public CorsConfiguration getCorsConfiguration() {
return corsConfiguration;
}

public void setCorsConfiguration(
CorsConfiguration newCorsConfiguration) throws IOException {
proxy.setBucketCors(volumeName, name, newCorsConfiguration);
this.corsConfiguration = newCorsConfiguration;
}

public void deleteCorsConfiguration() throws IOException {
proxy.deleteBucketCors(volumeName, name);
this.corsConfiguration = null;
}

/**
* Builder for OmBucketInfo.
/**
Expand Down Expand Up @@ -1231,6 +1249,7 @@ public static class Builder extends WithMetadata.Builder {
private String owner;
private long pendingDeleteBytes;
private long pendingDeleteNamespace;
private CorsConfiguration corsConfiguration;

protected Builder() {
}
Expand Down Expand Up @@ -1327,6 +1346,12 @@ public Builder setOwner(String owner) {
return this;
}

public Builder setCorsConfiguration(
CorsConfiguration corsConfig) {
this.corsConfiguration = corsConfig;
return this;
}

public Builder setPendingDeleteBytes(long pendingDeleteBytes) {
this.pendingDeleteBytes = pendingDeleteBytes;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.apache.hadoop.ozone.client.io.OzoneOutputStream;
import org.apache.hadoop.ozone.om.OMConfigKeys;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.helpers.CorsConfiguration;
import org.apache.hadoop.ozone.om.helpers.DeleteTenantState;
import org.apache.hadoop.ozone.om.helpers.ErrorInfo;
import org.apache.hadoop.ozone.om.helpers.LeaseKeyInfo;
Expand Down Expand Up @@ -277,6 +278,26 @@ void setBucketStorageType(String volumeName, String bucketName,
StorageType storageType)
throws IOException;

/**
* Sets the CORS configuration of a Bucket.
* @param volumeName Name of the Volume
* @param bucketName Name of the Bucket
* @param corsConfiguration CORS configuration to set
* @throws IOException
*/
void setBucketCors(String volumeName, String bucketName,
CorsConfiguration corsConfiguration)
throws IOException;

/**
* Clears the CORS configuration of a Bucket.
* @param volumeName Name of the Volume
* @param bucketName Name of the Bucket
* @throws IOException
*/
void deleteBucketCors(String volumeName, String bucketName)
throws IOException;

/**
* Deletes a bucket if it is empty.
* @param volumeName Name of the Volume
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
import org.apache.hadoop.ozone.om.helpers.BasicOmKeyInfo;
import org.apache.hadoop.ozone.om.helpers.BucketEncryptionKeyInfo;
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
import org.apache.hadoop.ozone.om.helpers.CorsConfiguration;
import org.apache.hadoop.ozone.om.helpers.DeleteTenantState;
import org.apache.hadoop.ozone.om.helpers.ErrorInfo;
import org.apache.hadoop.ozone.om.helpers.KeyInfoWithVolumeContext;
Expand Down Expand Up @@ -622,6 +623,9 @@ public void createBucket(
+ " not support Erasure Coded replication.");
}
}
if (bucketArgs.getCorsConfiguration() != null) {
checkBucketCorsFeatureEnabled();
}

final String owner;
// If S3 auth exists, set owner name to the short user name derived from the
Expand Down Expand Up @@ -656,7 +660,8 @@ public void createBucket(
.setQuotaInBytes(bucketArgs.getQuotaInBytes())
.setQuotaInNamespace(bucketArgs.getQuotaInNamespace())
.setBucketLayout(bucketLayout)
.setOwner(owner);
.setOwner(owner)
.setCorsConfiguration(bucketArgs.getCorsConfiguration());

if (bucketArgs.getAcls() != null) {
builder.acls().addAll(bucketArgs.getAcls());
Expand Down Expand Up @@ -1232,6 +1237,40 @@ public void setBucketStorageType(
ozoneManagerClient.setBucketProperty(builder.build());
}

@Override
public void setBucketCors(String volumeName, String bucketName,
CorsConfiguration corsConfiguration) throws IOException {
verifyVolumeName(volumeName);
verifyBucketName(bucketName);
Objects.requireNonNull(corsConfiguration, "corsConfiguration == null");
checkBucketCorsFeatureEnabled();
OmBucketArgs.Builder builder = OmBucketArgs.newBuilder();
builder.setVolumeName(volumeName)
.setBucketName(bucketName)
.setCorsConfiguration(corsConfiguration);
ozoneManagerClient.setBucketProperty(builder.build());
}

@Override
public void deleteBucketCors(String volumeName, String bucketName)
throws IOException {
verifyVolumeName(volumeName);
verifyBucketName(bucketName);
checkBucketCorsFeatureEnabled();
OmBucketArgs.Builder builder = OmBucketArgs.newBuilder();
builder.setVolumeName(volumeName)
.setBucketName(bucketName)
.setClearCorsConfiguration(true);
ozoneManagerClient.setBucketProperty(builder.build());
}

private void checkBucketCorsFeatureEnabled() throws IOException {
if (omVersion.compareTo(OzoneManagerVersion.S3_BUCKET_CORS) < 0) {
throw new IOException("OzoneManager does not support bucket CORS "
+ "configuration.");
}
}

@Override
public void setBucketQuota(String volumeName, String bucketName,
long quotaInNamespace, long quotaInBytes) throws IOException {
Expand Down Expand Up @@ -1340,6 +1379,7 @@ public OzoneBucket getBucketDetails(
.setBucketLayout(bucketInfo.getBucketLayout())
.setOwner(bucketInfo.getOwner())
.setDefaultReplicationConfig(bucketInfo.getDefaultReplicationConfig())
.setCorsConfiguration(bucketInfo.getCorsConfiguration())
.build();
}

Expand Down Expand Up @@ -1374,6 +1414,7 @@ public List<OzoneBucket> listBuckets(String volumeName, String bucketPrefix,
.setOwner(bucket.getOwner())
.setDefaultReplicationConfig(
bucket.getDefaultReplicationConfig())
.setCorsConfiguration(bucket.getCorsConfiguration())
.build())
.collect(Collectors.toList());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.hadoop.ozone.om.helpers;

import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.CORSConfiguration;

/**
* S3 bucket CORS configuration.
*/
public final class CorsConfiguration {
private final ImmutableList<CorsRule> rules;

private CorsConfiguration(Builder builder) {
this.rules = ImmutableList.copyOf(builder.rules);
}

public static Builder newBuilder() {
return new Builder();
}

public List<CorsRule> getRules() {
return rules;
}

public CORSConfiguration getProtobuf() {
return CORSConfiguration.newBuilder()
.addAllCorsRule(rules.stream()
.map(CorsRule::getProtobuf)
.collect(Collectors.toList()))
.build();
}

public static CorsConfiguration getFromProtobuf(
CORSConfiguration proto) {
return newBuilder()
.setRules(proto.getCorsRuleList().stream()
.map(CorsRule::getFromProtobuf)
.collect(Collectors.toList()))
.build();
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof CorsConfiguration)) {
return false;
}
CorsConfiguration that = (CorsConfiguration) obj;
return Objects.equals(rules, that.rules);
}

@Override
public int hashCode() {
return Objects.hash(rules);
}

@Override
public String toString() {
return "CorsConfiguration{"
+ "rules=" + rules
+ '}';
}

/**
* Builder for {@link CorsConfiguration}.
*/
public static final class Builder {
private List<CorsRule> rules = ImmutableList.of();

private Builder() {
}

public Builder setRules(List<CorsRule> corsRules) {
this.rules = corsRules == null ? ImmutableList.of() : corsRules;
return this;
}

public Builder addRule(CorsRule rule) {
ImmutableList.Builder<CorsRule> builder = ImmutableList.builder();
builder.addAll(rules);
builder.add(rule);
this.rules = builder.build();
return this;
}

public CorsConfiguration build() {
return new CorsConfiguration(this);
}
}
}
Loading