Adds url kubernetes iso (#10862)

Co-authored-by: Vitor Hugo Homem Marzarotto <vitor.marzarotto@scclouds.com.br>
Co-authored-by: Henrique Sato <henriquesato2003@gmail.com>
This commit is contained in:
Vitor Hugo Homem Marzarotto 2026-01-20 04:10:42 -03:00 committed by GitHub
parent 42f1e19362
commit 2a6ce0c8a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 105 additions and 31 deletions

View File

@ -1097,6 +1097,7 @@ public class ApiConstants {
public static final String DOCKER_REGISTRY_EMAIL = "dockerregistryemail"; public static final String DOCKER_REGISTRY_EMAIL = "dockerregistryemail";
public static final String ISO_NAME = "isoname"; public static final String ISO_NAME = "isoname";
public static final String ISO_STATE = "isostate"; public static final String ISO_STATE = "isostate";
public static final String ISO_URL = "isourl";
public static final String SEMANTIC_VERSION = "semanticversion"; public static final String SEMANTIC_VERSION = "semanticversion";
public static final String KUBERNETES_VERSION_ID = "kubernetesversionid"; public static final String KUBERNETES_VERSION_ID = "kubernetesversionid";
public static final String KUBERNETES_VERSION_NAME = "kubernetesversionname"; public static final String KUBERNETES_VERSION_NAME = "kubernetesversionname";

View File

@ -53,6 +53,7 @@ import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.dao.VMTemplateZoneDao;
import com.cloud.template.TemplateApiService; import com.cloud.template.TemplateApiService;
import com.cloud.template.VirtualMachineTemplate; import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.user.AccountManager; import com.cloud.user.AccountManager;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.ComponentContext;
@ -85,7 +86,7 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
public static final String MINIMUN_AUTOSCALER_SUPPORTED_VERSION = "1.15.0"; public static final String MINIMUN_AUTOSCALER_SUPPORTED_VERSION = "1.15.0";
protected void updateTemplateDetailsInKubernetesSupportedVersionResponse( protected void updateTemplateDetailsInKubernetesSupportedVersionResponse(
final KubernetesSupportedVersion kubernetesSupportedVersion, KubernetesSupportedVersionResponse response) { final KubernetesSupportedVersion kubernetesSupportedVersion, KubernetesSupportedVersionResponse response, boolean isRootAdmin) {
TemplateJoinVO template = templateJoinDao.findById(kubernetesSupportedVersion.getIsoId()); TemplateJoinVO template = templateJoinDao.findById(kubernetesSupportedVersion.getIsoId());
if (template == null) { if (template == null) {
return; return;
@ -95,11 +96,14 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
if (template.getState() != null) { if (template.getState() != null) {
response.setIsoState(template.getState().toString()); response.setIsoState(template.getState().toString());
} }
if (isRootAdmin) {
response.setIsoUrl(template.getUrl());
}
response.setIsoArch(template.getArch().getType()); response.setIsoArch(template.getArch().getType());
response.setDirectDownload(template.isDirectDownload()); response.setDirectDownload(template.isDirectDownload());
} }
private KubernetesSupportedVersionResponse createKubernetesSupportedVersionResponse(final KubernetesSupportedVersion kubernetesSupportedVersion) { private KubernetesSupportedVersionResponse createKubernetesSupportedVersionResponse(final KubernetesSupportedVersion kubernetesSupportedVersion, boolean isRootAdmin) {
KubernetesSupportedVersionResponse response = new KubernetesSupportedVersionResponse(); KubernetesSupportedVersionResponse response = new KubernetesSupportedVersionResponse();
response.setObjectName("kubernetessupportedversion"); response.setObjectName("kubernetessupportedversion");
response.setId(kubernetesSupportedVersion.getUuid()); response.setId(kubernetesSupportedVersion.getUuid());
@ -118,7 +122,7 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
response.setSupportsHA(compareSemanticVersions(kubernetesSupportedVersion.getSemanticVersion(), response.setSupportsHA(compareSemanticVersions(kubernetesSupportedVersion.getSemanticVersion(),
KubernetesClusterService.MIN_KUBERNETES_VERSION_HA_SUPPORT)>=0); KubernetesClusterService.MIN_KUBERNETES_VERSION_HA_SUPPORT)>=0);
response.setSupportsAutoscaling(versionSupportsAutoscaling(kubernetesSupportedVersion)); response.setSupportsAutoscaling(versionSupportsAutoscaling(kubernetesSupportedVersion));
updateTemplateDetailsInKubernetesSupportedVersionResponse(kubernetesSupportedVersion, response); updateTemplateDetailsInKubernetesSupportedVersionResponse(kubernetesSupportedVersion, response, isRootAdmin);
response.setCreated(kubernetesSupportedVersion.getCreated()); response.setCreated(kubernetesSupportedVersion.getCreated());
return response; return response;
} }
@ -126,8 +130,11 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
private ListResponse<KubernetesSupportedVersionResponse> createKubernetesSupportedVersionListResponse( private ListResponse<KubernetesSupportedVersionResponse> createKubernetesSupportedVersionListResponse(
List<KubernetesSupportedVersionVO> versions, Integer count) { List<KubernetesSupportedVersionVO> versions, Integer count) {
List<KubernetesSupportedVersionResponse> responseList = new ArrayList<>(); List<KubernetesSupportedVersionResponse> responseList = new ArrayList<>();
Account caller = CallContext.current().getCallingAccount();
boolean isRootAdmin = accountManager.isRootAdmin(caller.getId());
for (KubernetesSupportedVersionVO version : versions) { for (KubernetesSupportedVersionVO version : versions) {
responseList.add(createKubernetesSupportedVersionResponse(version)); responseList.add(createKubernetesSupportedVersionResponse(version, isRootAdmin));
} }
ListResponse<KubernetesSupportedVersionResponse> response = new ListResponse<>(); ListResponse<KubernetesSupportedVersionResponse> response = new ListResponse<>();
response.setResponses(responseList, count); response.setResponses(responseList, count);
@ -374,7 +381,7 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
supportedVersionVO = kubernetesSupportedVersionDao.persist(supportedVersionVO); supportedVersionVO = kubernetesSupportedVersionDao.persist(supportedVersionVO);
CallContext.current().putContextParameter(KubernetesSupportedVersion.class, supportedVersionVO.getUuid()); CallContext.current().putContextParameter(KubernetesSupportedVersion.class, supportedVersionVO.getUuid());
return createKubernetesSupportedVersionResponse(supportedVersionVO); return createKubernetesSupportedVersionResponse(supportedVersionVO, true);
} }
@Override @Override
@ -435,7 +442,7 @@ public class KubernetesVersionManagerImpl extends ManagerBase implements Kuberne
} }
version = kubernetesSupportedVersionDao.findById(versionId); version = kubernetesSupportedVersionDao.findById(versionId);
} }
return createKubernetesSupportedVersionResponse(version); return createKubernetesSupportedVersionResponse(version, true);
} }
@Override @Override

