mirror of
https://github.com/bitnami/charts.git
synced 2026-03-01 06:58:20 +08:00
[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:
committed by
GitHub
parent
2f90f61d14
commit
aa8aa2015e
1
.github/workflows/cd-pipeline.yml
vendored
1
.github/workflows/cd-pipeline.yml
vendored
@@ -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/**'
|
||||
|
||||
3
.vib/tensorflow-resnet/cypress/cypress.json
Normal file
3
.vib/tensorflow-resnet/cypress/cypress.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"baseUrl": "http://localhost"
|
||||
}
|
||||
BIN
.vib/tensorflow-resnet/cypress/cypress/fixtures/persian_cat.jpeg
Normal file
BIN
.vib/tensorflow-resnet/cypress/cypress/fixtures/persian_cat.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
12
.vib/tensorflow-resnet/cypress/cypress/support/commands.js
Normal file
12
.vib/tensorflow-resnet/cypress/cypress/support/commands.js
Normal 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();
|
||||
});
|
||||
20
.vib/tensorflow-resnet/cypress/cypress/support/index.js
Normal file
20
.vib/tensorflow-resnet/cypress/cypress/support/index.js
Normal 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')
|
||||
21
.vib/tensorflow-resnet/goss/goss.yaml
Normal file
21
.vib/tensorflow-resnet/goss/goss.yaml
Normal 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 }}/
|
||||
11
.vib/tensorflow-resnet/goss/vars.yaml
Normal file
11
.vib/tensorflow-resnet/goss/vars.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
containerPorts:
|
||||
server: 8500
|
||||
restApi: 8501
|
||||
service:
|
||||
ports:
|
||||
server: 8501
|
||||
restApi: 80
|
||||
containerSecurityContext:
|
||||
runAsUser: 1002
|
||||
podSecurityContext:
|
||||
fsGroup: 1002
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user