[bitnami/tensorflow-resnet] Add tests and publishing using VIB (#12619)

* [bitnami/tensorflow-resnet] Add tests

Signed-off-by: Jose Antonio Carmona <jcarmona@vmware.com>

* Publish using VIB

Signed-off-by: Jose Antonio Carmona <jcarmona@vmware.com>

* Only verify that prediction works

Signed-off-by: Jose Antonio Carmona <jcarmona@vmware.com>

* Do not use chai assertions for arrays & update https port

Signed-off-by: Jose Antonio Carmona <jcarmona@vmware.com>

Signed-off-by: Jose Antonio Carmona <jcarmona@vmware.com>
This commit is contained in:
Jose Antonio Carmona
2022-09-23 16:20:59 +02:00
committed by GitHub
parent 2f90f61d14
commit aa8aa2015e
10 changed files with 215 additions and 0 deletions

View File

@@ -64,6 +64,7 @@ on: # rebuild any PRs and main branch changes
- 'bitnami/spark/**'
- 'bitnami/spring-cloud-dataflow/**'
- 'bitnami/suitecrm/**'
- 'bitnami/tensorflow-resnet/**'
- 'bitnami/tomcat/**'
- 'bitnami/wildfly/**'
- 'bitnami/wordpress/**'

View File

@@ -0,0 +1,3 @@
{
"baseUrl": "http://localhost"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,55 @@
/// <reference types="cypress" />
it('classifies sample image', () => {
cy.fixture('persian_cat.jpeg').then((base64Img) => {
cy.loadB64Image(base64Img).then((loadedImg) => {
const img = loadedImg.get()[0];
let inputImgWidth = img.width;
let inputImgHeight = img.height;
// Convert the image to an array of RGB values
// Based on the tutorial:
// https://developers.google.com/codelabs/classify-images-tensorflow-serving#6
let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
canvas.width = inputImgWidth;
canvas.height = inputImgHeight;
let imgTensor = new Array();
let pixelArray = new Array();
context.drawImage(img, 0, 0);
for (let i = 0; i < inputImgHeight; i++) {
pixelArray[i] = new Array();
for (let j = 0; j < inputImgWidth; j++) {
pixelArray[i][j] = new Array();
pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[0] / 255);
pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[1] / 255);
pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[2] / 255);
}
}
imgTensor.push(pixelArray);
let data = JSON.stringify({
instances: imgTensor,
});
cy.request({
method: 'POST',
headers: { 'Content-Type': 'application/json; charset=utf-8' },
url: `v1/models/resnet:predict`,
body: data,
}).then((response) => {
expect(response.status).to.eq(200);
const predictionByCategory = response.body.predictions[0];
expect(
Array.isArray(predictionByCategory) &&
!predictionByCategory.includes('') &&
!predictionByCategory.includes(null)
).to.be.true;
const maxValue = Math.max(...predictionByCategory);
expect(maxValue !== -Infinity && !isNaN(maxValue)).to.be.true;
});
});
});
});

View File

@@ -0,0 +1,12 @@
Cypress.Commands.add('loadB64Image', (base64) => {
const loadImage = () => {
const img = new Image();
return new Promise((resolve) => {
img.onload = () => {
resolve(img);
};
img.src = 'data:image/jpeg;base64,' + base64;
});
};
return loadImage();
});

View File

@@ -0,0 +1,20 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@@ -0,0 +1,21 @@
addr:
tcp://tensorflow-resnet:{{ .Vars.service.ports.server }}:
reachable: true
port:
tcp:{{ .Vars.containerPorts.server }}:
listening: true
tcp:{{ .Vars.containerPorts.restApi }}:
listening: true
file:
/bitnami/model-data:
exists: true
filetype: directory
mode: "2777"
owner: root
command:
check-user-info:
exec: id
exit-status: 0
stdout:
- uid={{ .Vars.containerSecurityContext.runAsUser }}
- /groups=.*{{ .Vars.podSecurityContext.fsGroup }}/

View File

