Skip to content

Commit

Permalink
add ec2ShareAmi step
Browse files Browse the repository at this point in the history
  • Loading branch information
tylersouthwick committed Feb 21, 2018
1 parent f6f21f1 commit 7d9a7b0
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 0 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ This plugins adds Jenkins pipeline steps to interact with the AWS API.
* [setAccountAlias](#setaccountalias)
* [ecrLogin](#ecrlogin)
* [invokeLambda](#invokelambda)
* [ec2ShareAmi](#ec2ShareAmi)

[**see the changelog for release information**](#changelog)

Expand Down Expand Up @@ -499,6 +500,17 @@ String result = invokeLambda(
)
```

## ec2ShareAmi

Share an AMI image to one or more accounts

```
ec2ShareAmi(
amiId: 'ami-23842',
accountIds: [ "0123456789", "1234567890" ]
)
```

# Changelog

## current master
Expand Down
135 changes: 135 additions & 0 deletions src/main/java/de/taimos/pipeline/aws/EC2ShareAmiStep.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* -
* #%L
* Pipeline: AWS Steps
* %%
* Copyright (C) 2017 Taimos GmbH
* %%
* Licensed 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.
* #L%
*/

package de.taimos.pipeline.aws;

import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.AmazonEC2ClientBuilder;
import com.amazonaws.services.ec2.model.LaunchPermission;
import com.amazonaws.services.ec2.model.LaunchPermissionModifications;
import com.amazonaws.services.ec2.model.ModifyImageAttributeRequest;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import de.taimos.pipeline.aws.utils.StepUtils;
import hudson.Extension;
import hudson.model.TaskListener;
import org.jenkinsci.plugins.workflow.steps.Step;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.jenkinsci.plugins.workflow.steps.SynchronousStepExecution;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

import javax.annotation.Nullable;
import java.util.List;
import java.util.Set;

public class EC2ShareAmiStep extends Step {

private List<String> accountIds;
private String amiId;

@DataBoundConstructor
public EC2ShareAmiStep() {
//
}

@Override
public StepExecution start(StepContext context) throws Exception {
return new EC2ShareAmiStep.Execution(this, context);
}

public List<String> getAccountIds() {
return accountIds;
}

@DataBoundSetter
public void setAccountIds(List<String> accountIds) {
this.accountIds = accountIds;
}

public String getAmiId() {
return amiId;
}

@DataBoundSetter
public void setAmiId(String amiId) {
this.amiId = amiId;
}

@Extension
public static class DescriptorImpl extends StepDescriptor {

@Override
public String getFunctionName() {
return "ec2ShareAmi";
}

@Override
public String getDisplayName() {
return "Create and return the ECR login string";
}

@Override
public Set<? extends Class<?>> getRequiredContext() {
return StepUtils.requiresDefault();
}
}

public static class Execution extends SynchronousStepExecution<String> {

private final transient EC2ShareAmiStep step;

public Execution(EC2ShareAmiStep step, StepContext context) {
super(context);
this.step = step;
}

@Override
protected String run() throws Exception {

TaskListener listener = this.getContext().get(TaskListener.class);

listener.getLogger().println("Sharing amiId=" + this.step.amiId + " to accounts: " + this.step.accountIds);

AmazonEC2 ec2 = AWSClientFactory.create(AmazonEC2ClientBuilder.standard(), this.getContext());
ec2.modifyImageAttribute(new ModifyImageAttributeRequest()
.withImageId(this.step.amiId)
.withLaunchPermission(new LaunchPermissionModifications()
.withAdd(Lists.transform(this.step.accountIds, new Function<String, LaunchPermission>() {
@Override
public LaunchPermission apply(@Nullable String accountId) {
return new LaunchPermission()
.withUserId(accountId);
}
}))
)
);
listener.getLogger().println("Shared amiId=" + this.step.amiId + " to accounts: " + this.step.accountIds);
return null;
}

private static final long serialVersionUID = 1L;

}

}
72 changes: 72 additions & 0 deletions src/test/java/de/taimos/pipeline/aws/EC2ShareAmiStepTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package de.taimos.pipeline.aws;

import com.amazonaws.client.builder.AwsSyncClientBuilder;
import com.amazonaws.services.cloudformation.AmazonCloudFormation;
import com.amazonaws.services.cloudformation.model.AmazonCloudFormationException;
import com.amazonaws.services.cloudformation.model.ValidateTemplateRequest;
import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.model.LaunchPermission;
import com.amazonaws.services.ec2.model.LaunchPermissionModifications;
import com.amazonaws.services.ec2.model.ModifyImageAttributeRequest;
import com.amazonaws.services.ec2.model.ModifyImageAttributeResult;
import hudson.model.Result;
import org.assertj.core.api.Assertions;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.jvnet.hudson.test.JenkinsRule;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(AWSClientFactory.class)
@PowerMockIgnore("javax.crypto.*")
public class EC2ShareAmiStepTests {

@Rule
private JenkinsRule jenkinsRule = new JenkinsRule();
private AmazonEC2 ec2;

@Before
public void setupSdk() throws Exception {
PowerMockito.mockStatic(AWSClientFactory.class);
this.ec2 = Mockito.mock(AmazonEC2.class);
PowerMockito.when(AWSClientFactory.create(Mockito.any(AwsSyncClientBuilder.class), Mockito.any(StepContext.class)))
.thenReturn(this.ec2);
}

@Test
public void validateModifyAttributeRequest() throws Exception {
WorkflowJob job = this.jenkinsRule.jenkins.createProject(WorkflowJob.class, "ec2Test");
job.setDefinition(new CpsFlowDefinition(""
+ "node {\n"
+ " ec2ShareAmi(amiId: 'foo', accountIds: ['a1', 'a2'])"
+ "}\n", true)
);

this.jenkinsRule.assertBuildStatusSuccess(job.scheduleBuild2(0));

ArgumentCaptor<ModifyImageAttributeRequest> captor = ArgumentCaptor.forClass(ModifyImageAttributeRequest.class);
Mockito.verify(this.ec2).modifyImageAttribute(captor.capture());
Assertions.assertThat(captor.getValue()).isEqualTo(new ModifyImageAttributeRequest()
.withImageId("foo")
.withLaunchPermission(new LaunchPermissionModifications()
.withAdd(new LaunchPermission()
.withUserId("a1")
)
.withAdd(new LaunchPermission()
.withUserId("a2")
)
)
);
}

}

0 comments on commit 7d9a7b0

Please sign in to comment.