Skip to content

Commit

Permalink
Add OpenStack Swift scaler e2e tests (#1522)
Browse files Browse the repository at this point in the history
* Add OpenStack Swift scaler e2e tests

Signed-off-by: Pedro Felipe Dominguite <p.dominguite@sidi.org.br>

Co-authored-by: Antonio Robson de Paula <robson.p@sidi.org.br>

* Add OpenStack helper class

This helper creates an HTTP client using Axios for querying a specific OpenStack project API such as Swift.

Signed-off-by: Pedro Felipe Dominguite <p.dominguite@sidi.org.br>

* Add OpenStack Swift helper class

This helper encapsulates the Swift API operations

Signed-off-by: Pedro Felipe Dominguite <p.dominguite@sidi.org.br>

* Add Axios dependency to package.json

Signed-off-by: Pedro Felipe Dominguite <p.dominguite@sidi.org.br>

* Add environment variables for OpenStack Swift scaler e2e tests

Signed-off-by: Pedro Felipe Dominguite <p.dominguite@sidi.org.br>

* Update CHANGELOG.md

Signed-off-by: Pedro Felipe Dominguite <p.dominguite@sidi.org.br>

* Change test environment variable names

Signed-off-by: Pedro Felipe Dominguite <p.dominguite@sidi.org.br>
  • Loading branch information
pdominguite authored Apr 13, 2021
1 parent 9987d15 commit d9f8591
Show file tree
Hide file tree
Showing 8 changed files with 628 additions and 1 deletion.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

### Other

- TODO ([#XXX](https://github.com/kedacore/keda/issues/XXX))
- Adding OpenStack Swift scaler end-to-end tests ([#1522](https://github.com/kedacore/keda/pull/1522))

## v2.2.0

Expand Down
6 changes: 6 additions & 0 deletions tests/.env
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ AZURE_SUBSCRIPTION=
AZURE_RESOURCE_GROUP=
TEST_LOG_ANALYTICS_WORKSPACE_ID=
TEST_STORAGE_CONNECTION_STRING=
OPENSTACK_AUTH_URL=
OPENSTACK_USER_ID=
OPENSTACK_PASSWORD=
OPENSTACK_PROJECT_ID=
# OPENSTACK_SWIFT_URL=
# OPENSTACK_REGION_NAME=
13 changes: 13 additions & 0 deletions tests/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@types/tmp": "^0.1.0",
"async": "^3.1.0",
"ava": "^2.4.0",
"axios": "^0.21.1",
"azure-storage": "^2.10.3",
"chalk": "^2.4.2",
"eol": "^0.9.1",
Expand Down
316 changes: 316 additions & 0 deletions tests/scalers/openstack-swift.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
import * as sh from 'shelljs'
import * as tmp from 'tmp'
import * as fs from 'fs'
import test from 'ava';

import SwiftClient from './openstack/openstack-swift-helper';

var swiftClient: SwiftClient

const openstackSwiftURL = process.env['OPENSTACK_SWIFT_URL']
const openstackUserID = process.env['OPENSTACK_USER_ID']
const openstackPassword = process.env['OPENSTACK_PASSWORD']
const openstackProjectID = process.env['OPENSTACK_PROJECT_ID']
const openstackAuthURL = process.env['OPENSTACK_AUTH_URL']
const openstackRegionName = process.env['OPENSTACK_REGION_NAME']

const testNamespace = 'openstack-swift-password-test'

const swiftContainerName = 'my-container-password-test'
const swiftContainerObjects = [
'1/',
'1/2/',
'1/2/3/',
'1/2/3/4/',
'1/2/3/hello-world.txt',
'1/2/hello-world.txt',
'1/hello-world.txt',
'2/',
'2/hello-world.txt',
'3/',
]

const deploymentName = 'hello-node-password'
const deploymentImage = "k8s.gcr.io/echoserver:1.4"

const secretYamlFile = tmp.fileSync()
const scaledObjectYamlFile = tmp.fileSync()
const triggerAuthenticationYamlFile = tmp.fileSync()

test.before(t => {
sh.config.silent = true

if (!openstackUserID) {
t.fail('OPENSTACK_USER_ID environment variable is required for running tests')
}

if (!openstackPassword) {
t.fail('OPENSTACK_PASSWORD environment variable is required for running tests')
}

if (!openstackProjectID) {
t.fail('OPENSTACK_PROJECT_ID environment variable is required for running tests')
}

if (!openstackAuthURL) {
t.fail('OPENSTACK_AUTH_URL environment variable is required for running tests')
}
});

test.serial.before(async t => {
try {
swiftClient = await SwiftClient.create()

if (swiftClient) {
await swiftClient.createContainer(swiftContainerName)

for (const object of swiftContainerObjects) {
await swiftClient.createObject(swiftContainerName, object)
}
}
} catch (err) {
t.fail(err.message)
}
});

test.serial.before(async t => {
const base64OpenstackUserID = Buffer.from(openstackUserID).toString('base64')
const base64OpenstackPassword = Buffer.from(openstackPassword).toString('base64')
const base64OpenstackProjectID = Buffer.from(openstackProjectID).toString('base64')
const base64OpenstackAuthURL = Buffer.from(openstackAuthURL).toString('base64')
var base64OpenstackRegionName = ""

if (openstackRegionName) base64OpenstackRegionName = Buffer.from(openstackRegionName).toString('base64')

fs.writeFileSync(secretYamlFile.name, swiftSecretYaml
.replace('{{OPENSTACK_USER_ID}}', base64OpenstackUserID)
.replace('{{OPENSTACK_PASSWORD}}', base64OpenstackPassword)
.replace('{{OPENSTACK_PROJECT_ID}}', base64OpenstackProjectID)
.replace('{{OPENSTACK_AUTH_URL}}', base64OpenstackAuthURL)
.replace('{{OPENSTACK_REGION_NAME}}', base64OpenstackRegionName)
)

fs.writeFileSync(triggerAuthenticationYamlFile.name, swiftTriggerAuthenticationYaml)

fs.writeFileSync(scaledObjectYamlFile.name, swiftScaledObjectYaml
.replace('{{DEPLOYMENT_NAME}}', deploymentName)
.replace('{{OPENSTACK_SWIFT_URL}}', openstackSwiftURL)
.replace('{{CONTAINER_NAME}}', swiftContainerName)
)

sh.exec(`kubectl create namespace ${testNamespace}`)

t.is(
0,
sh.exec(`kubectl create deployment ${deploymentName} --image=${deploymentImage} --namespace ${testNamespace}`).code,
'Creating deployment for scaling tests should work.'
)

t.is(
0,
sh.exec(`kubectl apply -f ${secretYamlFile.name} --namespace ${testNamespace}`).code,
'Creating secret for storing OpenStack credentials should work.'
)

t.is(
0,
sh.exec(`kubectl apply -f ${triggerAuthenticationYamlFile.name} --namespace ${testNamespace}`).code,
'Creating triggerAuthentication should work.'
)
});

test.serial('Total number of objects inside container should be 10', async t => {
try {
const objectCount = await swiftClient.getObjectCount(swiftContainerName)
t.is(objectCount, 10);
} catch (err) {
t.fail(err.message)
}
});

test.serial('Deployment should have 1 replica on start', t => {
let replicaCount = ''

for (let i = 0; i < 20 && replicaCount !== '1'; i++) {
replicaCount = sh.exec(
`kubectl get deployment.apps/${deploymentName} --namespace ${testNamespace} -o jsonpath="{.spec.replicas}"`
).stdout

if (replicaCount !== '1') {
sh.exec('sleep 1s')
}
}

t.is(
replicaCount,
'1',
'Replica count should start out as 1'
)
})

test.serial('Deployment should be scaled to 10 after creating ScaledObject', t => {
t.is(
0,
sh.exec(`kubectl apply -f ${scaledObjectYamlFile.name} --namespace ${testNamespace}`).code,
'Creating scaledObject should work.'
)

let replicaCount = ''

for (let i = 0; i < 40 && replicaCount !== '10'; i++) {
replicaCount = sh.exec(
`kubectl get deployment.apps/${deploymentName} --namespace ${testNamespace} -o jsonpath="{.spec.replicas}"`
).stdout

if (replicaCount !== '10') {
sh.exec('sleep 3s')
}
}

t.is(
replicaCount,
'10',
'Replica count should be 10 after creating ScaledObject'
)
})

test.serial('Deployment should be scaled to 5 after deleting 5 objects in container', async t => {
try {
let replicaCount = ''

await swiftClient.deleteObject(swiftContainerName, '1/2/hello-world.txt')
await swiftClient.deleteObject(swiftContainerName, '1/hello-world.txt')
await swiftClient.deleteObject(swiftContainerName, '2/')
await swiftClient.deleteObject(swiftContainerName, '2/hello-world.txt')
await swiftClient.deleteObject(swiftContainerName, '3/')

for (let i = 0; i < 110 && replicaCount !== '5'; i++) {
replicaCount = sh.exec(
`kubectl get deployment.apps/${deploymentName} --namespace ${testNamespace} -o jsonpath="{.spec.replicas}"`
).stdout

if (replicaCount !== '5') {
sh.exec('sleep 3s')
}
}

t.is(
replicaCount,
'5',
'Replica count should be 5 after creating deleting 5 objects in conatainer'
)
} catch (err) {
t.fail(err.message)
}
})

test.serial('Deployment should be scaled to 0 after deleting all objects in container', async t => {
try {
let replicaCount = '10'

const { isEmpty, response } = await swiftClient.deleteAllObjects(swiftContainerName)

if(!isEmpty) {
t.fail(`Could not delete all objects inside container to test scaling to zero. Swift API returned: ${response}`)
}

for (let i = 0; i < 20 && replicaCount !== '0'; i++) {
replicaCount = sh.exec(
`kubectl get deployment.apps/${deploymentName} --namespace ${testNamespace} -o jsonpath="{.spec.replicas}"`
).stdout

if (replicaCount !== '0') {
sh.exec('sleep 3s')
}
}

t.is(
replicaCount,
'0',
'Replica count should be 0 after creating ScaledObject'
)
} catch (err) {
t.fail(err.message)
}
})

test.after.always('Clean up OpenStack Swift container', async t => {
try {
if(swiftClient) {
await swiftClient.deleteContainer(swiftContainerName)
}
} catch (err) {
t.fail(err.message)
}
});

test.after.always.cb('Clean up Secret, Deployment and openstack-swift scaler resources', t => {
const resources = [
'scaledobject.keda.sh/swift-password-scaledobject',
'triggerauthentication.keda.sh/keda-trigger-password-openstack-secret',
'secret/openstack-password-secrets',
`deployment.apps/${deploymentName}`,
]

for (const resource of resources) {
sh.exec(`kubectl delete ${resource} --namespace ${testNamespace}`)
}

sh.exec(`kubectl delete namespace ${testNamespace}`)

t.end();
});

const swiftSecretYaml = `
apiVersion: v1
kind: Secret
metadata:
name: openstack-password-secrets
type: Opaque
data:
userID: {{OPENSTACK_USER_ID}}
password: {{OPENSTACK_PASSWORD}}
projectID: {{OPENSTACK_PROJECT_ID}}
authURL: {{OPENSTACK_AUTH_URL}}
`

const swiftTriggerAuthenticationYaml = `
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: keda-trigger-password-openstack-secret
spec:
secretTargetRef:
- parameter: userID
name: openstack-password-secrets
key: userID
- parameter: password
name: openstack-password-secrets
key: password
- parameter: projectID
name: openstack-password-secrets
key: projectID
- parameter: authURL
name: openstack-password-secrets
key: authURL
`

const swiftScaledObjectYaml = `
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: swift-password-scaledobject
spec:
scaleTargetRef:
name: {{DEPLOYMENT_NAME}}
pollingInterval: 10
cooldownPeriod: 10
minReplicaCount: 0
triggers:
- type: openstack-swift
metadata:
containerName: {{CONTAINER_NAME}}
objectCount: '1'
authenticationRef:
name: keda-trigger-password-openstack-secret
`
Loading

0 comments on commit d9f8591

Please sign in to comment.