View File

@ -50,6 +50,10 @@ public class KubernetesSupportedVersionResponse extends BaseResponse {
@Param(description = "The name of the binaries ISO for Kubernetes supported version") @Param(description = "The name of the binaries ISO for Kubernetes supported version")
private String isoName; private String isoName;
@SerializedName(ApiConstants.ISO_URL)
@Param(description = "the URL of the binaries ISO for Kubernetes supported version")
private String isoUrl;
@SerializedName(ApiConstants.ISO_STATE) @SerializedName(ApiConstants.ISO_STATE)
@Param(description = "The state of the binaries ISO for Kubernetes supported version") @Param(description = "The state of the binaries ISO for Kubernetes supported version")
private String isoState; private String isoState;
@ -134,6 +138,14 @@ public class KubernetesSupportedVersionResponse extends BaseResponse {
this.isoName = isoName; this.isoName = isoName;
} }
public String getIsoUrl() {
return isoUrl;
}
public void setIsoUrl(String isoUrl) {
this.isoUrl = isoUrl;
}
public String getIsoState() { public String getIsoState() {
return isoState; return isoState;
} }

View File

@ -48,7 +48,7 @@ public class KubernetesVersionManagerImplTest {
Mockito.when(kubernetesSupportedVersion.getIsoId()).thenReturn(1L); Mockito.when(kubernetesSupportedVersion.getIsoId()).thenReturn(1L);
KubernetesSupportedVersionResponse response = new KubernetesSupportedVersionResponse(); KubernetesSupportedVersionResponse response = new KubernetesSupportedVersionResponse();
kubernetesVersionManager.updateTemplateDetailsInKubernetesSupportedVersionResponse(kubernetesSupportedVersion, kubernetesVersionManager.updateTemplateDetailsInKubernetesSupportedVersionResponse(kubernetesSupportedVersion,
response); response, true);
Assert.assertNull(ReflectionTestUtils.getField(response, "isoId")); Assert.assertNull(ReflectionTestUtils.getField(response, "isoId"));
} }
@ -63,13 +63,13 @@ public class KubernetesVersionManagerImplTest {
Mockito.when(templateJoinVO.getUuid()).thenReturn(uuid); Mockito.when(templateJoinVO.getUuid()).thenReturn(uuid);
Mockito.when(templateJoinDao.findById(1L)).thenReturn(templateJoinVO); Mockito.when(templateJoinDao.findById(1L)).thenReturn(templateJoinVO);
kubernetesVersionManager.updateTemplateDetailsInKubernetesSupportedVersionResponse(kubernetesSupportedVersion, kubernetesVersionManager.updateTemplateDetailsInKubernetesSupportedVersionResponse(kubernetesSupportedVersion,
response); response, true);
Assert.assertEquals(uuid, ReflectionTestUtils.getField(response, "isoId")); Assert.assertEquals(uuid, ReflectionTestUtils.getField(response, "isoId"));
Assert.assertNull(ReflectionTestUtils.getField(response, "isoState")); Assert.assertNull(ReflectionTestUtils.getField(response, "isoState"));
ObjectInDataStoreStateMachine.State state = ObjectInDataStoreStateMachine.State.Ready; ObjectInDataStoreStateMachine.State state = ObjectInDataStoreStateMachine.State.Ready;
Mockito.when(templateJoinVO.getState()).thenReturn(state); Mockito.when(templateJoinVO.getState()).thenReturn(state);
kubernetesVersionManager.updateTemplateDetailsInKubernetesSupportedVersionResponse(kubernetesSupportedVersion, kubernetesVersionManager.updateTemplateDetailsInKubernetesSupportedVersionResponse(kubernetesSupportedVersion,
response); response, true);
Assert.assertEquals(state.toString(), ReflectionTestUtils.getField(response, "isoState")); Assert.assertEquals(state.toString(), ReflectionTestUtils.getField(response, "isoState"));
} }
} }

