[AlibabaCloud] Change alicloud to alibabacloud (#470)

This commit is contained in:
muya-zj
2018-12-26 22:42:56 +08:00
committed by Brian Goff
commit e9e5c47037
21 changed files with 1891 additions and 0 deletions

57
README.md Normal file
View File

@@ -0,0 +1,57 @@
# Alibaba Cloud ECI
<img src="eci.svg" width="200" height="200" />
Alibaba Cloud ECI(Elastic Container Instance) is a service that allow you run containers without having to manage servers or clusters.
You can find more infomation via [alibaba cloud ECI web portal](https://www.aliyun.com/product/eci)
## Alibaba Cloud ECI Virtual-Kubelet Provider
Alibaba ECI provider is an adapter to connect between k8s and ECI service to implement pod from k8s cluster on alibaba cloud platform
## Prerequisites
To using ECI service on alibaba cloud, you may need open ECI service on [web portal](https://www.aliyun.com/product/eci), and then the ECI service will be available
## Deployment of the ECI provider in your cluster
configure and launch virtual kubelet
```
export ECI_REGION=cn-hangzhou
export ECI_SECURITY_GROUP=sg-123
export ECI_VSWITCH=vsw-123
export ECI_ACCESS_KEY=123
export ECI_SECRET_KEY=123
VKUBELET_TAINT_KEY=alibabacloud.com/eci virtual-kubelet --provider alibabacloud
```
confirm the virtual kubelet is connected to k8s cluster
```
$kubectl get node
NAME STATUS ROLES AGE VERSION
cn-shanghai.i-uf69qodr5ntaxleqdhhk Ready <none> 1d v1.9.3
virtual-kubelet Ready agent 10s v1.8.3
```
## Schedule K8s Pod to ECI via virtual kubelet
You can assign pod to virtual kubelet via node-selector and toleration.
```
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
nodeName: virtual-kubelet
containers:
- name: nginx
image: nginx
tolerations:
- key: alibabacloud.com/eci
operator: "Exists"
effect: NoSchedule
```
# Alibaba Cloud Serverless Kubernetes
Alibaba Cloud serverless kubernetes allows you to quickly create kubernetes container applications without
having to manage and maintain clusters and servers. It is based on ECI and fully compatible with the Kuberentes API.
You can find more infomation via [alibaba cloud serverless kubernetes product doc](https://www.alibabacloud.com/help/doc-detail/94078.htm)

56
config.go Normal file
View File

@@ -0,0 +1,56 @@
package alibabacloud
import (
"io"
"github.com/BurntSushi/toml"
"github.com/virtual-kubelet/virtual-kubelet/providers"
)
type providerConfig struct {
Region string
OperatingSystem string
CPU string
Memory string
Pods string
VSwitch string
SecureGroup string
ClusterName string
}
func (p *ECIProvider) loadConfig(r io.Reader) error {
var config providerConfig
if _, err := toml.DecodeReader(r, &config); err != nil {
return err
}
p.region = config.Region
if p.region == "" {
p.region = "cn-hangzhou"
}
p.vSwitch = config.VSwitch
p.secureGroup = config.SecureGroup
p.cpu = config.CPU
if p.cpu == "" {
p.cpu = "20"
}
p.memory = config.Memory
if p.memory == "" {
p.memory = "100Gi"
}
p.pods = config.Pods
if p.pods == "" {
p.pods = "20"
}
p.operatingSystem = config.OperatingSystem
if p.operatingSystem == "" {
p.operatingSystem = providers.OperatingSystemLinux
}
p.clusterName = config.ClusterName
if p.clusterName == "" {
p.clusterName = "default"
}
return nil
}

895
eci.go Normal file
View File

@@ -0,0 +1,895 @@
package alibabacloud
import (
"bytes"
"context"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strconv"
"strings"
"time"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/cpuguy83/strongerrors"
"github.com/virtual-kubelet/virtual-kubelet/log"
"github.com/virtual-kubelet/virtual-kubelet/manager"
"github.com/virtual-kubelet/virtual-kubelet/providers/alibabacloud/eci"
"k8s.io/api/core/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/remotecommand"
)
// The service account secret mount path.
const serviceAccountSecretMountPath = "/var/run/secrets/kubernetes.io/serviceaccount"
const podTagTimeFormat = "2006-01-02T15-04-05Z"
const timeFormat = "2006-01-02T15:04:05Z"
// ECIProvider implements the virtual-kubelet provider interface and communicates with Alibaba Cloud's ECI APIs.
type ECIProvider struct {
eciClient *eci.Client
resourceManager *manager.ResourceManager
resourceGroup string
region string
nodeName string
operatingSystem string
clusterName string
cpu string
memory string
pods string
internalIP string
daemonEndpointPort int32
secureGroup string
vSwitch string
}
// AuthConfig is the secret returned from an ImageRegistryCredential
type AuthConfig struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
Auth string `json:"auth,omitempty"`
Email string `json:"email,omitempty"`
ServerAddress string `json:"serveraddress,omitempty"`
IdentityToken string `json:"identitytoken,omitempty"`
RegistryToken string `json:"registrytoken,omitempty"`
}
var validEciRegions = []string{
"cn-hangzhou",
"cn-shanghai",
"cn-beijing",
"us-west-1",
}
// isValidECIRegion checks to make sure we're using a valid ECI region
func isValidECIRegion(region string) bool {
regionLower := strings.ToLower(region)
regionTrimmed := strings.Replace(regionLower, " ", "", -1)
for _, validRegion := range validEciRegions {
if regionTrimmed == validRegion {
return true
}
}
return false
}
// NewECIProvider creates a new ECIProvider.
func NewECIProvider(config string, rm *manager.ResourceManager, nodeName, operatingSystem string, internalIP string, daemonEndpointPort int32) (*ECIProvider, error) {
var p ECIProvider
var err error
p.resourceManager = rm
if config != "" {
f, err := os.Open(config)
if err != nil {
return nil, err
}
defer f.Close()
if err := p.loadConfig(f); err != nil {
return nil, err
}
}
if r := os.Getenv("ECI_CLUSTER_NAME"); r != "" {
p.clusterName = r
}
if p.clusterName == "" {
p.clusterName = "default"
}
if r := os.Getenv("ECI_REGION"); r != "" {
p.region = r
}
if p.region == "" {
return nil, errors.New("Region can't be empty please set ECI_REGION\n")
}
if r := p.region; !isValidECIRegion(r) {
unsupportedRegionMessage := fmt.Sprintf("Region %s is invalid. Current supported regions are: %s",
r, strings.Join(validEciRegions, ", "))
return nil, errors.New(unsupportedRegionMessage)
}
var accessKey, secretKey string
if ak := os.Getenv("ECI_ACCESS_KEY"); ak != "" {
accessKey = ak
}
if sk := os.Getenv("ECI_SECRET_KEY"); sk != "" {
secretKey = sk
}
if sg := os.Getenv("ECI_SECURITY_GROUP"); sg != "" {
p.secureGroup = sg
}
if vsw := os.Getenv("ECI_VSWITCH"); vsw != "" {
p.vSwitch = vsw
}
if p.secureGroup == "" {
return nil, errors.New("secureGroup can't be empty\n")
}
if p.vSwitch == "" {
return nil, errors.New("vSwitch can't be empty\n")
}
p.eciClient, err = eci.NewClientWithAccessKey(p.region, accessKey, secretKey)
if err != nil {
return nil, err
}
p.cpu = "1000"
p.memory = "4Ti"
p.pods = "1000"
if cpuQuota := os.Getenv("ECI_QUOTA_CPU"); cpuQuota != "" {
p.cpu = cpuQuota
}
if memoryQuota := os.Getenv("ECI_QUOTA_MEMORY"); memoryQuota != "" {
p.memory = memoryQuota
}
if podsQuota := os.Getenv("ECI_QUOTA_POD"); podsQuota != "" {
p.pods = podsQuota
}
p.operatingSystem = operatingSystem
p.nodeName = nodeName
p.internalIP = internalIP
p.daemonEndpointPort = daemonEndpointPort
return &p, err
}
// CreatePod accepts a Pod definition and creates
// an ECI deployment
func (p *ECIProvider) CreatePod(ctx context.Context, pod *v1.Pod) error {
//Ignore daemonSet Pod
if pod != nil && pod.OwnerReferences != nil && len(pod.OwnerReferences) != 0 && pod.OwnerReferences[0].Kind == "DaemonSet" {
msg := fmt.Sprintf("Skip to create DaemonSet pod %q", pod.Name)
log.G(ctx).WithField("Method", "CreatePod").Info(msg)
return nil
}
request := eci.CreateCreateContainerGroupRequest()
request.RestartPolicy = string(pod.Spec.RestartPolicy)
// get containers
containers, err := p.getContainers(pod, false)
initContainers, err := p.getContainers(pod, true)
if err != nil {
return err
}
// get registry creds
creds, err := p.getImagePullSecrets(pod)
if err != nil {
return err
}
// get volumes
volumes, err := p.getVolumes(pod)
if err != nil {
return err
}
// assign all the things
request.Containers = containers
request.InitContainers = initContainers
request.Volumes = volumes
request.ImageRegistryCredentials = creds
CreationTimestamp := pod.CreationTimestamp.UTC().Format(podTagTimeFormat)
tags := []eci.Tag{
eci.Tag{Key: "ClusterName", Value: p.clusterName},
eci.Tag{Key: "NodeName", Value: p.nodeName},
eci.Tag{Key: "NameSpace", Value: pod.Namespace},
eci.Tag{Key: "PodName", Value: pod.Name},
eci.Tag{Key: "UID", Value: string(pod.UID)},
eci.Tag{Key: "CreationTimestamp", Value: CreationTimestamp},
}
ContainerGroupName := containerGroupName(pod)
request.Tags = tags
request.SecurityGroupId = p.secureGroup
request.VSwitchId = p.vSwitch
request.ContainerGroupName = ContainerGroupName
msg := fmt.Sprintf("CreateContainerGroup request %+v", request)
log.G(ctx).WithField("Method", "CreatePod").Info(msg)
response, err := p.eciClient.CreateContainerGroup(request)
if err != nil {
return err
}
msg = fmt.Sprintf("CreateContainerGroup successed. %s, %s, %s", response.RequestId, response.ContainerGroupId, ContainerGroupName)
log.G(ctx).WithField("Method", "CreatePod").Info(msg)
return nil
}
func containerGroupName(pod *v1.Pod) string {
return fmt.Sprintf("%s-%s", pod.Namespace, pod.Name)
}
// UpdatePod is a noop, ECI currently does not support live updates of a pod.
func (p *ECIProvider) UpdatePod(ctx context.Context, pod *v1.Pod) error {
return nil
}
// DeletePod deletes the specified pod out of ECI.
func (p *ECIProvider) DeletePod(ctx context.Context, pod *v1.Pod) error {
eciId := ""
for _, cg := range p.GetCgs() {
if getECITagValue(&cg, "PodName") == pod.Name && getECITagValue(&cg, "NameSpace") == pod.Namespace {
eciId = cg.ContainerGroupId
break
}
}
if eciId == "" {
return strongerrors.NotFound(fmt.Errorf("DeletePod can't find Pod %s-%s", pod.Namespace, pod.Name))
}
request := eci.CreateDeleteContainerGroupRequest()
request.ContainerGroupId = eciId
_, err := p.eciClient.DeleteContainerGroup(request)
return wrapError(err)
}
// GetPod returns a pod by name that is running inside ECI
// returns nil if a pod by that name is not found.
func (p *ECIProvider) GetPod(ctx context.Context, namespace, name string) (*v1.Pod, error) {
pods, err := p.GetPods(ctx)
if err != nil {
return nil, err
}
for _, pod := range pods {
if pod.Name == name && pod.Namespace == namespace {
return pod, nil
}
}
return nil, nil
}
// GetContainerLogs returns the logs of a pod by name that is running inside ECI.
func (p *ECIProvider) GetContainerLogs(ctx context.Context, namespace, podName, containerName string, tail int) (string, error) {
eciId := ""
for _, cg := range p.GetCgs() {
if getECITagValue(&cg, "PodName") == podName && getECITagValue(&cg, "NameSpace") == namespace {
eciId = cg.ContainerGroupId
break
}
}
if eciId == "" {
return "", errors.New(fmt.Sprintf("GetContainerLogs can't find Pod %s-%s", namespace, podName))
}
request := eci.CreateDescribeContainerLogRequest()
request.ContainerGroupId = eciId
request.ContainerName = containerName
request.Tail = requests.Integer(tail)
// get logs from cg
logContent := ""
retry := 10
for i := 0; i < retry; i++ {
response, err := p.eciClient.DescribeContainerLog(request)
if err != nil {
msg := fmt.Sprint("Error getting container logs, retrying")
log.G(ctx).WithField("Method", "GetContainerLogs").Info(msg)
time.Sleep(5000 * time.Millisecond)
} else {
logContent = response.Content
break
}
}
return logContent, nil
}
// Get full pod name as defined in the provider context
func (p *ECIProvider) GetPodFullName(namespace string, pod string) string {
return fmt.Sprintf("%s-%s", namespace, pod)
}
// ExecInContainer executes a command in a container in the pod, copying data
// between in/out/err and the container's stdin/stdout/stderr.
func (p *ECIProvider) ExecInContainer(name string, uid types.UID, container string, cmd []string, in io.Reader, out, errstream io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error {
return nil
}
// GetPodStatus returns the status of a pod by name that is running inside ECI
// returns nil if a pod by that name is not found.
func (p *ECIProvider) GetPodStatus(ctx context.Context, namespace, name string) (*v1.PodStatus, error) {
pod, err := p.GetPod(ctx, namespace, name)
if err != nil {
return nil, err
}
if pod == nil {
return nil, nil
}
return &pod.Status, nil
}
func (p *ECIProvider) GetCgs() []eci.ContainerGroup {
cgs := make([]eci.ContainerGroup, 0)
request := eci.CreateDescribeContainerGroupsRequest()
for {
cgsResponse, err := p.eciClient.DescribeContainerGroups(request)
if err != nil || len(cgsResponse.ContainerGroups) == 0 {
break
}
request.NextToken = cgsResponse.NextToken
for _, cg := range cgsResponse.ContainerGroups {
if getECITagValue(&cg, "NodeName") != p.nodeName {
continue
}
cn := getECITagValue(&cg, "ClusterName")
if cn == "" {
cn = "default"
}
if cn != p.clusterName {
continue
}
cgs = append(cgs, cg)
}
if request.NextToken == "" {
break
}
}
return cgs
}
// GetPods returns a list of all pods known to be running within ECI.
func (p *ECIProvider) GetPods(ctx context.Context) ([]*v1.Pod, error) {
pods := make([]*v1.Pod, 0)
for _, cg := range p.GetCgs() {
c := cg
pod, err := containerGroupToPod(&c)
if err != nil {
msg := fmt.Sprint("error converting container group to pod", cg.ContainerGroupId, err)
log.G(context.TODO()).WithField("Method", "GetPods").Info(msg)
continue
}
pods = append(pods, pod)
}
return pods, nil
}
// Capacity returns a resource list containing the capacity limits set for ECI.
func (p *ECIProvider) Capacity(ctx context.Context) v1.ResourceList {
return v1.ResourceList{
"cpu": resource.MustParse(p.cpu),
"memory": resource.MustParse(p.memory),
"pods": resource.MustParse(p.pods),
}
}
// NodeConditions returns a list of conditions (Ready, OutOfDisk, etc), for updates to the node status
// within Kubernetes.
func (p *ECIProvider) NodeConditions(ctx context.Context) []v1.NodeCondition {
// TODO: Make these dynamic and augment with custom ECI specific conditions of interest
return []v1.NodeCondition{
{
Type: "Ready",
Status: v1.ConditionTrue,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "KubeletReady",
Message: "kubelet is ready.",
},
{
Type: "OutOfDisk",
Status: v1.ConditionFalse,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "KubeletHasSufficientDisk",
Message: "kubelet has sufficient disk space available",
},
{
Type: "MemoryPressure",
Status: v1.ConditionFalse,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "KubeletHasSufficientMemory",
Message: "kubelet has sufficient memory available",
},
{
Type: "DiskPressure",
Status: v1.ConditionFalse,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "KubeletHasNoDiskPressure",
Message: "kubelet has no disk pressure",
},
{
Type: "NetworkUnavailable",
Status: v1.ConditionFalse,
LastHeartbeatTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: "RouteCreated",
Message: "RouteController created a route",
},
}
}
// NodeAddresses returns a list of addresses for the node status
// within Kubernetes.
func (p *ECIProvider) NodeAddresses(ctx context.Context) []v1.NodeAddress {
// TODO: Make these dynamic and augment with custom ECI specific conditions of interest
return []v1.NodeAddress{
{
Type: "InternalIP",
Address: p.internalIP,
},
}
}
// NodeDaemonEndpoints returns NodeDaemonEndpoints for the node status
// within Kubernetes.
func (p *ECIProvider) NodeDaemonEndpoints(ctx context.Context) *v1.NodeDaemonEndpoints {
return &v1.NodeDaemonEndpoints{
KubeletEndpoint: v1.DaemonEndpoint{
Port: p.daemonEndpointPort,
},
}
}
// OperatingSystem returns the operating system that was provided by the config.
func (p *ECIProvider) OperatingSystem() string {
return p.operatingSystem
}
func (p *ECIProvider) getImagePullSecrets(pod *v1.Pod) ([]eci.ImageRegistryCredential, error) {
ips := make([]eci.ImageRegistryCredential, 0, len(pod.Spec.ImagePullSecrets))
for _, ref := range pod.Spec.ImagePullSecrets {
secret, err := p.resourceManager.GetSecret(ref.Name, pod.Namespace)
if err != nil {
return ips, err
}
if secret == nil {
return nil, fmt.Errorf("error getting image pull secret")
}
// TODO: Check if secret type is v1.SecretTypeDockercfg and use DockerConfigKey instead of hardcoded value
// TODO: Check if secret type is v1.SecretTypeDockerConfigJson and use DockerConfigJsonKey to determine if it's in json format
// TODO: Return error if it's not one of these two types
switch secret.Type {
case v1.SecretTypeDockercfg:
ips, err = readDockerCfgSecret(secret, ips)
case v1.SecretTypeDockerConfigJson:
ips, err = readDockerConfigJSONSecret(secret, ips)
default:
return nil, fmt.Errorf("image pull secret type is not one of kubernetes.io/dockercfg or kubernetes.io/dockerconfigjson")
}
if err != nil {
return ips, err
}
}
return ips, nil
}
func readDockerCfgSecret(secret *v1.Secret, ips []eci.ImageRegistryCredential) ([]eci.ImageRegistryCredential, error) {
var err error
var authConfigs map[string]AuthConfig
repoData, ok := secret.Data[string(v1.DockerConfigKey)]
if !ok {
return ips, fmt.Errorf("no dockercfg present in secret")
}
err = json.Unmarshal(repoData, &authConfigs)
if err != nil {
return ips, fmt.Errorf("failed to unmarshal auth config %+v", err)
}
for server, authConfig := range authConfigs {
ips = append(ips, eci.ImageRegistryCredential{
Password: authConfig.Password,
Server: server,
UserName: authConfig.Username,
})
}
return ips, err
}
func readDockerConfigJSONSecret(secret *v1.Secret, ips []eci.ImageRegistryCredential) ([]eci.ImageRegistryCredential, error) {
var err error
repoData, ok := secret.Data[string(v1.DockerConfigJsonKey)]
if !ok {
return ips, fmt.Errorf("no dockerconfigjson present in secret")
}
var authConfigs map[string]map[string]AuthConfig
err = json.Unmarshal(repoData, &authConfigs)
if err != nil {
return ips, err
}
auths, ok := authConfigs["auths"]
if !ok {
return ips, fmt.Errorf("malformed dockerconfigjson in secret")
}
for server, authConfig := range auths {
ips = append(ips, eci.ImageRegistryCredential{
Password: authConfig.Password,
Server: server,
UserName: authConfig.Username,
})
}
return ips, err
}
func (p *ECIProvider) getContainers(pod *v1.Pod, init bool) ([]eci.CreateContainer, error) {
podContainers := pod.Spec.Containers
if init {
podContainers = pod.Spec.InitContainers
}
containers := make([]eci.CreateContainer, 0, len(podContainers))
for _, container := range podContainers {
c := eci.CreateContainer{
Name: container.Name,
Image: container.Image,
Commands: append(container.Command, container.Args...),
Ports: make([]eci.ContainerPort, 0, len(container.Ports)),
}
for _, p := range container.Ports {
c.Ports = append(c.Ports, eci.ContainerPort{
Port: requests.Integer(strconv.FormatInt(int64(p.ContainerPort), 10)),
Protocol: string(p.Protocol),
})
}
c.VolumeMounts = make([]eci.VolumeMount, 0, len(container.VolumeMounts))
for _, v := range container.VolumeMounts {
c.VolumeMounts = append(c.VolumeMounts, eci.VolumeMount{
Name: v.Name,
MountPath: v.MountPath,
ReadOnly: requests.Boolean(strconv.FormatBool(v.ReadOnly)),
})
}
c.EnvironmentVars = make([]eci.EnvironmentVar, 0, len(container.Env))
for _, e := range container.Env {
c.EnvironmentVars = append(c.EnvironmentVars, eci.EnvironmentVar{Key: e.Name, Value: e.Value})
}
cpuRequest := 1.00
if _, ok := container.Resources.Requests[v1.ResourceCPU]; ok {
cpuRequest = float64(container.Resources.Requests.Cpu().MilliValue()) / 1000.00
}
c.Cpu = requests.Float(fmt.Sprintf("%.3f", cpuRequest))
memoryRequest := 2.0
if _, ok := container.Resources.Requests[v1.ResourceMemory]; ok {
memoryRequest = float64(container.Resources.Requests.Memory().Value()) / 1024.0 / 1024.0 / 1024.0
}
c.Memory = requests.Float(fmt.Sprintf("%.3f", memoryRequest))
c.ImagePullPolicy = string(container.ImagePullPolicy)
c.WorkingDir = container.WorkingDir
containers = append(containers, c)
}
return containers, nil
}
func (p *ECIProvider) getVolumes(pod *v1.Pod) ([]eci.Volume, error) {
volumes := make([]eci.Volume, 0, len(pod.Spec.Volumes))
for _, v := range pod.Spec.Volumes {
// Handle the case for the EmptyDir.
if v.EmptyDir != nil {
volumes = append(volumes, eci.Volume{
Type: eci.VOL_TYPE_EMPTYDIR,
Name: v.Name,
EmptyDirVolumeEnable: requests.Boolean(strconv.FormatBool(true)),
})
continue
}
// Handle the case for the NFS.
if v.NFS != nil {
volumes = append(volumes, eci.Volume{
Type: eci.VOL_TYPE_NFS,
Name: v.Name,
NfsVolumeServer: v.NFS.Server,
NfsVolumePath: v.NFS.Path,
NfsVolumeReadOnly: requests.Boolean(strconv.FormatBool(v.NFS.ReadOnly)),
})
continue
}
// Handle the case for ConfigMap volume.
if v.ConfigMap != nil {
ConfigFileToPaths := make([]eci.ConfigFileToPath, 0)
configMap, err := p.resourceManager.GetConfigMap(v.ConfigMap.Name, pod.Namespace)
if v.ConfigMap.Optional != nil && !*v.ConfigMap.Optional && k8serr.IsNotFound(err) {
return nil, fmt.Errorf("ConfigMap %s is required by Pod %s and does not exist", v.ConfigMap.Name, pod.Name)
}
if configMap == nil {
continue
}
for k, v := range configMap.Data {
var b bytes.Buffer
enc := base64.NewEncoder(base64.StdEncoding, &b)
enc.Write([]byte(v))
ConfigFileToPaths = append(ConfigFileToPaths, eci.ConfigFileToPath{Path: k, Content: b.String()})
}
if len(ConfigFileToPaths) != 0 {
volumes = append(volumes, eci.Volume{
Type: eci.VOL_TYPE_CONFIGFILEVOLUME,
Name: v.Name,
ConfigFileToPaths: ConfigFileToPaths,
})
}
continue
}
if v.Secret != nil {
ConfigFileToPaths := make([]eci.ConfigFileToPath, 0)
secret, err := p.resourceManager.GetSecret(v.Secret.SecretName, pod.Namespace)
if v.Secret.Optional != nil && !*v.Secret.Optional && k8serr.IsNotFound(err) {
return nil, fmt.Errorf("Secret %s is required by Pod %s and does not exist", v.Secret.SecretName, pod.Name)
}
if secret == nil {
continue
}
for k, v := range secret.Data {
var b bytes.Buffer
enc := base64.NewEncoder(base64.StdEncoding, &b)
enc.Write(v)
ConfigFileToPaths = append(ConfigFileToPaths, eci.ConfigFileToPath{Path: k, Content: b.String()})
}
if len(ConfigFileToPaths) != 0 {
volumes = append(volumes, eci.Volume{
Type: eci.VOL_TYPE_CONFIGFILEVOLUME,
Name: v.Name,
ConfigFileToPaths: ConfigFileToPaths,
})
}
continue
}
// If we've made it this far we have found a volume type that isn't supported
return nil, fmt.Errorf("Pod %s requires volume %s which is of an unsupported type\n", pod.Name, v.Name)
}
return volumes, nil
}
func containerGroupToPod(cg *eci.ContainerGroup) (*v1.Pod, error) {
var podCreationTimestamp, containerStartTime metav1.Time
CreationTimestamp := getECITagValue(cg, "CreationTimestamp")
if CreationTimestamp != "" {
if t, err := time.Parse(podTagTimeFormat, CreationTimestamp); err == nil {
podCreationTimestamp = metav1.NewTime(t)
}
}
if t, err := time.Parse(timeFormat, cg.Containers[0].CurrentState.StartTime); err == nil {
containerStartTime = metav1.NewTime(t)
}
// Use the Provisioning State if it's not Succeeded,
// otherwise use the state of the instance.
eciState := cg.Status
containers := make([]v1.Container, 0, len(cg.Containers))
containerStatuses := make([]v1.ContainerStatus, 0, len(cg.Containers))
for _, c := range cg.Containers {
container := v1.Container{
Name: c.Name,
Image: c.Image,
Command: c.Commands,
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse(fmt.Sprintf("%.2f", c.Cpu)),
v1.ResourceMemory: resource.MustParse(fmt.Sprintf("%.1fG", c.Memory)),
},
},
}
container.Resources.Limits = v1.ResourceList{
v1.ResourceCPU: resource.MustParse(fmt.Sprintf("%.2f", c.Cpu)),
v1.ResourceMemory: resource.MustParse(fmt.Sprintf("%.1fG", c.Memory)),
}
containers = append(containers, container)
containerStatus := v1.ContainerStatus{
Name: c.Name,
State: eciContainerStateToContainerState(c.CurrentState),
LastTerminationState: eciContainerStateToContainerState(c.PreviousState),
Ready: eciStateToPodPhase(c.CurrentState.State) == v1.PodRunning,
RestartCount: int32(c.RestartCount),
Image: c.Image,
ImageID: "",
ContainerID: getContainerID(cg.ContainerGroupId, c.Name),
}
// Add to containerStatuses
containerStatuses = append(containerStatuses, containerStatus)
}
pod := v1.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: getECITagValue(cg, "PodName"),
Namespace: getECITagValue(cg, "NameSpace"),
ClusterName: getECITagValue(cg, "ClusterName"),
UID: types.UID(getECITagValue(cg, "UID")),
CreationTimestamp: podCreationTimestamp,
},
Spec: v1.PodSpec{
NodeName: getECITagValue(cg, "NodeName"),
Volumes: []v1.Volume{},
Containers: containers,
},
Status: v1.PodStatus{
Phase: eciStateToPodPhase(eciState),
Conditions: eciStateToPodConditions(eciState, podCreationTimestamp),
Message: "",
Reason: "",
HostIP: "",
PodIP: cg.IntranetIp,
StartTime: &containerStartTime,
ContainerStatuses: containerStatuses,
},
}
return &pod, nil
}
func getContainerID(cgID, containerName string) string {
if cgID == "" {
return ""
}
containerResourceID := fmt.Sprintf("%s/containers/%s", cgID, containerName)
h := sha256.New()
h.Write([]byte(strings.ToUpper(containerResourceID)))
hashBytes := h.Sum(nil)
return fmt.Sprintf("eci://%s", hex.EncodeToString(hashBytes))
}
func eciStateToPodPhase(state string) v1.PodPhase {
switch state {
case "Scheduling":
return v1.PodPending
case "ScheduleFailed":
return v1.PodFailed
case "Pending":
return v1.PodPending
case "Running":
return v1.PodRunning
case "Failed":
return v1.PodFailed
case "Succeeded":
return v1.PodSucceeded
}
return v1.PodUnknown
}
func eciStateToPodConditions(state string, transitionTime metav1.Time) []v1.PodCondition {
switch state {
case "Running", "Succeeded":
return []v1.PodCondition{
v1.PodCondition{
Type: v1.PodReady,
Status: v1.ConditionTrue,
LastTransitionTime: transitionTime,
}, v1.PodCondition{
Type: v1.PodInitialized,
Status: v1.ConditionTrue,
LastTransitionTime: transitionTime,
}, v1.PodCondition{
Type: v1.PodScheduled,
Status: v1.ConditionTrue,
LastTransitionTime: transitionTime,
},
}
}
return []v1.PodCondition{}
}
func eciContainerStateToContainerState(cs eci.ContainerState) v1.ContainerState {
t1, err := time.Parse(timeFormat, cs.StartTime)
if err != nil {
return v1.ContainerState{}
}
startTime := metav1.NewTime(t1)
// Handle the case where the container is running.
if cs.State == "Running" || cs.State == "Succeeded" {
return v1.ContainerState{
Running: &v1.ContainerStateRunning{
StartedAt: startTime,
},
}
}
t2, err := time.Parse(timeFormat, cs.FinishTime)
if err != nil {
return v1.ContainerState{}
}
finishTime := metav1.NewTime(t2)
// Handle the case where the container failed.
if cs.State == "Failed" || cs.State == "Canceled" {
return v1.ContainerState{
Terminated: &v1.ContainerStateTerminated{
ExitCode: int32(cs.ExitCode),
Reason: cs.State,
Message: cs.DetailStatus,
StartedAt: startTime,
FinishedAt: finishTime,
},
}
}
// Handle the case where the container is pending.
// Which should be all other eci states.
return v1.ContainerState{
Waiting: &v1.ContainerStateWaiting{
Reason: cs.State,
Message: cs.DetailStatus,
},
}
}
func getECITagValue(cg *eci.ContainerGroup, key string) string {
for _, tag := range cg.Tags {
if tag.Key == key {
return tag.Value
}
}
return ""
}

