Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: InitContainer support for ACI Connector #204

Merged
merged 66 commits into from
Nov 19, 2022
Merged
Show file tree
Hide file tree
Changes from 59 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
e979e99
ignore helm init and pod specs example
t-ysalazar Jun 7, 2022
7fc6c1b
add initContainers property
t-ysalazar Jun 7, 2022
16c676b
declaration of getInitContainers
t-ysalazar Jun 7, 2022
c991e03
getInitContainers
t-ysalazar Jun 7, 2022
cbe2be4
factorize getVolumeMounts() from getContainers()
t-ysalazar Jun 7, 2022
ff1a32f
fix type errors
t-ysalazar Jun 7, 2022
1b051a5
factorize getEnvironmentVariables
t-ysalazar Jun 7, 2022
b967171
basics of init containers on getInitContainer
t-ysalazar Jun 8, 2022
0ed160d
getBasicContainer
t-ysalazar Jun 8, 2022
179f9ce
getBasicContainer on initContainer
t-ysalazar Jun 8, 2022
c5fba52
implementation of InitContainerDefinition type
t-ysalazar Jun 9, 2022
4ac821a
factorize verifyContainer from getContainer
t-ysalazar Jun 9, 2022
4a63bbf
throw erros with unsupported properties in ACI initContainers
t-ysalazar Jun 9, 2022
c98c7c4
fix .gitignore for PR
t-ysalazar Jun 9, 2022
3d4bd30
unit test for InitContainers
t-ysalazar Jun 9, 2022
db4eef2
revert gitignore changes
t-ysalazar Jun 10, 2022
98686e2
comments on added methods
t-ysalazar Jun 10, 2022
767a445
complete unit test for ACI InitContainers
t-ysalazar Jun 10, 2022
fd52af9
Move the InitContainerDefinition closer to InitContainerProperties
t-ysalazar Jun 10, 2022
bdf2cc1
Revert "ignore helm init and pod specs example"
t-ysalazar Jun 13, 2022
a161e72
e2e for init containers
t-ysalazar Jul 5, 2022
bd4ec99
e2e for init containers
t-ysalazar Jul 5, 2022
443468c
e2e for init containers, move podName and podDir
t-ysalazar Jul 5, 2022
961171e
e2e comments
t-ysalazar Jul 5, 2022
d0d7228
Merge branch 'master' into t-ysalazar/init-containers
t-ysalazar Jul 6, 2022
5daedd1
fix pod specs for init containers in e2e
t-ysalazar Jul 6, 2022
df6eb45
Merge branch 't-ysalazar/init-containers' of https://github.com/t-ysa…
t-ysalazar Jul 6, 2022
5ef36bc
fix pod specs for init containers in e2e
t-ysalazar Jul 6, 2022
4f9b1e2
Merge branch 'virtual-kubelet:master' into master
t-ysalazar Jul 7, 2022
7ba7fb1
Merge branch 'virtual-kubelet:master' into t-ysalazar/init-containers
t-ysalazar Jul 7, 2022
2506197
Merge branch 'virtual-kubelet:master' into t-ysalazar/init-containers
t-ysalazar Jul 11, 2022
3c9d25a
Merge branch 'virtual-kubelet:master' into master
t-ysalazar Jul 11, 2022
fe5cbd1
Merge branch 'virtual-kubelet:master' into master
t-ysalazar Jul 20, 2022
05f87ad
Merge branch 'master' into t-ysalazar/init-containers
t-ysalazar Jul 20, 2022
65e77f5
fix merge conflict
t-ysalazar Jul 20, 2022
cd68b60
Merge branch 't-ysalazar/init-containers' of https://github.com/t-ysa…
t-ysalazar Jul 20, 2022
10f2b3c
Merge branch 'virtual-kubelet:master' into t-ysalazar/init-containers
suselva Jul 21, 2022
bcfe341
Merge branch 'virtual-kubelet:master' into t-ysalazar/init-containers
t-ysalazar Jul 27, 2022
f97e251
added init containers usage to README
fnuarnav Aug 17, 2022
d8600b8
Merge remote-tracking branch 'upstream/master'
fnuarnav Sep 9, 2022
fff9885
Merge branch 'master' into t-ysalazar/init-containers
fnuarnav Sep 9, 2022
1040b31
use separate test for init container for clean namespace
fnuarnav Sep 13, 2022
bfe957b
remove unused code
fnuarnav Sep 13, 2022
6f547ea
removed rg from helm install since role isn't present
fnuarnav Sep 14, 2022
aa31d28
merged changes from upstream
fnuarnav Oct 28, 2022
3d72f73
added initContainer using sdk
fnuarnav Oct 28, 2022
9e7fd19
added unit tests for init containers
fnuarnav Oct 31, 2022
0164951
renoved duplicate test
fnuarnav Oct 31, 2022
007f743
removed init containers from old code before sdk
fnuarnav Oct 31, 2022
b60194b
removed init container test from old code before sdk
fnuarnav Oct 31, 2022
186c220
removed unused import
fnuarnav Oct 31, 2022
7b79e29
Merge remote-tracking branch 'upstream/master' into t-ysalazar/init-c…
fnuarnav Oct 31, 2022
899e83b
fix error with pointer
fnuarnav Nov 1, 2022
2b53bf4
added initContainer to AzureFilesVolume test; log errors; error text …
fnuarnav Nov 2, 2022
0f214fc
removed init containers from limitations
fnuarnav Nov 2, 2022
79920d2
added volume to init container e2e test
fnuarnav Nov 2, 2022
4c1c6ac
Merge remote-tracking branch 'upstream/master' into t-ysalazar/init-c…
fnuarnav Nov 9, 2022
ee2d54d
create pod correctly in e2e test
fnuarnav Nov 9, 2022
0e59b10
Merge remote-tracking branch 'upstream/master' into t-ysalazar/init-c…
fnuarnav Nov 10, 2022
a75b217
added e2e test for init container order
fnuarnav Nov 14, 2022
7ca3314
merged changes from upstream/master
fnuarnav Nov 15, 2022
c28c349
merge with upstream/master
fnuarnav Nov 17, 2022
d69adb9
moved initcontainer e2e tests to a separate file
fnuarnav Nov 17, 2022
096f9b4
fix merge error
fnuarnav Nov 18, 2022
e6ca1f8
fix lint error
fnuarnav Nov 18, 2022
60e6aeb
combined initcontainer test; remove println; use index
fnuarnav Nov 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ This document details configuring the Virtual Kubelet ACI provider.