View File

@ -17,6 +17,9 @@
package com.cloud.kubernetes.version; package com.cloud.kubernetes.version;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@ -25,6 +28,11 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
import com.cloud.cpu.CPU; import com.cloud.cpu.CPU;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.User;
import com.cloud.user.UserVO;
import org.apache.cloudstack.api.command.admin.kubernetes.version.AddKubernetesSupportedVersionCmd; import org.apache.cloudstack.api.command.admin.kubernetes.version.AddKubernetesSupportedVersionCmd;
import org.apache.cloudstack.api.command.admin.kubernetes.version.DeleteKubernetesSupportedVersionCmd; import org.apache.cloudstack.api.command.admin.kubernetes.version.DeleteKubernetesSupportedVersionCmd;
import org.apache.cloudstack.api.command.admin.kubernetes.version.UpdateKubernetesSupportedVersionCmd; import org.apache.cloudstack.api.command.admin.kubernetes.version.UpdateKubernetesSupportedVersionCmd;
@ -63,11 +71,6 @@ import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.template.TemplateApiService; import com.cloud.template.TemplateApiService;
import com.cloud.template.VirtualMachineTemplate; import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.User;
import com.cloud.user.UserVO;
import com.cloud.utils.Pair; import com.cloud.utils.Pair;
import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.ComponentContext;
import com.cloud.utils.db.Filter; import com.cloud.utils.db.Filter;
@ -95,6 +98,8 @@ public class KubernetesVersionServiceTest {
private DataCenterDao dataCenterDao; private DataCenterDao dataCenterDao;
@Mock @Mock
private TemplateApiService templateService; private TemplateApiService templateService;
@Mock
private Account accountMock;
AutoCloseable closeable; AutoCloseable closeable;
@ -124,6 +129,7 @@ public class KubernetesVersionServiceTest {
when(dataCenterDao.findById(Mockito.anyLong())).thenReturn(zone); when(dataCenterDao.findById(Mockito.anyLong())).thenReturn(zone);
TemplateJoinVO templateJoinVO = Mockito.mock(TemplateJoinVO.class); TemplateJoinVO templateJoinVO = Mockito.mock(TemplateJoinVO.class);
when(templateJoinVO.getUrl()).thenReturn("https://download.cloudstack.com");
when(templateJoinVO.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Ready); when(templateJoinVO.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Ready);
when(templateJoinVO.getArch()).thenReturn(CPU.CPUArch.getDefault()); when(templateJoinVO.getArch()).thenReturn(CPU.CPUArch.getDefault());
when(templateJoinDao.findById(Mockito.anyLong())).thenReturn(templateJoinVO); when(templateJoinDao.findById(Mockito.anyLong())).thenReturn(templateJoinVO);
@ -140,20 +146,67 @@ public class KubernetesVersionServiceTest {
@Test @Test
public void listKubernetesSupportedVersionsTest() { public void listKubernetesSupportedVersionsTest() {
CallContext callContextMock = Mockito.mock(CallContext.class);
try (MockedStatic<CallContext> callContextMockedStatic = Mockito.mockStatic(CallContext.class)) {
callContextMockedStatic.when(CallContext::current).thenReturn(callContextMock);
final SearchCriteria<KubernetesSupportedVersionVO> versionSearchCriteria = Mockito.mock(SearchCriteria.class);
when(callContextMock.getCallingAccount()).thenReturn(accountMock);
ListKubernetesSupportedVersionsCmd cmd = Mockito.mock(ListKubernetesSupportedVersionsCmd.class); ListKubernetesSupportedVersionsCmd cmd = Mockito.mock(ListKubernetesSupportedVersionsCmd.class);
List<KubernetesSupportedVersionVO> versionVOs = new ArrayList<>(); List<KubernetesSupportedVersionVO> versionVOs = new ArrayList<>();
KubernetesSupportedVersionVO versionVO = Mockito.mock(KubernetesSupportedVersionVO.class); KubernetesSupportedVersionVO versionVO = Mockito.mock(KubernetesSupportedVersionVO.class);
when(versionVO.getSemanticVersion()).thenReturn(KubernetesVersionService.MIN_KUBERNETES_VERSION); when(versionVO.getSemanticVersion()).thenReturn(KubernetesVersionService.MIN_KUBERNETES_VERSION);
versionVOs.add(versionVO); versionVOs.add(versionVO);
when(kubernetesSupportedVersionDao.findById(Mockito.anyLong())).thenReturn(versionVO); when(kubernetesSupportedVersionDao.findById(Mockito.anyLong())).thenReturn(versionVO);
when(kubernetesSupportedVersionDao.searchAndCount(Mockito.any(SearchCriteria.class), when(kubernetesSupportedVersionDao.searchAndCount(Mockito.any(), Mockito.any(Filter.class)))
Mockito.any(Filter.class))).thenReturn(new Pair<>(versionVOs, versionVOs.size())); .thenReturn(new Pair<>(versionVOs, versionVOs.size()));
ListResponse<KubernetesSupportedVersionResponse> versionsResponse = ListResponse<KubernetesSupportedVersionResponse> versionsResponse =
kubernetesVersionService.listKubernetesSupportedVersions(cmd); kubernetesVersionService.listKubernetesSupportedVersions(cmd);
Assert.assertEquals(versionVOs.size(), versionsResponse.getCount().intValue()); Assert.assertEquals(versionVOs.size(), versionsResponse.getCount().intValue());
Assert.assertTrue(CollectionUtils.isNotEmpty(versionsResponse.getResponses())); Assert.assertTrue(CollectionUtils.isNotEmpty(versionsResponse.getResponses()));
Assert.assertEquals(versionVOs.size(), versionsResponse.getResponses().size()); Assert.assertEquals(versionVOs.size(), versionsResponse.getResponses().size());
} }
}
@Test
public void listKubernetesSupportedVersionsTestWhenAdmin() {
CallContext callContextMock = Mockito.mock(CallContext.class);
try (MockedStatic<CallContext> callContextMockedStatic = Mockito.mockStatic(CallContext.class)) {
callContextMockedStatic.when(CallContext::current).thenReturn(callContextMock);
ListKubernetesSupportedVersionsCmd cmd = Mockito.mock(ListKubernetesSupportedVersionsCmd.class);
List<KubernetesSupportedVersionVO> versionVOs = new ArrayList<>();
KubernetesSupportedVersionVO versionVO = Mockito.mock(KubernetesSupportedVersionVO.class);
when(versionVO.getSemanticVersion()).thenReturn(KubernetesVersionService.MIN_KUBERNETES_VERSION);
versionVOs.add(versionVO);
when(callContextMock.getCallingAccount()).thenReturn(accountMock);
when(kubernetesSupportedVersionDao.findById(Mockito.anyLong())).thenReturn(versionVO);
when(kubernetesSupportedVersionDao.searchAndCount(Mockito.any(), Mockito.any(Filter.class)))
.thenReturn(new Pair<>(versionVOs, versionVOs.size()));
when(accountManager.isRootAdmin(anyLong())).thenReturn(true);
ListResponse<KubernetesSupportedVersionResponse> response = kubernetesVersionService.listKubernetesSupportedVersions(cmd);
assertNotNull(response.getResponses().get(0).getIsoUrl());
}
}
@Test
public void listKubernetesSupportedVersionsTestWhenOtherUser() {
CallContext callContextMock = Mockito.mock(CallContext.class);
try (MockedStatic<CallContext> callContextMockedStatic = Mockito.mockStatic(CallContext.class)) {
callContextMockedStatic.when(CallContext::current).thenReturn(callContextMock);
ListKubernetesSupportedVersionsCmd cmd = Mockito.mock(ListKubernetesSupportedVersionsCmd.class);
List<KubernetesSupportedVersionVO> versionVOs = new ArrayList<>();
KubernetesSupportedVersionVO versionVO = Mockito.mock(KubernetesSupportedVersionVO.class);
when(versionVO.getSemanticVersion()).thenReturn(KubernetesVersionService.MIN_KUBERNETES_VERSION);
versionVOs.add(versionVO);
when(callContextMock.getCallingAccount()).thenReturn(accountMock);
when(kubernetesSupportedVersionDao.findById(Mockito.anyLong())).thenReturn(versionVO);
when(kubernetesSupportedVersionDao.searchAndCount(Mockito.any(), Mockito.any(Filter.class)))
.thenReturn(new Pair<>(versionVOs, versionVOs.size()));
when(accountManager.isRootAdmin(anyLong())).thenReturn(false);
when(accountMock.getId()).thenReturn(2L);
ListResponse<KubernetesSupportedVersionResponse> response = kubernetesVersionService.listKubernetesSupportedVersions(cmd);
assertNull(response.getResponses().get(0).getIsoUrl());
}
}
@Test(expected = InvalidParameterValueException.class) @Test(expected = InvalidParameterValueException.class)
public void addKubernetesSupportedVersionLowerUnsupportedTest() { public void addKubernetesSupportedVersionLowerUnsupportedTest() {
@ -224,7 +277,6 @@ public class KubernetesVersionServiceTest {
mockedComponentContext.when(() -> ComponentContext.inject(Mockito.any(RegisterIsoCmd.class))).thenReturn( mockedComponentContext.when(() -> ComponentContext.inject(Mockito.any(RegisterIsoCmd.class))).thenReturn(
new RegisterIsoCmd()); new RegisterIsoCmd());
mockedCallContext.when(CallContext::current).thenReturn(callContext); mockedCallContext.when(CallContext::current).thenReturn(callContext);
when(templateService.registerIso(Mockito.any(RegisterIsoCmd.class))).thenReturn( when(templateService.registerIso(Mockito.any(RegisterIsoCmd.class))).thenReturn(
Mockito.mock(VirtualMachineTemplate.class)); Mockito.mock(VirtualMachineTemplate.class));
VMTemplateVO templateVO = Mockito.mock(VMTemplateVO.class); VMTemplateVO templateVO = Mockito.mock(VMTemplateVO.class);

View File

@ -1254,6 +1254,7 @@
"label.isoname": "Attached ISO", "label.isoname": "Attached ISO",
"label.isos": "ISOs", "label.isos": "ISOs",
"label.isostate": "ISO state", "label.isostate": "ISO state",
"label.isourl": "ISO URL",
"label.ispersistent": "Persistent ", "label.ispersistent": "Persistent ",
"label.ispublic": "Public", "label.ispublic": "Public",
"label.isready": "Ready", "label.isready": "Ready",

View File

@ -874,6 +874,7 @@
"label.isoname": "Imagem ISO plugada", "label.isoname": "Imagem ISO plugada",
"label.isos": "ISOs", "label.isos": "ISOs",
"label.isostate": "Estado da ISO", "label.isostate": "Estado da ISO",
"label.isourl": "URL da ISO",
"label.ispersistent": "Persistente", "label.ispersistent": "Persistente",
"label.ispublic": "P\u00fablico", "label.ispublic": "P\u00fablico",
"label.isready": "Pronto", "label.isready": "Pronto",

View File

@ -60,9 +60,9 @@ export default {
details: () => { details: () => {
var fields = ['name', 'id', 'displaytext', 'checksum', 'hypervisor', 'arch', 'format', 'ostypename', 'size', 'physicalsize', 'isready', 'passwordenabled', var fields = ['name', 'id', 'displaytext', 'checksum', 'hypervisor', 'arch', 'format', 'ostypename', 'size', 'physicalsize', 'isready', 'passwordenabled',
'crossZones', 'templatetype', 'directdownload', 'deployasis', 'ispublic', 'isfeatured', 'isextractable', 'isdynamicallyscalable', 'crosszones', 'type', 'crossZones', 'templatetype', 'directdownload', 'deployasis', 'ispublic', 'isfeatured', 'isextractable', 'isdynamicallyscalable', 'crosszones', 'type',
'account', 'domain', 'created', 'userdatadetails', 'userdatapolicy'] 'account', 'domain', 'created', 'userdatadetails', 'userdatapolicy', 'url']
if (['Admin'].includes(store.getters.userInfo.roletype)) { if (['Admin'].includes(store.getters.userInfo.roletype)) {
fields.push('templatetag', 'templatetype', 'url') fields.push('templatetag', 'templatetype')
} }
return fields return fields
}, },
@ -372,7 +372,7 @@ export default {
permission: ['listKubernetesSupportedVersions'], permission: ['listKubernetesSupportedVersions'],
searchFilters: ['zoneid', 'minimumsemanticversion', 'arch'], searchFilters: ['zoneid', 'minimumsemanticversion', 'arch'],
columns: ['name', 'state', 'semanticversion', 'isostate', 'mincpunumber', 'minmemory', 'arch', 'zonename'], columns: ['name', 'state', 'semanticversion', 'isostate', 'mincpunumber', 'minmemory', 'arch', 'zonename'],
details: ['name', 'semanticversion', 'supportsautoscaling', 'zoneid', 'zonename', 'isoid', 'isoname', 'isostate', 'arch', 'mincpunumber', 'minmemory', 'supportsha', 'state', 'created'], details: ['name', 'semanticversion', 'supportsautoscaling', 'zoneid', 'zonename', 'isoid', 'isoname', 'isostate', 'arch', 'mincpunumber', 'minmemory', 'supportsha', 'state', 'created', 'isourl'],
tabs: [ tabs: [
{ {
name: 'details', name: 'details',