diff --git a/.travis.yml b/.travis.yml index 0266262183..6389f8cea7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -93,6 +93,7 @@ jobs: before_script: - ./scripts/ci/job_hooks/before_e2e.sh - ./scripts/ci/check-env/check-cs-env.sh || travis_terminate 1 + - ./scripts/ci/check-env/check-ps-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/core-e2e.sh after_script: ./scripts/ci/job_hooks/after_e2e.sh @@ -114,7 +115,9 @@ jobs: - stage: e2e Test name: process - before_script: ./scripts/ci/job_hooks/before_e2e.sh || travis_terminate 1 + before_script: + - ./scripts/ci/job_hooks/before_e2e.sh || travis_terminate 1 + - ./scripts/ci/check-env/check-ps-env.sh || travis_terminate 1 script: ./scripts/travis/e2e/process-services-e2e.sh after_script: ./scripts/ci/job_hooks/after_e2e.sh diff --git a/lib/cli/README.md b/lib/cli/README.md index f2cdda0934..90b00af9bd 100644 --- a/lib/cli/README.md +++ b/lib/cli/README.md @@ -48,6 +48,7 @@ In develop mode, the CLI takes the prebuilt scripts from the dist folder. |artifact-to-s3 |Get artifact to S3 | |docker-publish |publish docker image| |init-aae-env |Init env| +|init-aps-env |Init aps| |kubectl-delete |delete kubectl | |kubectl-image |This command allows you to update a specific service on the rancher env with a specific tag | |npm-publish | publish on npm | diff --git a/lib/cli/package-lock.json b/lib/cli/package-lock.json index f3915f5135..afb5cbac2d 100644 --- a/lib/cli/package-lock.json +++ b/lib/cli/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@alfresco/js-api": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@alfresco/js-api/-/js-api-4.0.0.tgz", - "integrity": "sha512-k2XFyRueRfDnUWsYRW8hwJfrv8Ftx5ePJBEf5gWkgSlOcSVS0NSQUbq/9x12xGCerXnvjh7CLQNXqwgisBuu2Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@alfresco/js-api/-/js-api-4.1.0.tgz", + "integrity": "sha512-9eWihZ3lNtI08cffrnTqB9G8j9syDbLegyTescj8J1vqcxaXF7pvFCfP1zcNRIzfEErx8ncqjFf+IMbh8lvb7A==", "requires": { "event-emitter": "^0.3.5", "minimatch": "3.0.4", @@ -1141,9 +1141,9 @@ } }, "superagent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.0.0.tgz", - "integrity": "sha512-gBiyDSUR3zbYO8za+MudSNxMFSOhKcZfQ1Anya1DWzk9R32zl++cYUFHXzP3VnyvQO8h/1/uPPA9FUDDtE+qdA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", + "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", "requires": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.2", @@ -1159,11 +1159,11 @@ }, "dependencies": { "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "readable-stream": { diff --git a/lib/cli/scripts/init-aps-env.ts b/lib/cli/scripts/init-aps-env.ts new file mode 100755 index 0000000000..2cf24e6239 --- /dev/null +++ b/lib/cli/scripts/init-aps-env.ts @@ -0,0 +1,421 @@ +let alfrescoApi = require('@alfresco/js-api'); +let program = require('commander'); +let fs = require ('fs'); +const path = require('path'); +const { throwError } = require('rxjs'); +const { AppDefinitionsApi, RuntimeAppDefinitionsApi } = require('@alfresco/js-api'); +let MAX_RETRY = 10; +let counter = 0; +let TIMEOUT = 6000; +const TENANT_DEFAULT_ID = 1; +const TENANT_DEFAULT_NAME = 'default'; +const CONTENT_DEFAULT_NAME = 'adw-content'; +const APS_DEFAULT_APP_NAME = 'e2e-Application'; + +let alfrescoJsApi; + +export default async function () { + await main(); +} + +async function main() { + + program + .version('0.1.0') + .option('--host [type]', 'Remote environment host') + .option('-p, --password [type]', 'password ') + .option('-u, --username [type]', 'username ') + .option('--license [type]', 'APS license S3 path ') + .parse(process.argv); + + await checkEnv(); + + console.log(`***** Step 1 - Check License *****`); + + let licenceUploaded = false; + const hasValidLicense = await hasLicense() ; + if (!hasValidLicense) { + console.log(`Aps License missing`); + const isLicenseFileDownloaded = await downloadLicenseFile(program.license); + if (isLicenseFileDownloaded) { + licenceUploaded = await updateLicense(); + } + } else { + licenceUploaded = true; + console.log(`Aps License present`); + } + let tenantId; + if (licenceUploaded) { + console.log(`***** Step 2 - Check Tenant *****`); + console.log(`is tenandId:${TENANT_DEFAULT_ID} with name:${TENANT_DEFAULT_NAME} present?`); + try { + const hasDefault = await hasDefaultTenant(TENANT_DEFAULT_ID, TENANT_DEFAULT_NAME); + tenantId = TENANT_DEFAULT_ID; + if (!hasDefault) { + // the tenandId should be equal to TENANT_DEFAULT_ID if we choose 1 as id. + tenantId = await createDefaultTenant(TENANT_DEFAULT_NAME); + } + console.log(`***** Step 3 - Add Content Repo *****`); + const isContentPresent = await isContenRepoPresent(TENANT_DEFAULT_ID, CONTENT_DEFAULT_NAME); + if (!isContentPresent) { + console.log(`No content repo with name ${CONTENT_DEFAULT_NAME} found`); + await addContentRepoWithBasic(TENANT_DEFAULT_ID, CONTENT_DEFAULT_NAME) + } + console.log(`***** Step 4 - Create users *****`); + const users = await getUserFromRealm(); + if (tenantId && users && users.length > 0) { + for (let i = 0; i < users.length; i++) { + await createUsers(tenantId, users[i]); + } + for (let i = 0; i < users.length; i++) { + console.log('Impersonate user: '+ users[i].username); + await this.alfrescoJsApiRepo.login(users[i].username, 'password'); + await authorizeUserToContentRepo(users[i]); + + const defaultUser = 'hruser'; + if (users[i].username.includes(defaultUser)) { + console.log(`***** Step verify default app already imported for ${defaultUser} *****`); + const isDefaultAppDepl = await isDefaultAppDeployed(); + if (isDefaultAppDepl !== undefined && !isDefaultAppDepl) { + const appDefinition = await importPublishApp(); + await deployApp(appDefinition.appDefinition.id); + } else { + console.log(`***** Default app already deployed *****`); + } + } + + } + } else { + console.log('Something went wrong. Was not able to create the users'); + } + + } catch (error) { + console.log(`Aps something went wrong. Tenant id ${tenantId}`); + } + } else { + console.log('APS license error: check the configuration'); + } + +} + +async function checkEnv() { + try { + + alfrescoJsApi = new alfrescoApi.AlfrescoApiCompatibility({ + provider: 'ALL', + hostBpm: program.host, + hostEcm: program.host, + authType: 'OAUTH', + oauth2: { + host: `${program.host}/auth/realms/alfresco`, + clientId: "alfresco", + scope: "openid" + } + }); + await alfrescoJsApi.login(program.username, program.password); + } catch (e) { + console.log('Login error environment down or inaccessible'); + counter++; + if (MAX_RETRY === counter) { + console.log('Give up'); + process.exit(1); + } else { + console.log(`Retry in 1 minute attempt N ${counter}`); + sleep(TIMEOUT); + checkEnv(); + } + } +} + +async function hasDefaultTenant(tenantId, tenantName) { + let tenant; + + try { + tenant = await alfrescoJsApi.activiti.adminTenantsApi.getTenant(tenantId); + } catch (error) { + console.log(`Aps: does not have tenant with id: ${tenantId}`); + return false; + } + if (tenant.name === tenantName) { + console.log(`Aps: has default tenantId: ${tenantId} and name ${tenantName}`); + return true; + } else { + console.log(`Wrong configuration. Another tenant has been created with id ${tenant.id} and name ${tenant.name}`); + throwError(`Wrong configuration. Another tenant has been created with id ${tenant.id} and name ${tenant.name}`); + } +} + +async function createDefaultTenant(tenantName) { + let tenantPost = { + 'active': true, + 'maxUsers': 10000, + 'name' : tenantName + }; + + + try { + const tenant = await alfrescoJsApi.activiti.adminTenantsApi.createTenant(tenantPost); + console.log(`APS: Tenant ${tenantName} created with id: ${tenant.id}`); + return tenant.id; + } catch (error) { + console.log(`APS: not able to create the default tenant: ${JSON.parse(error.message)}` ); + } +} + +async function createUsers(tenandId, user) { + console.log(`Create user ${user.email} on tenant: ${tenandId}`); + const passwordCamelCase = 'Password'; + const userJson = { + 'email': user.email, + 'firstName': user.firstName, + 'lastName': user.lastName, + 'status': 'active', + 'type': 'enterprise', + 'password': passwordCamelCase, + 'tenantId': tenandId + }; + + try { + const user = await alfrescoJsApi.activiti.adminUsersApi.createNewUser(userJson); + console.log(`APS: User ${user.email} created with id: ${user.id}`); + return user; + } catch (error) { + console.log(`APS: not able to create the default user: ${error.message}` ); + } +} + +async function updateLicense() { + const fileContent = fs.createReadStream(path.join(__dirname, "/activiti.lic")); + + try { + await alfrescoJsApi.oauth2Auth.callCustomApi( + `${program.host}/activiti-app/app/rest/license`, + 'POST', + {}, + {}, + {}, + { file: fileContent }, + {}, + ['multipart/form-data'], + ['application/json'] + ); + console.log(`Aps License uploaded!`); + return true; + } catch (error) { + console.log(`Aps License failed!` ); + return false; + } +} + +async function isDefaultAppDeployed() { + console.log(`Verify ${APS_DEFAULT_APP_NAME} already deployed`); + try { + let runtimeAppDefinitionsApi = new RuntimeAppDefinitionsApi(alfrescoJsApi); + const availableApps = await runtimeAppDefinitionsApi.getAppDefinitions(); + const defaultApp = availableApps.data && availableApps.data.filter( app => app.name && app.name.includes(APS_DEFAULT_APP_NAME)); + return defaultApp && defaultApp.length > 0; + } catch (error) { + console.log(`Aps app failed to import/Publish!`); + } +} + +async function importPublishApp() { + const appName = `${APS_DEFAULT_APP_NAME}.zip`; + console.log(`Import app ${appName}`); + const fileContent = fs.createReadStream(path.join(__dirname, `../../../apps/process-services-extension-e2e/src/resources/${appName}`)); + + try { + let appdefinitionsApi = new AppDefinitionsApi(alfrescoJsApi); + const result = await appdefinitionsApi.importAndPublishApp(fileContent, {renewIdmEntries: true}); + console.log(`Aps app imported and published!`); + return result; + } catch (error) { + console.log(`Aps app failed to import/Publish!`); + } +} + +async function deployApp(appDefinitioId) { + console.log(`Deploy app with id ${appDefinitioId}`); + const body = { + appDefinitions: [{id: appDefinitioId}] + }; + + try { + let runtimeAppDefinitionsApi = new RuntimeAppDefinitionsApi(alfrescoJsApi); + await runtimeAppDefinitionsApi.deployAppDefinitions(body); + console.log(`Aps app deployed`); + } catch (error) { + console.log(`Aps app failed to deploy!`); + } +} + +async function hasLicense() { + try { + const license = await alfrescoJsApi.oauth2Auth.callCustomApi( + `${program.host}/activiti-app/app/rest/license`, + 'GET', + {}, + {}, + {}, + {}, + {}, + ['application/json'], + ['application/json'] + ); + if (license && license.status === 'valid') { + console.log(`Aps has a valid License!`); + return true + } + console.log(`Aps does NOT have a valid License!`); + return false + } catch (error) { + console.log(`Aps not able to check the license` ); + } +} + +async function getUserFromRealm() { + + try { + const users = await alfrescoJsApi.oauth2Auth.callCustomApi( + `${program.host}/auth/admin/realms/alfresco/users`, + 'GET', + {}, + {}, + {}, + {}, + {}, + ['application/json'], + ['application/json'] + ); + let usersExample = users.filter(user => user.email.includes('@example.com')); + let usersWithoutAdmin = usersExample.filter(user => (user.username !== program.username && user.username !== "client")); + console.log(`Keycloak found ${usersWithoutAdmin.length} users`); + return usersWithoutAdmin; + } catch (error) { + console.log(`APS: not able to fetch user: ${error.message}` ); + } +} + +async function isContenRepoPresent(tenantId, contentName) { + + try { + const contentRepos = await alfrescoJsApi.oauth2Auth.callCustomApi( + `${program.host}/activiti-app/app/rest/integration/alfresco?tenantId=${tenantId}`, + 'GET', + {}, + {}, + {}, + {}, + {}, + ['application/json'], + ['application/json'] + ); + return !!contentRepos.data.find(repo => repo.name === contentName); + } catch (error) { + console.log(`APS: not able to create content: ${error.message}` ); + } +} + +async function addContentRepoWithBasic(tenantId, name) { + console.log(`Create Content with name ${name} and basic auth`); + const body = { + alfrescoTenantId: '', + authenticationType: "basic", + name: name, + repositoryUrl: `${program.host}/alfresco`, + shareUrl: `${program.host}/share`, + // sitesFolder: '', not working on activiti 1.11.1.1 + tenantId: tenantId, + version: "6.1.1" + }; + + + try { + const content = await alfrescoJsApi.oauth2Auth.callCustomApi( + `${program.host}/activiti-app/app/rest/integration/alfresco`, + 'POST', + {}, + {}, + {}, + {}, + body, + ['application/json'], + ['application/json'] + ); + console.log(`Content created!`); + return content; + } catch (error) { + console.log(`APS: not able to create content: ${error.message}` ); + } +} + +async function authorizeUserToContentRepo(user) { + console.log(`Authorize user ${user.email}`); + try { + const content = await alfrescoJsApi.oauth2Auth.callCustomApi( + `${program.host}/activiti-app/app/rest/integration/alfresco`, + 'GET', + {}, + {}, + {}, + {}, + {}, + ['application/json'], + ['application/json'] + ); + console.log(`Found ${content.data && content.data.length} contents`); + if (content.data) { + for (let i = 0; i < content.data.length; i++) { + if (content.data[i].authenticationType === 'basic') { + await authorizeUserToContentWithBasic(user.username, content.data[i].id); + } + } + } + + return; + } catch (error) { + console.log(`APS: not able to authorize content: ${error.message}` ); + } +} + +async function authorizeUserToContentWithBasic(username, contentId) { + console.log(`Authorize ${username} on contentId: ${contentId} in basic auth`); + try { + const body = {username, password: 'password'}; + const content = await alfrescoJsApi.oauth2Auth.callCustomApi( + `${program.host}/activiti-app/app/rest/integration/alfresco/${contentId}/account`, + 'POST', + {}, + {}, + {}, + {}, + body, + ['application/json'], + ['application/json'] + ); + console.log(`User authorized!`); + return content; + } catch (error) { + console.log(`APS: not able to authorize content: ${error.message}` ); + } +} + +async function downloadLicenseFile(apsLicensePath) { + + try { + const child_process = require("child_process"); + child_process.execSync(` aws s3 cp ${apsLicensePath} ./ `, { + cwd: path.resolve(__dirname, `./`) + }); + console.log(`Aps license file download from S3 bucket`); + return true; + } catch (error) { + console.log(`Not able to download the APS license from S3 bucket` ); + return false; + } +} + +function sleep(delay) { + let start = new Date().getTime(); + while (new Date().getTime() < start + delay) ; +} diff --git a/scripts/ci/check-env/check-ps-env.sh b/scripts/ci/check-env/check-ps-env.sh index 42e8207634..891ae465af 100755 --- a/scripts/ci/check-env/check-ps-env.sh +++ b/scripts/ci/check-env/check-ps-env.sh @@ -6,4 +6,4 @@ cd $DIR/../../../ echo "====== Check PS UP =====" -#./node_modules/@alfresco/adf-cli/bin/adf-cli check-ps-env --host "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" || exit 1 TODO chanvge login check SSO +./node_modules/@alfresco/adf-cli/bin/adf-cli init-aps-env --host "$E2E_HOST" -u "$E2E_USERNAME" -p "$E2E_PASSWORD" --license "$AWS_S3_BUCKET_ACTIVITI_LICENSE"