Skip to content

Commit

Permalink
add findImage and ScalingActivitie Controller (#4365)
Browse files Browse the repository at this point in the history
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
956081712 and mergify[bot] authored Feb 28, 2020
1 parent 8074a65 commit 977051b
Show file tree
Hide file tree
Showing 5 changed files with 459 additions and 1 deletion.
5 changes: 4 additions & 1 deletion clouddriver-alicloud/clouddriver-alicloud.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies {
implementation "org.apache.httpcomponents:httpcore"
implementation "com.github.ben-manes.caffeine:guava"
implementation "com.netflix.spinnaker.moniker:moniker"
implementation "javax.servlet:javax.servlet-api"

implementation 'com.aestasit.infrastructure.sshoogr:sshoogr:0.9.25'
implementation 'com.jcraft:jsch.agentproxy.jsch:0.0.9'
Expand All @@ -24,7 +25,9 @@ dependencies {
implementation 'com.aliyun:aliyun-java-sdk-slb:3.2.9'
implementation 'com.aliyun:aliyun-java-sdk-vpc:3.0.6'
implementation 'com.aliyun:aliyun-java-sdk-ecs:4.16.10'
implementation 'com.aliyun:aliyun-java-sdk-ess:2.3.0'
implementation 'com.aliyun:aliyun-java-sdk-ess:2.3.2'
implementation "org.springframework.boot:spring-boot-actuator"
implementation "org.springframework.boot:spring-boot-starter-web"

testImplementation "cglib:cglib-nodep"
testImplementation "org.mockito:mockito-core"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
* Copyright 2019 Alibaba Group.
*
* 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.
*/

package com.netflix.spinnaker.clouddriver.alicloud.controllers;

import static com.netflix.spinnaker.clouddriver.core.provider.agent.Namespace.IMAGES;
import static com.netflix.spinnaker.clouddriver.core.provider.agent.Namespace.NAMED_IMAGES;

import com.netflix.spinnaker.cats.cache.Cache;
import com.netflix.spinnaker.cats.cache.CacheData;
import com.netflix.spinnaker.clouddriver.alicloud.cache.Keys;
import com.netflix.spinnaker.kork.web.exceptions.InvalidRequestException;
import groovy.util.logging.Slf4j;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequestMapping("/alicloud/images")
public class AliCloudImageController {

private final Cache cacheView;

@Autowired
public AliCloudImageController(Cache cacheView) {
this.cacheView = cacheView;
}

@RequestMapping(value = "/find", method = RequestMethod.GET)
List<Image> list(LookupOptions lookupOptions, HttpServletRequest request) {
String glob = lookupOptions.getQ();
if (StringUtils.isAllBlank(glob) && glob.length() < 3) {
throw new InvalidRequestException("Lost search condition or length less 3");
}
glob = "*" + glob + "*";
String imageSearchKey =
Keys.getImageKey(
glob,
StringUtils.isAllBlank(lookupOptions.account) ? "*" : lookupOptions.account,
StringUtils.isAllBlank(lookupOptions.region) ? "*" : lookupOptions.region);
Collection<String> imageIdentifiers = cacheView.filterIdentifiers(IMAGES.ns, imageSearchKey);
Collection<CacheData> images = cacheView.getAll(IMAGES.ns, imageIdentifiers, null);
String nameKey =
Keys.getNamedImageKey(
StringUtils.isAllBlank(lookupOptions.account) ? "*" : lookupOptions.account, glob);
Collection<String> nameImageIdentifiers = cacheView.filterIdentifiers(NAMED_IMAGES.ns, nameKey);
Collection<CacheData> nameImages =
cacheView.getAll(NAMED_IMAGES.ns, nameImageIdentifiers, null);
return filter(render(nameImages, images), extractTagFilters(request));
}

private static List<Image> filter(List<Image> namedImages, Map<String, String> tagFilters) {
if (tagFilters.isEmpty()) {
return namedImages;
}
List<Image> filter = new ArrayList<>();
for (Image namedImage : namedImages) {
if (checkInclude(namedImage, tagFilters)) {
filter.add(namedImage);
}
}
return filter;
}

/* private static boolean checkInclude(Image image, Map<String, String> tagFilters) {
boolean flag = false;
List<Map> tags = (List) image.getAttributes().get("tags");
if (tags != null) {
for (Map tag : tags) {
String tagKey = tag.get("tagKey").toString();
String tagValue = tag.get("tagValue").toString();
if (StringUtils.isNotEmpty(tagFilters.get(tagKey))
&& tagFilters.get(tagKey).equalsIgnoreCase(tagValue)) {
flag = true;
} else {
flag = false;
break;
}
}
}
return flag;
}*/

private static boolean checkInclude(Image image, Map<String, String> tagFilters) {
boolean flag = false;
List<Map> tags = (List) image.getAttributes().get("tags");
Map<String, String> imageMap = new HashMap<>(tags.size());
for (Map tag : tags) {
imageMap.put(tag.get("tagKey").toString(), tag.get("tagValue").toString());
}
for (Map.Entry<String, String> entry : tagFilters.entrySet()) {
String tagKey = entry.getKey();
String tagValue = entry.getValue();
if (StringUtils.isNotEmpty(imageMap.get(tagKey))
&& imageMap.get(tagKey).equalsIgnoreCase(tagValue)) {
flag = true;
} else {
flag = false;
break;
}
}
return flag;
}

private List<Image> render(Collection<CacheData> namedImages, Collection<CacheData> images) {
List<Image> list = new ArrayList<>();
for (CacheData image : images) {
Map<String, Object> attributes = image.getAttributes();
list.add(new Image(String.valueOf(attributes.get("imageName")), attributes));
}

for (CacheData nameImage : namedImages) {
Map<String, Object> attributes = nameImage.getAttributes();
list.add(new Image(String.valueOf(attributes.get("imageName")), attributes));
}
return list;
}

private static Map<String, String> extractTagFilters(HttpServletRequest request) {
Map<String, String> parameters = new HashMap<>(16);
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String parameterName = parameterNames.nextElement();
if (parameterName.toLowerCase().startsWith("tag:")) {
parameters.put(
parameterName.replaceAll("tag:", "").toLowerCase(),
request.getParameter(parameterName));
}
}
return parameters;
}

@Data
public static class Image {
public Image(String imageName, Map<String, Object> attributes) {
this.imageName = imageName;
this.attributes = attributes;
}

String imageName;
Map<String, Object> attributes;
}

@Data
static class LookupOptions {
String q;
String account;
String region;
}

@Data
static class Tag {
String tagKey;
String tagValue;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright 2019 Alibaba Group.
*
* 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.
*/

package com.netflix.spinnaker.clouddriver.alicloud.controllers;

import com.aliyuncs.IAcsClient;
import com.aliyuncs.ess.model.v20140828.DescribeScalingActivitiesRequest;
import com.aliyuncs.ess.model.v20140828.DescribeScalingActivitiesResponse;
import com.aliyuncs.ess.model.v20140828.DescribeScalingActivitiesResponse.ScalingActivity;
import com.aliyuncs.ess.model.v20140828.DescribeScalingGroupsRequest;
import com.aliyuncs.ess.model.v20140828.DescribeScalingGroupsResponse;
import com.aliyuncs.ess.model.v20140828.DescribeScalingGroupsResponse.ScalingGroup;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.netflix.spinnaker.clouddriver.alicloud.common.ClientFactory;
import com.netflix.spinnaker.clouddriver.alicloud.security.AliCloudCredentials;
import com.netflix.spinnaker.clouddriver.security.AccountCredentials;
import com.netflix.spinnaker.clouddriver.security.AccountCredentialsProvider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(
"/applications/{application}/clusters/{account}/{clusterName}/alicloud/serverGroups/{serverGroupName}")
public class AliCloudScalingActivitiesController {

private final AccountCredentialsProvider accountCredentialsProvider;

private final ClientFactory clientFactory;

@Autowired
public AliCloudScalingActivitiesController(
AccountCredentialsProvider accountCredentialsProvider, ClientFactory clientFactory) {
this.accountCredentialsProvider = accountCredentialsProvider;
this.clientFactory = clientFactory;
}

@RequestMapping(value = "/scalingActivities", method = RequestMethod.GET)
ResponseEntity getScalingActivities(
@PathVariable String account,
@PathVariable String serverGroupName,
@RequestParam(value = "region", required = true) String region) {
List<ScalingActivity> resultList = new ArrayList<>();
AccountCredentials credentials = accountCredentialsProvider.getCredentials(account);
if (!(credentials instanceof AliCloudCredentials)) {
Map<String, String> messageMap = new HashMap<>();
messageMap.put("message", "bad credentials");
return new ResponseEntity(messageMap, HttpStatus.BAD_REQUEST);
}
AliCloudCredentials aliCloudCredentials = (AliCloudCredentials) credentials;
IAcsClient client =
clientFactory.createClient(
region, aliCloudCredentials.getAccessKeyId(), aliCloudCredentials.getAccessSecretKey());
DescribeScalingGroupsRequest describeScalingGroupsRequest = new DescribeScalingGroupsRequest();
describeScalingGroupsRequest.setScalingGroupName(serverGroupName);
describeScalingGroupsRequest.setPageSize(50);
DescribeScalingGroupsResponse describeScalingGroupsResponse;
try {
describeScalingGroupsResponse = client.getAcsResponse(describeScalingGroupsRequest);
if (describeScalingGroupsResponse.getScalingGroups().size() > 0) {
ScalingGroup scalingGroup = describeScalingGroupsResponse.getScalingGroups().get(0);
DescribeScalingActivitiesRequest activitiesRequest = new DescribeScalingActivitiesRequest();
activitiesRequest.setScalingGroupId(scalingGroup.getScalingGroupId());
activitiesRequest.setPageSize(50);
DescribeScalingActivitiesResponse activitiesResponse =
client.getAcsResponse(activitiesRequest);
resultList.addAll(activitiesResponse.getScalingActivities());
}

} catch (ServerException e) {
e.printStackTrace();
throw new IllegalStateException(e.getMessage());
} catch (ClientException e) {
e.printStackTrace();
throw new IllegalStateException(e.getMessage());
}
return new ResponseEntity(resultList, HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2019 Alibaba Group.
*
* 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.
*/
package com.netflix.spinnaker.clouddriver.alicloud.controllers;

import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import com.netflix.spinnaker.cats.cache.Cache;
import com.netflix.spinnaker.cats.cache.CacheData;
import com.netflix.spinnaker.cats.cache.DefaultCacheData;
import com.netflix.spinnaker.clouddriver.alicloud.controllers.AliCloudImageController.Image;
import com.netflix.spinnaker.clouddriver.alicloud.controllers.AliCloudImageController.LookupOptions;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class AliCloudImageControllerTest {

private final String ACCOUNT = "test-account";
private final String REGION = "cn-test";

final Cache cacheView = mock(Cache.class);
final LookupOptions lookupOptions = mock(LookupOptions.class);
final HttpServletRequest request = mock(HttpServletRequest.class);

@Before
public void testBefore() {
when(cacheView.filterIdentifiers(anyString(), anyString())).thenAnswer(new FilterAnswer());
when(cacheView.getAll(anyString(), any(), any())).thenAnswer(new CacheDataAnswer());
when(lookupOptions.getQ()).thenReturn("test");
when(request.getParameterNames()).thenAnswer(new RequestAnswer());
}

@Test
public void testList() {
AliCloudImageController controller = new AliCloudImageController(cacheView);
List<Image> list = controller.list(lookupOptions, request);
assertTrue(list.size() == 2);
}

private class FilterAnswer implements Answer<List<String>> {
@Override
public List<String> answer(InvocationOnMock invocation) throws Throwable {
List<String> list = new ArrayList<>();
list.add("alicloud:images:ali-account:cn-hangzhou:win_xxx_xxx_xxx.vhd");
return list;
}
}

private class CacheDataAnswer implements Answer<List<CacheData>> {
@Override
public List<CacheData> answer(InvocationOnMock invocation) throws Throwable {
List<CacheData> cacheDatas = new ArrayList<>();
Map<String, Object> attributes = new HashMap<>();
attributes.put("account", ACCOUNT);
attributes.put("regionId", REGION);
attributes.put("imageName", "win_xxx_xxx_xxx.vhd");
CacheData cacheData1 =
new DefaultCacheData(
"alicloud:images:ali-account:cn-hangzhou:win_xxx_xxx_xxx.vhd", attributes, null);
cacheDatas.add(cacheData1);
return cacheDatas;
}
}

private class RequestAnswer implements Answer<Enumeration<String>> {
@Override
public Enumeration<String> answer(InvocationOnMock invocation) throws Throwable {
List<String> list = new ArrayList<>();
Enumeration<String> enumeration = Collections.enumeration(list);
return enumeration;
}
}
}
Loading

0 comments on commit 977051b

Please sign in to comment.