@@ -0,0 +1,11 @@
containerPorts:
server: 8500
restApi: 8501
service:
ports:
server: 8501
restApi: 80
containerSecurityContext:
runAsUser: 1002
podSecurityContext:
fsGroup: 1002

View File

@@ -16,6 +16,52 @@
}
]
},
"verify": {
"context": {
"resources": {
"url": "{SHA_ARCHIVE}",
"path": "/bitnami/tensorflow-resnet"
},
"runtime_parameters": "Y29udGFpbmVyUG9ydHM6CiAgc2VydmVyOiA4NTAwCiAgcmVzdEFwaTogODUwMQpzZXJ2aWNlOgogIHR5cGU6IExvYWRCYWxhbmNlcgogIHBvcnRzOgogICAgc2VydmVyOiA4NTAxCiAgICByZXN0QXBpOiA4MApjb250YWluZXJTZWN1cml0eUNvbnRleHQ6CiAgZW5hYmxlZDogdHJ1ZQogIHJ1bkFzVXNlcjogMTAwMgogIHJ1bkFzTm9uUm9vdDogdHJ1ZQpwb2RTZWN1cml0eUNvbnRleHQ6CiAgZW5hYmxlZDogdHJ1ZQogIGZzR3JvdXA6IDEwMDIK",
"target_platform": {
"target_platform_id": "{VIB_ENV_TARGET_PLATFORM}",
"size": {
"name": "S4"
}
}
},
"actions": [
{
"action_id": "health-check",
"params": {
"endpoint": "lb-tensorflow-resnet-tf-serving-api",
"app_protocol": "HTTP"
}
},
{
"action_id": "goss",
"params": {
"resources": {
"path": "/.vib/tensorflow-resnet/goss"
},
"remote": {
"workload": "deploy-tensorflow-resnet"
},
"vars_file": "vars.yaml"
}
},
{
"action_id": "cypress",
"params": {
"resources": {
"path": "/.vib/tensorflow-resnet/cypress"
},
"endpoint": "lb-tensorflow-resnet-tf-serving-api",
"app_protocol": "HTTP"
}
}
]
},
"publish": {
"actions": [
{

View File

@@ -15,6 +15,52 @@
"action_id": "helm-lint"
}
]
},
"verify": {
"context": {
"resources": {
"url": "{SHA_ARCHIVE}",
"path": "/bitnami/tensorflow-resnet"
},
"runtime_parameters": "Y29udGFpbmVyUG9ydHM6CiAgc2VydmVyOiA4NTAwCiAgcmVzdEFwaTogODUwMQpzZXJ2aWNlOgogIHR5cGU6IExvYWRCYWxhbmNlcgogIHBvcnRzOgogICAgc2VydmVyOiA4NTAxCiAgICByZXN0QXBpOiA4MApjb250YWluZXJTZWN1cml0eUNvbnRleHQ6CiAgZW5hYmxlZDogdHJ1ZQogIHJ1bkFzVXNlcjogMTAwMgogIHJ1bkFzTm9uUm9vdDogdHJ1ZQpwb2RTZWN1cml0eUNvbnRleHQ6CiAgZW5hYmxlZDogdHJ1ZQogIGZzR3JvdXA6IDEwMDIK",
"target_platform": {
"target_platform_id": "{VIB_ENV_TARGET_PLATFORM}",
"size": {
"name": "S4"
}
}
},
"actions": [
{
"action_id": "health-check",
"params": {
"endpoint": "lb-tensorflow-resnet-tf-serving-api",
"app_protocol": "HTTP"
}
},
{
"action_id": "goss",
"params": {
"resources": {
"path": "/.vib/tensorflow-resnet/goss"
},
"remote": {
"workload": "deploy-tensorflow-resnet"
},
"vars_file": "vars.yaml"
}
},
{
"action_id": "cypress",
"params": {
"resources": {
"path": "/.vib/tensorflow-resnet/cypress"
},
"endpoint": "lb-tensorflow-resnet-tf-serving-api",
"app_protocol": "HTTP"
}
}
]
}
}
}