diff --git a/CloudronManifest.json b/CloudronManifest.json
index b4f5896..d5c69b7 100644
--- a/CloudronManifest.json
+++ b/CloudronManifest.json
@@ -5,11 +5,19 @@
"description": "file://DESCRIPTION.md",
"changelog": "file://CHANGELOG",
"tagline": "Distributed object storage",
- "version": "1.165.1",
+ "version": "2.0.0",
"healthCheckPath": "/minio/login",
"httpPort": 8000,
+ "tcpPorts": {
+ "API_PORT": {
+ "title": "API PORT",
+ "description": "API PORT",
+ "defaultValue": 9000
+ }
+ },
"addons": {
- "localstorage": {}
+ "localstorage": {},
+ "ldap": {}
},
"manifestVersion": 2,
"website": "http://www.minio.io",
diff --git a/DESCRIPTION.md b/DESCRIPTION.md
index e2ff5b2..f03e6d1 100644
--- a/DESCRIPTION.md
+++ b/DESCRIPTION.md
@@ -1,4 +1,4 @@
-This app packages Minio 2021-07-08T01-15-01Z.
+This app packages Minio 2021-07-15T22-27-34Z.
Minio is a distributed object storage server built for cloud applications and devops.
diff --git a/Dockerfile b/Dockerfile
index d79a82f..865677f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,9 +3,11 @@ FROM cloudron/base:3.0.0@sha256:455c70428723e3a823198c57472785437eb6eab082e79b3f
RUN mkdir -p /app/code
WORKDIR /app/code
-ARG VERSION=RELEASE.2021-07-08T01-15-01Z
+ARG VERSION=RELEASE.2021-07-15T22-27-34Z
+ARG MC_VERSION=RELEASE.2021-06-13T17-48-22Z
-RUN wget https://dl.min.io/server/minio/release/linux-amd64/minio.${VERSION} -O /app/code/minio && chmod +x /app/code/minio
+RUN wget https://dl.min.io/server/minio/release/linux-amd64/minio.${VERSION} -O /app/code/minio && chmod +x /app/code/minio && \
+ wget https://dl.min.io/client/mc/release/linux-amd64/mc.${MC_VERSION} -O /app/code/mc && chmod +x /app/code/mc
ADD start.sh /app/code/start.sh
ADD minio-credentials /app/code/minio-credentials
diff --git a/start.sh b/start.sh
index 611cf2a..d3ea368 100755
--- a/start.sh
+++ b/start.sh
@@ -9,11 +9,38 @@ mkdir -p /app/data/data /run/minio/config /run/minio/certs
echo "==> Changing ownership"
[[ $(stat --format '%U' /app/data/data) != "cloudron" ]] && chown -R cloudron:cloudron /app/data
-[[ ! -f /app/data/env.sh ]] && echo -e "# Add custom minio configuration to this file. Restart the app for changes to take effect.\n\nexport CLOUDRON_MINIO_STARTUP_ARGS='server /app/data/data'" > /app/data/env.sh
+if [[ ! -f /app/data/env.sh ]]; then
+ echo -e "# Add custom minio configuration to this file. Restart the app for changes to take effect.\n\nexport CLOUDRON_MINIO_STARTUP_ARGS='server /app/data/data'" > /app/data/env.sh
+ # https://github.com/minio/minio#things-to-consider
+ echo -e "export MINIO_BROWSER_REDIRECT_URI=$(echo $CLOUDRON_APP_ORIGIN)" >> /app/data/env.sh
+ # ###### ! WARNING ! LDAP IS DISABLED FOR NOW ######
+ # https://github.com/minio/minio/blob/master/docs/sts/ldap.md
+ # https://docs.min.io/minio/baremetal/security/ad-ldap-external-identity-management/configure-ad-ldap-external-identity-management.html#minio-authenticate-using-ad-ldap-generic
+ # (address) AD/LDAP server address e.g. "myldapserver.com:636"
+ # echo -e "export MINIO_IDENTITY_LDAP_SERVER_ADDR='$(echo $CLOUDRON_LDAP_SERVER):$(echo $CLOUDRON_LDAP_PORT)'" >> /app/data/env.sh
+ # (string) DN for LDAP read-only service account used to perform DN and group lookups
+ # echo -e "export MINIO_IDENTITY_LDAP_LOOKUP_BIND_DN='$(echo $CLOUDRON_LDAP_BIND_DN)'" >> /app/data/env.sh
+ # (string) Password for LDAP read-only service account used to perform DN and group lookups
+ # echo -e "export MINIO_IDENTITY_LDAP_LOOKUP_BIND_PASSWORD='$(echo $CLOUDRON_LDAP_BIND_PASSWORD)'" >> /app/data/env.sh
+ # (string) Base LDAP DN to search for user DN
+ # echo -e "export MINIO_IDENTITY_LDAP_USER_DN_SEARCH_BASE_DN='$(echo $CLOUDRON_LDAP_USERS_BASE_DN)'" >> /app/data/env.sh
+ # (string) Search filter to lookup user DN
+ # echo -e "export MINIO_IDENTITY_LDAP_USER_DN_SEARCH_FILTER='(&(objectclass=user)(|(username=%uid)(mail=%uid)))'" >> /app/data/env.sh
+ # https://docs.min.io/minio/baremetal/reference/minio-server/minio-server.html#envvar.MINIO_IDENTITY_LDAP_TLS_SKIP_VERIFY
+ # echo -e "export MINIO_IDENTITY_LDAP_TLS_SKIP_VERIFY='on'" >> /app/data/env.sh
+ # https://docs.min.io/minio/baremetal/reference/minio-server/minio-server.html#envvar.MINIO_IDENTITY_LDAP_TLS_SKIP_VERIFY
+ # echo -e "export MINIO_IDENTITY_LDAP_SERVER_INSECURE='on'" >> /app/data/env.sh
+ # ###### ! WARNING ! LDAP IS DISABLED FOR NOW ######
+fi
+
+if [[ ! -d /app/data/mc_config ]]; then
+ echo "==> Set /app/data/mc default config dir"
+ mkdir -p /app/data/mc_config
+ /app/code/mc --config-dir /app/data/mc_config &> /dev/null || true
+fi
source /app/data/env.sh
# the --config-dir is deprecated and not used. but without it, minio will try to create $HOME/.minio :/ same for --certs-dir
echo "==> Starting minio"
-exec /usr/local/bin/gosu cloudron:cloudron /app/code/minio --certs-dir /run/minio/certs --config-dir /run/minio/config --quiet ${CLOUDRON_MINIO_STARTUP_ARGS} --address :8000
-
+exec /usr/local/bin/gosu cloudron:cloudron /app/code/minio --certs-dir /run/minio/certs --config-dir /run/minio/config --quiet ${CLOUDRON_MINIO_STARTUP_ARGS} --address :$API_PORT --console-address :8000
diff --git a/test/test.js b/test/test.js
index 67ec4ec..4d3bd95 100644
--- a/test/test.js
+++ b/test/test.js
@@ -1,9 +1,11 @@
#!/usr/bin/env node
+/* jshint esversion: 8 */
/* global describe */
/* global before */
/* global after */
/* global it */
+/* global xit */
'use strict';
@@ -15,7 +17,6 @@ var execSync = require('child_process').execSync,
{ Builder, By, Key, until } = require('selenium-webdriver'),
{ Options } = require('selenium-webdriver/chrome');
-
describe('Application life cycle test', function () {
this.timeout(0);
@@ -23,130 +24,121 @@ describe('Application life cycle test', function () {
const TEST_TIMEOUT = 10000;
const EXEC_ARGS = { cwd: path.resolve(__dirname, '..'), stdio: 'inherit' };
const BUCKET = 'cloudrontestbucket';
+ const username = process.env.USERNAME;
+ const password = process.env.PASSWORD;
- var app;
- var browser;
+ let browser, app;
+
+ before(function (done) {
+ if (!process.env.PASSWORD) return done(new Error('PASSWORD env var not set'));
+ if (!process.env.USERNAME) return done(new Error('USERNAME env var not set'));
- before(function () {
browser = new Builder().forBrowser('chrome').setChromeOptions(new Options().windowSize({ width: 1280, height: 1024 })).build();
+ done();
});
after(function () {
browser.quit();
});
+ function sleep(millis) {
+ return new Promise(resolve => setTimeout(resolve, millis));
+ }
+
+ async function waitForElement(elem) {
+ await browser.wait(until.elementLocated(elem), TEST_TIMEOUT);
+ await browser.wait(until.elementIsVisible(browser.findElement(elem)), TEST_TIMEOUT);
+ }
+
function getAppInfo() {
var inspect = JSON.parse(execSync('cloudron inspect'));
app = inspect.apps.filter(function (a) { return a.location.indexOf(LOCATION) === 0; })[0];
expect(app).to.be.an('object');
}
- function pageLoaded() {
- return browser.wait(until.elementLocated(By.className('page-load pl-0 pl-1')), TEST_TIMEOUT);
+ async function login(accessKey='minioadmin', secretKey='minioadmin') {
+ await browser.get(`https://${app.fqdn}/login`);
+ await waitForElement(By.id('accessKey'));
+ await browser.findElement(By.id('accessKey')).sendKeys(accessKey);
+ await browser.findElement(By.id('secretKey')).sendKeys(secretKey);
+ await browser.findElement(By.xpath('//button[@type="submit"]/span[text()="Login"]')).click();
+ await waitForElement(By.xpath(`//div/span[contains(text(), "Dashboard")]`));
}
- function visible(selector) {
- return browser.wait(until.elementLocated(selector), TEST_TIMEOUT).then(function () {
- return browser.wait(until.elementIsVisible(browser.findElement(selector)), TEST_TIMEOUT);
- });
+ async function old_login(accessKey='minioadmin', secretKey='minioadmin') {
+ await browser.get(`https://${app.fqdn}/minio/login`);
+ await waitForElement(By.id('accessKey'));
+ await browser.findElement(By.id('accessKey')).sendKeys(accessKey);
+ await browser.findElement(By.id('secretKey')).sendKeys(secretKey);
+ await browser.findElement(By.xpath('//button[@type="submit"]')).click();
+ await waitForElement(By.xpath(`//input[@placeholder="Search Buckets..."]`));
}
- function login(accessKey, secretKey, callback) {
- browser.manage().deleteAllCookies();
- browser.get('https://' + app.fqdn).then(function () {
- return visible(By.id('accessKey'));
- }).then(function () {
- return browser.findElement(By.id('accessKey')).sendKeys(accessKey);
- }).then(function () {
- return browser.findElement(By.id('secretKey')).sendKeys(secretKey);
- }).then(function () {
- // return browser.findElement(By.className('lw-btn')).click();
- return browser.findElement(By.tagName('form')).submit();
- }).then(function () {
- return browser.wait(until.elementLocated(By.id('top-right-menu')), TEST_TIMEOUT);
- }).then(function () {
- callback();
- });
+ async function logout() {
+ await browser.get(`https://${app.fqdn}/`);
+ await waitForElement(By.xpath(`//div/span[contains(text(), "Dashboard")]`));
+ await browser.findElement(By.xpath('//div/span[contains(text(), "Logout")]')).click();
+ await waitForElement(By.id('accessKey'));
}
- function logout(callback) {
- browser.get('https://' + app.fqdn);
-
- pageLoaded().then(function () {
- return visible(By.id('top-right-menu'));
- }).then(function () {
- return browser.findElement(By.id('top-right-menu')).click();
- }).then(function () {
- if (app.manifest.version === '1.137.0') {
- return visible(By.xpath('//*[text()="Sign Out "]'));
- } else {
- return visible(By.xpath('//*[contains(text(), "Logout")]'));
- }
- }).then(function () {
- if (app.manifest.version === '1.137.0') {
- return browser.findElement(By.xpath('//*[text()="Sign Out "]')).click();
- } else {
- return browser.findElement(By.xpath('//*[contains(text(),"Logout")]')).click();
- }
- }).then(function () {
- return browser.wait(until.elementLocated(By.id('accessKey')), TEST_TIMEOUT);
- }).then(function () {
- callback();
- });
+ async function old_logout() {
+ await browser.get(`https://${app.fqdn}/`);
+ await waitForElement(By.xpath(`//input[@placeholder="Search Buckets..."]`));
+ await browser.findElement(By.xpath('//div/button[@id="top-right-menu"]')).click();
+ await browser.findElement(By.xpath('//ul/li/a[@id="logout"]')).click();
+ await waitForElement(By.id('accessKey'));
}
- function addBucket(callback) {
- browser.get('https://' + app.fqdn);
-
- pageLoaded().then(function () {
- return visible(By.className('fa-plus'));
- }).then(function () {
- return browser.findElement(By.className('fa-plus')).click();
- }).then(function () {
- const c = 'fa-hdd';
- return visible(By.className(c));
- }).then(function () {
- const c = 'fa-hdd';
- return browser.findElement(By.className(c)).click();
- }).then(function () {
- return visible(By.xpath('//*[@class="modal-body"]/form/div/input'));
- }).then(function () {
- return browser.findElement(By.xpath('//*[@class="modal-body"]/form/div/input')).sendKeys(BUCKET);
- }).then(function () {
- return browser.findElement(By.xpath('//*[@class="modal-body"]/form')).submit();
- }).then(function () {
- return visible(By.xpath('//*[@class="main"]/a[text()="' + BUCKET + '"]'));
- }).then(function () {
- callback();
- });
+ async function addBucket() {
+ await browser.get(`https://${app.fqdn}/`);
+ await waitForElement(By.xpath('//div/span[contains(text(), "Bucket")]'));
+ await browser.findElement(By.xpath('//div/span[contains(text(), "Bucket")]')).click();
+ await waitForElement(By.xpath('//span[text()="Create Bucket"]'));
+ await browser.findElement(By.xpath('//span[text()="Create Bucket"]')).click();
+ await browser.findElement(By.xpath('//input[@id="bucket-name"]')).sendKeys(BUCKET);
+ await browser.findElement(By.xpath('//button[@type="submit"]/span[text()="Save"]')).click();
+ await waitForElement(By.xpath(`//div/span[contains(text(), "${BUCKET}")]`));
+ await browser.findElement(By.xpath(`//div/span[contains(text(), "${BUCKET}")]`));
}
- function checkBucket(callback) {
- browser.get('https://' + app.fqdn);
-
- pageLoaded().then(function () {
- return browser.findElement(By.xpath(`//a[contains(text(), ${BUCKET})]`));
- }).then(function () {
- callback();
- });
+ async function old_addBucket() {
+ await browser.get(`https://${app.fqdn}/`);
+ await waitForElement(By.xpath(`//input[@placeholder="Search Buckets..."]`));
+ await browser.findElement(By.xpath('//div/button[@id="fe-action-toggle"]')).click();
+ await browser.findElement(By.xpath('//ul/a[@id="show-make-bucket"]')).click();
+ await browser.findElement(By.xpath('//input[@placeholder="Bucket Name"]')).sendKeys(BUCKET);
+ await browser.findElement(By.xpath('//*[@class="modal-body"]/form')).submit();
+ await waitForElement(By.xpath(`//*[@class="main"]/a[text()="${BUCKET}"]`));
}
- function openSettings(callback) {
- browser.get('https://' + app.fqdn);
+ async function checkBucket() {
+ await browser.get(`https://${app.fqdn}/`);
+ await waitForElement(By.xpath(`//div/span[contains(text(), "Dashboard")]`));
+ await browser.findElement(By.xpath('//div/span[contains(text(), "Bucket")]')).click();
+ await waitForElement(By.xpath(`//div/span[contains(text(), "${BUCKET}")]`));
+ await browser.findElement(By.xpath(`//div/span[contains(text(), "${BUCKET}")]`));
+ }
- pageLoaded().then(function () {
- return visible(By.id('top-right-menu'));
- }).then(function () {
- return browser.findElement(By.id('top-right-menu')).click();
- }).then(function () {
- return visible(By.xpath('//*[contains(text(), "Change Password")]'));
- }).then(function () {
- return browser.findElement(By.xpath('//*[contains(text(),"Change Password")]')).click();
- }).then(function () {
- return browser.wait(until.elementLocated(By.xpath('//*[contains(text(), "Change Password")]')), TEST_TIMEOUT);
- }).then(function () {
- callback();
- });
+ async function old_checkBucket() {
+ await browser.get(`https://${app.fqdn}/`);
+ await waitForElement(By.xpath(`//input[@placeholder="Search Buckets..."]`));
+ await waitForElement(By.xpath(`//*[@class="main"]/a[text()="${BUCKET}"]`));
+ }
+
+ async function openSettings() {
+ await browser.get(`https://${app.fqdn}/`);
+ await waitForElement(By.xpath(`//div/span[contains(text(), "Dashboard")]`));
+ await browser.findElement(By.xpath('//div/span[contains(text(), "Account")]')).click();
+ await waitForElement(By.xpath(`//button/span[text()="Change Password"]`));
+ await browser.findElement(By.xpath('//button/span[text()="Change Password"]'));
+ }
+
+ async function old_openSettings() {
+ await browser.get(`https://${app.fqdn}/`);
+ await waitForElement(By.xpath(`//input[@placeholder="Search Buckets..."]`));
+ await browser.findElement(By.xpath('//div/button[@id="top-right-menu"]')).click();
+ await waitForElement(By.xpath('//ul/li/a[contains(text(), "Change Password")]'));
+ await browser.findElement(By.xpath('//ul/li/a[contains(text(), "Change Password")]'));
}
xit('build app', function () { execSync('cloudron build', EXEC_ARGS); });
@@ -199,10 +191,11 @@ describe('Application life cycle test', function () {
it('can install app', function () { execSync('cloudron install --appstore-id io.minio.cloudronapp --location ' + LOCATION, EXEC_ARGS); });
it('can get app information', getAppInfo);
- it('can login', login.bind(null, 'minioadmin', 'minioadmin'));
- it('can add buckets', addBucket);
- it('can logout', logout);
- it('can update', function () { execSync('cloudron update --app ' + LOCATION, EXEC_ARGS); });
+ it('can login', old_login.bind(null, 'minioadmin', 'minioadmin'));
+ it('can add buckets', old_addBucket);
+ it('can logout', old_logout);
+ it('can update', function () { execSync(`cloudron update --app ${LOCATION} --no-wait`, EXEC_ARGS); });
+ it('can enable API Port', function () { execSync(`cloudron configure --app ${LOCATION} -p API_PORT=9000 -l ${LOCATION} `, EXEC_ARGS); });
it('can get app information', getAppInfo);
it('can login', login.bind(null, 'minioadmin', 'minioadmin'));