16
eci.svg Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
<style type="text/css">
.st0{fill:#00C1DE;}
</style>
<g>
<polygon class="st0" points="41.4,22.7 41.4,25 43.7,25.6 43.7,31.8 24,36.3 4.3,31.8 4.3,25.6 6.5,25 6.5,22.7 2,23.7 2,33.7
24,38.7 46,33.7 46,23.7 "/>
<polygon class="st0" points="38.1,10.6 24,8 9.9,10.6 24,12.9 "/>
<polygon class="st0" points="22.8,15.1 8.8,12.6 8.8,30.2 22.8,33.4 "/>
<path class="st0" d="M25.2,33.4l14-3.2V12.6l-14,2.5V33.4z M35.2,15.3l2-0.4v13.7l-2,0.5V15.3z M31.3,15.9l2-0.4v13.9l-2,0.5V15.9z
M27.5,16.6l2-0.4v14l-2,0.5V16.6z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 878 B

6
eci.toml Normal file
View File

@@ -0,0 +1,6 @@
Region = "cn-hangzhou"
OperatingSystem = "Linux"
CPU = "20"
Memory = "100Gi"
Pods = "20"
ClusterName = "default"

81
eci/client.go Normal file
View File

@@ -0,0 +1,81 @@
package eci
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
import (
"github.com/aliyun/alibaba-cloud-sdk-go/sdk"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth"
)
// Client is the sdk client struct, each func corresponds to an OpenAPI
type Client struct {
sdk.Client
}
// NewClient creates a sdk client with environment variables
func NewClient() (client *Client, err error) {
client = &Client{}
err = client.Init()
return
}
// NewClientWithOptions creates a sdk client with regionId/sdkConfig/credential
// this is the common api to create a sdk client
func NewClientWithOptions(regionId string, config *sdk.Config, credential auth.Credential) (client *Client, err error) {
client = &Client{}
err = client.InitWithOptions(regionId, config, credential)
return
}
// NewClientWithAccessKey is a shortcut to create sdk client with accesskey
// usage: https://help.aliyun.com/document_detail/66217.html
func NewClientWithAccessKey(regionId, accessKeyId, accessKeySecret string) (client *Client, err error) {
client = &Client{}
err = client.InitWithAccessKey(regionId, accessKeyId, accessKeySecret)
return
}
// NewClientWithStsToken is a shortcut to create sdk client with sts token
// usage: https://help.aliyun.com/document_detail/66222.html
func NewClientWithStsToken(regionId, stsAccessKeyId, stsAccessKeySecret, stsToken string) (client *Client, err error) {
client = &Client{}
err = client.InitWithStsToken(regionId, stsAccessKeyId, stsAccessKeySecret, stsToken)
return
}
// NewClientWithRamRoleArn is a shortcut to create sdk client with ram roleArn
// usage: https://help.aliyun.com/document_detail/66222.html
func NewClientWithRamRoleArn(regionId string, accessKeyId, accessKeySecret, roleArn, roleSessionName string) (client *Client, err error) {
client = &Client{}
err = client.InitWithRamRoleArn(regionId, accessKeyId, accessKeySecret, roleArn, roleSessionName)
return
}
// NewClientWithEcsRamRole is a shortcut to create sdk client with ecs ram role
// usage: https://help.aliyun.com/document_detail/66223.html
func NewClientWithEcsRamRole(regionId string, roleName string) (client *Client, err error) {
client = &Client{}
err = client.InitWithEcsRamRole(regionId, roleName)
return
}
// NewClientWithRsaKeyPair is a shortcut to create sdk client with rsa key pair
// attention: rsa key pair auth is only Japan regions available
func NewClientWithRsaKeyPair(regionId string, publicKeyId, privateKey string, sessionExpiration int) (client *Client, err error) {
client = &Client{}
err = client.InitWithRsaKeyPair(regionId, publicKeyId, privateKey, sessionExpiration)
return
}

View File

@@ -0,0 +1,138 @@
package eci
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
import (
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses"
)
// CreateContainerGroup invokes the eci.CreateContainerGroup API synchronously
// api document: https://help.aliyun.com/api/eci/createcontainergroup.html
func (client *Client) CreateContainerGroup(request *CreateContainerGroupRequest) (response *CreateContainerGroupResponse, err error) {
response = CreateCreateContainerGroupResponse()
err = client.DoAction(request, response)
return
}
// CreateContainerGroupWithChan invokes the eci.CreateContainerGroup API asynchronously
// api document: https://help.aliyun.com/api/eci/createcontainergroup.html
// asynchronous document: https://help.aliyun.com/document_detail/66220.html
func (client *Client) CreateContainerGroupWithChan(request *CreateContainerGroupRequest) (<-chan *CreateContainerGroupResponse, <-chan error) {
responseChan := make(chan *CreateContainerGroupResponse, 1)
errChan := make(chan error, 1)
err := client.AddAsyncTask(func() {
defer close(responseChan)
defer close(errChan)
response, err := client.CreateContainerGroup(request)
if err != nil {
errChan <- err
} else {
responseChan <- response
}
})
if err != nil {
errChan <- err
close(responseChan)
close(errChan)
}
return responseChan, errChan
}
// CreateContainerGroupWithCallback invokes the eci.CreateContainerGroup API asynchronously
// api document: https://help.aliyun.com/api/eci/createcontainergroup.html
// asynchronous document: https://help.aliyun.com/document_detail/66220.html
func (client *Client) CreateContainerGroupWithCallback(request *CreateContainerGroupRequest, callback func(response *CreateContainerGroupResponse, err error)) <-chan int {
result := make(chan int, 1)
err := client.AddAsyncTask(func() {
var response *CreateContainerGroupResponse
var err error
defer close(result)
response, err = client.CreateContainerGroup(request)
callback(response, err)
result <- 1
})
if err != nil {
defer close(result)
callback(nil, err)
result <- 0
}
return result
}
// CreateContainerGroupRequest is the request struct for api CreateContainerGroup
type CreateContainerGroupRequest struct {
*requests.RpcRequest
Containers []CreateContainer `position:"Query" name:"Container" type:"Repeated"`
InitContainers []CreateContainer `position:"Query" name:"InitContainer" type:"Repeated"`
ResourceOwnerId requests.Integer `position:"Query" name:"ResourceOwnerId"`
SecurityGroupId string `position:"Query" name:"SecurityGroupId"`
ImageRegistryCredentials []ImageRegistryCredential `position:"Query" name:"ImageRegistryCredential" type:"Repeated"`
Tags []Tag `position:"Query" name:"Tag" type:"Repeated"`
ResourceOwnerAccount string `position:"Query" name:"ResourceOwnerAccount"`
RestartPolicy string `position:"Query" name:"RestartPolicy"`
OwnerAccount string `position:"Query" name:"OwnerAccount"`
OwnerId requests.Integer `position:"Query" name:"OwnerId"`
VSwitchId string `position:"Query" name:"VSwitchId"`
Volumes []Volume `position:"Query" name:"Volume" type:"Repeated"`
ContainerGroupName string `position:"Query" name:"ContainerGroupName"`
ZoneId string `position:"Query" name:"ZoneId"`
}
type CreateContainer struct {
Name string `name:"Name"`
Image string `name:"Image"`
Memory requests.Float `name:"Memory"`
Cpu requests.Float `name:"Cpu"`
WorkingDir string `name:"WorkingDir"`
ImagePullPolicy string `name:"ImagePullPolicy"`
Commands []string `name:"Command" type:"Repeated"`
Args []string `name:"Arg" type:"Repeated"`
VolumeMounts []VolumeMount `name:"VolumeMount" type:"Repeated"`
Ports []ContainerPort `name:"Port" type:"Repeated"`
EnvironmentVars []EnvironmentVar `name:"EnvironmentVar" type:"Repeated"`
}
// CreateContainerGroupImageRegistryCredential is a repeated param struct in CreateContainerGroupRequest
type ImageRegistryCredential struct {
Server string `name:"Server"`
UserName string `name:"UserName"`
Password string `name:"Password"`
}
// CreateContainerGroupResponse is the response struct for api CreateContainerGroup
type CreateContainerGroupResponse struct {
*responses.BaseResponse
RequestId string
ContainerGroupId string
}
// CreateCreateContainerGroupRequest creates a request to invoke CreateContainerGroup API
func CreateCreateContainerGroupRequest() (request *CreateContainerGroupRequest) {
request = &CreateContainerGroupRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Eci", "2018-08-08", "CreateContainerGroup", "eci", "openAPI")
return
}
// CreateCreateContainerGroupResponse creates a response to parse from CreateContainerGroup response
func CreateCreateContainerGroupResponse() (response *CreateContainerGroupResponse) {
response = &CreateContainerGroupResponse{
BaseResponse: &responses.BaseResponse{},
}
return
}

View File

@@ -0,0 +1,107 @@
package eci
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
import (
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses"
)
// DeleteContainerGroup invokes the eci.DeleteContainerGroup API synchronously
// api document: https://help.aliyun.com/api/eci/deletecontainergroup.html
func (client *Client) DeleteContainerGroup(request *DeleteContainerGroupRequest) (response *DeleteContainerGroupResponse, err error) {
response = CreateDeleteContainerGroupResponse()
err = client.DoAction(request, response)
return
}
// DeleteContainerGroupWithChan invokes the eci.DeleteContainerGroup API asynchronously
// api document: https://help.aliyun.com/api/eci/deletecontainergroup.html
// asynchronous document: https://help.aliyun.com/document_detail/66220.html
func (client *Client) DeleteContainerGroupWithChan(request *DeleteContainerGroupRequest) (<-chan *DeleteContainerGroupResponse, <-chan error) {
responseChan := make(chan *DeleteContainerGroupResponse, 1)
errChan := make(chan error, 1)
err := client.AddAsyncTask(func() {
defer close(responseChan)
defer close(errChan)
response, err := client.DeleteContainerGroup(request)
if err != nil {
errChan <- err
} else {
responseChan <- response
}
})
if err != nil {
errChan <- err
close(responseChan)
close(errChan)
}
return responseChan, errChan
}
// DeleteContainerGroupWithCallback invokes the eci.DeleteContainerGroup API asynchronously
// api document: https://help.aliyun.com/api/eci/deletecontainergroup.html
// asynchronous document: https://help.aliyun.com/document_detail/66220.html
func (client *Client) DeleteContainerGroupWithCallback(request *DeleteContainerGroupRequest, callback func(response *DeleteContainerGroupResponse, err error)) <-chan int {
result := make(chan int, 1)
err := client.AddAsyncTask(func() {
var response *DeleteContainerGroupResponse
var err error
defer close(result)
response, err = client.DeleteContainerGroup(request)
callback(response, err)
result <- 1
})
if err != nil {
defer close(result)
callback(nil, err)
result <- 0
}
return result
}
// DeleteContainerGroupRequest is the request struct for api DeleteContainerGroup
type DeleteContainerGroupRequest struct {
*requests.RpcRequest
ResourceOwnerId requests.Integer `position:"Query" name:"ResourceOwnerId"`
ContainerGroupId string `position:"Query" name:"ContainerGroupId"`
ResourceOwnerAccount string `position:"Query" name:"ResourceOwnerAccount"`
OwnerAccount string `position:"Query" name:"OwnerAccount"`
OwnerId requests.Integer `position:"Query" name:"OwnerId"`
}
// DeleteContainerGroupResponse is the response struct for api DeleteContainerGroup
type DeleteContainerGroupResponse struct {
*responses.BaseResponse
RequestId string `json:"RequestId" xml:"RequestId"`
}
// CreateDeleteContainerGroupRequest creates a request to invoke DeleteContainerGroup API
func CreateDeleteContainerGroupRequest() (request *DeleteContainerGroupRequest) {
request = &DeleteContainerGroupRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Eci", "2018-08-08", "DeleteContainerGroup", "eci", "openAPI")
return
}
// CreateDeleteContainerGroupResponse creates a response to parse from DeleteContainerGroup response
func CreateDeleteContainerGroupResponse() (response *DeleteContainerGroupResponse) {
response = &DeleteContainerGroupResponse{
BaseResponse: &responses.BaseResponse{},
}
return
}

View File

@@ -0,0 +1,122 @@
package eci
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
import (
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses"
)
// DescribeContainerGroups invokes the eci.DescribeContainerGroups API synchronously
// api document: https://help.aliyun.com/api/eci/describecontainergroups.html
func (client *Client) DescribeContainerGroups(request *DescribeContainerGroupsRequest) (response *DescribeContainerGroupsResponse, err error) {
response = CreateDescribeContainerGroupsResponse()
err = client.DoAction(request, response)
return
}
// DescribeContainerGroupsWithChan invokes the eci.DescribeContainerGroups API asynchronously
// api document: https://help.aliyun.com/api/eci/describecontainergroups.html
// asynchronous document: https://help.aliyun.com/document_detail/66220.html
func (client *Client) DescribeContainerGroupsWithChan(request *DescribeContainerGroupsRequest) (<-chan *DescribeContainerGroupsResponse, <-chan error) {
responseChan := make(chan *DescribeContainerGroupsResponse, 1)
errChan := make(chan error, 1)
err := client.AddAsyncTask(func() {
defer close(responseChan)
defer close(errChan)
response, err := client.DescribeContainerGroups(request)
if err != nil {
errChan <- err
} else {
responseChan <- response
}
})
if err != nil {
errChan <- err
close(responseChan)
close(errChan)
}
return responseChan, errChan
}
// DescribeContainerGroupsWithCallback invokes the eci.DescribeContainerGroups API asynchronously
// api document: https://help.aliyun.com/api/eci/describecontainergroups.html
// asynchronous document: https://help.aliyun.com/document_detail/66220.html
func (client *Client) DescribeContainerGroupsWithCallback(request *DescribeContainerGroupsRequest, callback func(response *DescribeContainerGroupsResponse, err error)) <-chan int {
result := make(chan int, 1)
err := client.AddAsyncTask(func() {
var response *DescribeContainerGroupsResponse
var err error
defer close(result)
response, err = client.DescribeContainerGroups(request)
callback(response, err)
result <- 1
})
if err != nil {
defer close(result)
callback(nil, err)
result <- 0
}
return result
}
// DescribeContainerGroupsRequest is the request struct for api DescribeContainerGroups
type DescribeContainerGroupsRequest struct {
*requests.RpcRequest
ResourceOwnerId requests.Integer `position:"Query" name:"ResourceOwnerId"`
NextToken string `position:"Query" name:"NextToken"`
Limit requests.Integer `position:"Query" name:"Limit"`
Tags *[]DescribeContainerGroupsTag `position:"Query" name:"Tag" type:"Repeated"`
ContainerGroupId string `position:"Query" name:"ContainerGroupId"`
ResourceOwnerAccount string `position:"Query" name:"ResourceOwnerAccount"`
OwnerAccount string `position:"Query" name:"OwnerAccount"`
OwnerId requests.Integer `position:"Query" name:"OwnerId"`
VSwitchId string `position:"Query" name:"VSwitchId"`
ContainerGroupName string `position:"Query" name:"ContainerGroupName"`
ZoneId string `position:"Query" name:"ZoneId"`
}
// DescribeContainerGroupsTag is a repeated param struct in DescribeContainerGroupsRequest
type DescribeContainerGroupsTag struct {
Key string `name:"Key"`
Value string `name:"Value"`
}
// DescribeContainerGroupsResponse is the response struct for api DescribeContainerGroups
type DescribeContainerGroupsResponse struct {
*responses.BaseResponse
RequestId string `json:"RequestId" xml:"RequestId"`
NextToken string `json:"NextToken" xml:"NextToken"`
TotalCount int `json:"TotalCount" xml:"TotalCount"`
ContainerGroups []ContainerGroup `json:"ContainerGroups" xml:"ContainerGroups"`
}
// CreateDescribeContainerGroupsRequest creates a request to invoke DescribeContainerGroups API
func CreateDescribeContainerGroupsRequest() (request *DescribeContainerGroupsRequest) {
request = &DescribeContainerGroupsRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Eci", "2018-08-08", "DescribeContainerGroups", "eci", "openAPI")
return
}
// CreateDescribeContainerGroupsResponse creates a response to parse from DescribeContainerGroups response
func CreateDescribeContainerGroupsResponse() (response *DescribeContainerGroupsResponse) {
response = &DescribeContainerGroupsResponse{
BaseResponse: &responses.BaseResponse{},
}
return
}

View File

@@ -0,0 +1,112 @@
package eci
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
import (
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses"
)
// DescribeContainerLog invokes the eci.DescribeContainerLog API synchronously
// api document: https://help.aliyun.com/api/eci/describecontainerlog.html
func (client *Client) DescribeContainerLog(request *DescribeContainerLogRequest) (response *DescribeContainerLogResponse, err error) {
response = CreateDescribeContainerLogResponse()
err = client.DoAction(request, response)
return
}
// DescribeContainerLogWithChan invokes the eci.DescribeContainerLog API asynchronously
// api document: https://help.aliyun.com/api/eci/describecontainerlog.html
// asynchronous document: https://help.aliyun.com/document_detail/66220.html
func (client *Client) DescribeContainerLogWithChan(request *DescribeContainerLogRequest) (<-chan *DescribeContainerLogResponse, <-chan error) {
responseChan := make(chan *DescribeContainerLogResponse, 1)
errChan := make(chan error, 1)
err := client.AddAsyncTask(func() {
defer close(responseChan)
defer close(errChan)
response, err := client.DescribeContainerLog(request)
if err != nil {
errChan <- err
} else {
responseChan <- response
}
})
if err != nil {
errChan <- err
close(responseChan)
close(errChan)
}
return responseChan, errChan
}
// DescribeContainerLogWithCallback invokes the eci.DescribeContainerLog API asynchronously
// api document: https://help.aliyun.com/api/eci/describecontainerlog.html
// asynchronous document: https://help.aliyun.com/document_detail/66220.html
func (client *Client) DescribeContainerLogWithCallback(request *DescribeContainerLogRequest, callback func(response *DescribeContainerLogResponse, err error)) <-chan int {
result := make(chan int, 1)
err := client.AddAsyncTask(func() {
var response *DescribeContainerLogResponse
var err error
defer close(result)
response, err = client.DescribeContainerLog(request)
callback(response, err)
result <- 1
})
if err != nil {
defer close(result)
callback(nil, err)
result <- 0
}
return result
}
// DescribeContainerLogRequest is the request struct for api DescribeContainerLog
type DescribeContainerLogRequest struct {
*requests.RpcRequest
ResourceOwnerId requests.Integer `position:"Query" name:"ResourceOwnerId"`
ContainerName string `position:"Query" name:"ContainerName"`
StartTime string `position:"Query" name:"StartTime"`
ContainerGroupId string `position:"Query" name:"ContainerGroupId"`
ResourceOwnerAccount string `position:"Query" name:"ResourceOwnerAccount"`
Tail requests.Integer `position:"Query" name:"Tail"`
OwnerAccount string `position:"Query" name:"OwnerAccount"`
OwnerId requests.Integer `position:"Query" name:"OwnerId"`
}
// DescribeContainerLogResponse is the response struct for api DescribeContainerLog
type DescribeContainerLogResponse struct {
*responses.BaseResponse
RequestId string `json:"RequestId" xml:"RequestId"`
ContainerName string `json:"ContainerName" xml:"ContainerName"`
Content string `json:"Content" xml:"Content"`
}
// CreateDescribeContainerLogRequest creates a request to invoke DescribeContainerLog API
func CreateDescribeContainerLogRequest() (request *DescribeContainerLogRequest) {
request = &DescribeContainerLogRequest{
RpcRequest: &requests.RpcRequest{},
}
request.InitWithApiInfo("Eci", "2018-08-08", "DescribeContainerLog", "eci", "openAPI")
return
}
// CreateDescribeContainerLogResponse creates a response to parse from DescribeContainerLog response
func CreateDescribeContainerLogResponse() (response *DescribeContainerLogResponse) {
response = &DescribeContainerLogResponse{
BaseResponse: &responses.BaseResponse{},
}
return
}

View File

@@ -0,0 +1,22 @@
package eci
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
// ConfigFileVolumeConfigFileToPath is a nested struct in eci response
type ConfigFileToPath struct {
Content string `name:"Content"`
Path string `name:"Path"`
}

34
eci/struct_container.go Normal file
View File

@@ -0,0 +1,34 @@
package eci
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
// Container is a nested struct in eci response
type Container struct {
Name string `json:"Name" xml:"Name" `
Image string `json:"Image" xml:"Image"`
Memory float64 `json:"Memory" xml:"Memory"`
Cpu float64 `json:"Cpu" xml:"Cpu"`
RestartCount int `json:"RestartCount" xml:"RestartCount"`
WorkingDir string `json:"WorkingDir" xml:"WorkingDir"`
ImagePullPolicy string `json:"ImagePullPolicy" xml:"ImagePullPolicy"`
Commands []string `json:"Commands" xml:"Commands"`
Args []string `json:"Args" xml:"Args"`
PreviousState ContainerState `json:"PreviousState" xml:"PreviousState"`
CurrentState ContainerState `json:"CurrentState" xml:"CurrentState"`
VolumeMounts []VolumeMount `json:"VolumeMounts" xml:"VolumeMounts"`
Ports []ContainerPort `json:"Ports" xml:"Ports"`
EnvironmentVars []EnvironmentVar `json:"EnvironmentVars" xml:"EnvironmentVars"`
}

View File

@@ -0,0 +1,38 @@
package eci
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
// ContainerGroup is a nested struct in eci response
type ContainerGroup struct {
ContainerGroupId string `json:"ContainerGroupId" xml:"ContainerGroupId"`
ContainerGroupName string `json:"ContainerGroupName" xml:"ContainerGroupName"`
RegionId string `json:"RegionId" xml:"RegionId"`
ZoneId string `json:"ZoneId" xml:"ZoneId"`
Memory float64 `json:"Memory" xml:"Memory"`
Cpu float64 `json:"Cpu" xml:"Cpu"`
VSwitchId string `json:"VSwitchId" xml:"VSwitchId"`
SecurityGroupId string `json:"SecurityGroupId" xml:"SecurityGroupId"`
RestartPolicy string `json:"RestartPolicy" xml:"RestartPolicy"`
IntranetIp string `json:"IntranetIp" xml:"IntranetIp"`
Status string `json:"Status" xml:"Status"`
InternetIp string `json:"InternetIp" xml:"InternetIp"`
CreationTime string `json:"CreationTime" xml:"CreationTime"`
SucceededTime string `json:"SucceededTime" xml:"SucceededTime"`
Tags []Tag `json:"Tags" xml:"Tags"`
Events []Event `json:"Events" xml:"Events"`
Containers []Container `json:"Containers" xml:"Containers"`
Volumes []Volume `json:"Volumes" xml:"Volumes"`
}

View File

@@ -0,0 +1,26 @@
package eci
import (
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
)
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
// ContainerPort is a nested struct in eci response
type ContainerPort struct {
Port requests.Integer `name:"Port"`
Protocol string `name:"Protocol"`
}

View File

@@ -0,0 +1,25 @@
package eci
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
// CurrentState is a nested struct in eci response
type ContainerState struct {
State string `json:"State" xml:"State"`
DetailStatus string `json:"DetailStatus" xml:"DetailStatus"`
ExitCode int `json:"ExitCode" xml:"ExitCode"`
StartTime string `json:"StartTime" xml:"StartTime"`
FinishTime string `json:"FinishTime" xml:"FinishTime"`
}

View File

@@ -0,0 +1,22 @@
package eci
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
// EnvironmentVar is a nested struct in eci response
type EnvironmentVar struct {
Key string `name:"Key"`
Value string `name:"Value"`
}

26
eci/struct_event.go Normal file
View File

@@ -0,0 +1,26 @@
package eci
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
// Event is a nested struct in eci response
type Event struct {
Count int `json:"Count" xml:"Count"`
Type string `json:"Type" xml:"Type"`
Name string `json:"Name" xml:"Name"`
Message string `json:"Message" xml:"Message"`
FirstTimestamp string `json:"FirstTimestamp" xml:"FirstTimestamp"`
LastTimestamp string `json:"LastTimestamp" xml:"LastTimestamp"`
}

22
eci/struct_tag.go Normal file
View File

@@ -0,0 +1,22 @@
package eci
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
// Label is a nested struct in eci response
type Tag struct {
Key string `name:"Key"`
Value string `name:"Value"`
}

35
eci/struct_volume.go Normal file
View File

@@ -0,0 +1,35 @@
package eci
import "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
// Volume is a nested struct in eci response
const (
VOL_TYPE_NFS = "NFSVolume"
VOL_TYPE_EMPTYDIR = "EmptyDirVolume"
VOL_TYPE_CONFIGFILEVOLUME = "ConfigFileVolume"
)
type Volume struct {
Type string `name:"Type"`
Name string `name:"Name"`
NfsVolumePath string `name:"NFSVolume.Path"`
NfsVolumeServer string `name:"NFSVolume.Server"`
NfsVolumeReadOnly requests.Boolean `name:"NFSVolume.ReadOnly"`
EmptyDirVolumeEnable requests.Boolean `name:"EmptyDirVolume.Enable"`
ConfigFileToPaths []ConfigFileToPath `name:"ConfigFileVolume.ConfigFileToPath" type:"Repeated"`
}

View File

@@ -0,0 +1,25 @@
package eci
import "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
//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.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.
// VolumeMount is a nested struct in eci response
type VolumeMount struct {
MountPath string `name:"MountPath"`
ReadOnly requests.Boolean `name:"ReadOnly"`
Name string `name:"Name"`
}

26
errors.go Normal file
View File

@@ -0,0 +1,26 @@
package alibabacloud
import (
"net/http"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors"
"github.com/cpuguy83/strongerrors"
)
func wrapError(err error) error {
if err == nil {
return nil
}
se, ok := err.(*errors.ServerError)
if !ok {
return err
}
switch se.HttpStatus() {
case http.StatusNotFound:
return strongerrors.NotFound(err)
default:
return err
}
}