Virtual Kubelet's ACI provider relies heavily on the feature set that Azure Container Instances provide. Please check the Azure documentation accurate details on region availability, pricing and new features. The list here attempts to give an accurate reference for the features we support in ACI and the ACI provider within Virtual Kubelet.

### features
### Features

* Volumes: empty dir, github repo, projection, Azure Files, Azure Files CSI drivers
* Secure env variables, config maps
Expand All @@ -36,6 +36,7 @@ Virtual Kubelet's ACI provider relies heavily on the feature set that Azure Cont
* Basic Azure Networking support within AKS virtual node
* [Exec support](https://docs.microsoft.com/azure/container-instances/container-instances-exec) for container instances
* Azure Monitor integration or formally known as OMS
* Support for init-containers ([use init containers](#Create-pod-with-init-containers))
helayoty marked this conversation as resolved.
Show resolved Hide resolved

### Limitations

Expand All @@ -44,7 +45,6 @@ Virtual Kubelet's ACI provider relies heavily on the feature set that Azure Cont
* [Limitations](https://docs.microsoft.com/azure/container-instances/container-instances-vnet) with VNet
* VNet peering
* Argument support for exec
* Init containers
* [Host aliases](https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/) support
* downward APIs (i.e podIP)

Expand Down Expand Up @@ -650,6 +650,28 @@ Output:
```
-->

### Create pod with init containers
Multiple init containers can be specified in the podspec similar to how containers are specified

```yaml
spec:
initContainers:
- image: <INIT CONTAINER IMAGE 1>
name: init-container-01
command: [ "/bin/sh" ]
args: [ "-c", "echo \"Hi\"" ]
- image: <INIT CONTAINER IMAGE 2>
name: init-container-02
command: [ "/bin/sh" ]
args: [ "-c", "echo \"Hi\"" ]
containers:
- image: <CONTAINER IMAGE>
imagePullPolicy: Always
name: container
command: [ "/bin/sh" ]
```
More information on init containers can be found in [Kubernetes](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) and [ACI](https://docs.microsoft.com/en-us/azure/container-instances/container-instances-init-container) documentations

## Work around for the virtual kubelet pod

If your pod that's scheduled onto the Virtual Kubelet node is in a pending state please add this workaround to your Virtual Kubelet pod spec.
Expand Down
45 changes: 45 additions & 0 deletions e2e/fixtures/initcontainers_pod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
apiVersion: v1
kind: Pod
metadata:
name: vk-e2e-initcontainers
namespace: vk-test
spec:
initContainers:
- image: alpine
name: init-container-01
command: [ "/bin/sh" ]
args: [ "-c", "echo \"Hi\"" ]
- image: alpine
name: init-container-02
command: [ "/bin/sh" ]
args: [ "-c", "echo \"Hi\"" ]
containers:
helayoty marked this conversation as resolved.
Show resolved Hide resolved
- image: alpine
imagePullPolicy: Always
name: container
command: [
"sh",
"-c",
"while sleep 10; do echo pod with init container; done;"
]
resources:
requests:
memory: 1G
cpu: 1
volumeMounts:
- name: azure
mountPath: /mnt/azure
nodeSelector:
kubernetes.io/role: agent
beta.kubernetes.io/os: linux
type: virtual-kubelet
tolerations:
- key: virtual-kubelet.io/provider
operator: Exists
volumes:
- name: azure
csi:
driver: file.csi.azure.com
volumeAttributes:
secretName: csidriversecret # required
shareName: vncsidriversharename # required
61 changes: 60 additions & 1 deletion e2e/pods_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func TestPodLifecycle(t *testing.T) {
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatal(string(out))
}

cmd = kubectl("apply", "-f", "fixtures/hpa.yml", "--namespace=vk-test")
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatal(string(out))
Expand Down Expand Up @@ -50,7 +51,65 @@ func TestPodLifecycle(t *testing.T) {
time.Sleep(10 * time.Second)
}

t.Log("clean up pod")
t.Log("clean up")
cmd = kubectl("delete", "namespace", "vk-test", "--ignore-not-found")
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatal(string(out))
}
}

func TestPodWithInitContainers(t *testing.T) {
// delete the namespace first
cmd := kubectl("delete", "namespace", "vk-test", "--ignore-not-found")
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatal(string(out))
}

// create namespace
cmd = kubectl("apply", "-f", "fixtures/namespace.yml")
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatal(string(out))
}

testStorageAccount := os.Getenv("CSI_DRIVER_STORAGE_ACCOUNT_NAME")
testStorageKey := os.Getenv("CSI_DRIVER_STORAGE_ACCOUNT_KEY")

cmd = kubectl("create", "secret", "generic", "csidriversecret", "--from-literal", "azurestorageaccountname="+testStorageAccount, "--from-literal", "azurestorageaccountkey="+testStorageKey, "--namespace=vk-test")
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatal(string(out))
}

cmd = kubectl("apply", "-f", "fixtures/initcontainers_pod.yml")
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatal(string(out))
}
deadline, ok := t.Deadline()
timeout := time.Until(deadline)
if !ok {
timeout = 300 * time.Second
}
cmd = kubectl("wait", "--for=condition=ready", "--timeout="+timeout.String(), "pod/vk-e2e-initcontainers", "--namespace=vk-test")
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatal(string(out))
}
t.Log("success create pod")

// query metrics
deadline = time.Now().Add(10 * time.Minute)
for {
t.Log("query metrics ....")
cmd = kubectl("get", "--raw", "/apis/metrics.k8s.io/v1beta1/namespaces/vk-test/pods/vk-e2e-initcontainers")
out, err := cmd.CombinedOutput()
if time.Now().After(deadline) {
t.Fatal("failed to query pod's stats from metrics server API")
}
if err == nil {
t.Logf("success query metrics %s", string(out))
break
}
time.Sleep(10 * time.Second)
}
t.Log("clean up")
cmd = kubectl("delete", "namespace", "vk-test", "--ignore-not-found")
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatal(string(out))
Expand Down
2 changes: 1 addition & 1 deletion hack/e2e/aks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ if [ "$PR_RAND" = "" ]; then
fi

: "${RESOURCE_GROUP:=vk-aci-test-$RANDOM_NUM}"
: "${LOCATION:=westus2}"
: "${LOCATION:=eastus2}"
: "${CLUSTER_NAME:=${RESOURCE_GROUP}}"
: "${NODE_COUNT:=1}"
: "${CHART_NAME:=vk-aci-test-aks}"
Expand Down
93 changes: 93 additions & 0 deletions pkg/provider/aci.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,15 @@ func (p *ACIProvider) CreatePod(ctx context.Context, pod *v1.Pod) error {
return err

}

// get initContainers
initContainers, err := p.getInitContainers(ctx, pod)
if err != nil {
return err
}

// assign all the things
cg.ContainerGroupPropertiesWrapper.ContainerGroupProperties.InitContainers = &initContainers
cg.ContainerGroupPropertiesWrapper.ContainerGroupProperties.Containers = containers
cg.ContainerGroupPropertiesWrapper.ContainerGroupProperties.Volumes = &volumes
cg.ContainerGroupPropertiesWrapper.ContainerGroupProperties.ImageRegistryCredentials = creds
Expand Down Expand Up @@ -802,6 +810,91 @@ func readDockerConfigJSONSecret(secret *v1.Secret, ips []azaci.ImageRegistryCred
return ips, err
}

//verify if Container is properly declared for the use on ACI
func (p *ACIProvider) verifyContainer(container *v1.Container) error {
if len(container.Command) == 0 && len(container.Args) > 0 {
return errdefs.InvalidInput("ACI does not support providing args without specifying the command. Please supply both command and args to the pod spec.")
}
return nil
}

//this method is used for both initConainers and containers
func (p *ACIProvider) getCommand(container *v1.Container) *[]string {
command := append(container.Command, container.Args...)
return &command
}

//get VolumeMounts declared on Container as []aci.VolumeMount
func (p *ACIProvider) getVolumeMounts(container *v1.Container) *[]azaci.VolumeMount {
volumeMounts := make([]azaci.VolumeMount, 0, len(container.VolumeMounts))
for _, v := range container.VolumeMounts {
volumeMounts = append(volumeMounts, azaci.VolumeMount{
Name: &v.Name,
MountPath: &v.MountPath,
ReadOnly: &v.ReadOnly,
})
}
return &volumeMounts
}

//get EnvironmentVariables declared on Container as []aci.EnvironmentVariable
func (p *ACIProvider) getEnvironmentVariables(container *v1.Container) *[]azaci.EnvironmentVariable {
environmentVariable := make([]azaci.EnvironmentVariable, 0, len(container.Env))
for _, e := range container.Env {
fnuarnav marked this conversation as resolved.
Show resolved Hide resolved
if e.Value != "" {
envVar := getACIEnvVar(e)
environmentVariable = append(environmentVariable, envVar)
}
}
return &environmentVariable
}

//get InitContainers defined in Pod as []aci.InitContainerDefinition
func (p *ACIProvider) getInitContainers(ctx context.Context, pod *v1.Pod) ([]azaci.InitContainerDefinition, error) {
initContainers := make([]azaci.InitContainerDefinition, 0, len(pod.Spec.InitContainers))
for i, initContainer := range pod.Spec.InitContainers {
err := p.verifyContainer(&initContainer)
if err != nil {
log.G(ctx).Errorf("couldn't verify container %v", err)
return nil, err
}

if initContainer.Ports != nil {
log.G(ctx).Errorf("azure container instances initcontainers do not support ports")
return nil, errdefs.InvalidInput("azure container instances initContainers do not support ports")
}
if initContainer.Resources.Requests != nil {
log.G(ctx).Errorf("azure container instances initcontainers do not support resources requests")
return nil, errdefs.InvalidInput("azure container instances initContainers do not support resources requests")
}
if initContainer.Resources.Limits != nil {
log.G(ctx).Errorf("azure container instances initcontainers do not support resources limits")
return nil, errdefs.InvalidInput("azure container instances initContainers do not support resources limits")
}
if initContainer.LivenessProbe != nil {
log.G(ctx).Errorf("azure container instances initcontainers do not support livenessProbe")
return nil, errdefs.InvalidInput("azure container instances initContainers do not support livenessProbe")
}
if initContainer.ReadinessProbe != nil {
log.G(ctx).Errorf("azure container instances initcontainers do not support readinessProbe")
return nil, errdefs.InvalidInput("azure container instances initContainers do not support readinessProbe")
}

newInitContainer := azaci.InitContainerDefinition{
Name: &pod.Spec.InitContainers[i].Name,
InitContainerPropertiesDefinition: &azaci.InitContainerPropertiesDefinition {
Image: &pod.Spec.InitContainers[i].Image,
Command: p.getCommand(&initContainer),
VolumeMounts: p.getVolumeMounts(&initContainer),
EnvironmentVariables: p.getEnvironmentVariables(&initContainer),
},
}

initContainers = append(initContainers, newInitContainer)
}
return initContainers, nil
}

func (p *ACIProvider) getContainers(pod *v1.Pod) (*[]azaci.Container, error) {
containers := make([]azaci.Container, 0, len(pod.Spec.Containers))

Expand Down
Loading