diff --git a/.travis.yml b/.travis.yml index 2db6850..731426b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,31 +12,41 @@ script: # Audit npm packages. Fail build whan a PR audit fails, otherwise report the vulnerability and proceed. - if [ "${TRAVIS_PULL_REQUEST}" != "false" ]; then npm audit; else npm audit || true; fi - npm run lint + - if [[ "${TRAVIS_TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$ ]]; then npm version --no-git-tag-version "${TRAVIS_TAG}"; fi - docker build --rm -t "quay.io/razee/clustersubscription:${TRAVIS_COMMIT}" . - if [ -n "${TRAVIS_TAG}" ]; then docker tag quay.io/razee/clustersubscription:${TRAVIS_COMMIT} quay.io/razee/clustersubscription:${TRAVIS_TAG}; fi - docker images - ./build/process-template.sh kubernetes/ClusterSubscription/resource.yaml >/tmp/resource.yaml - - if [[ "${TRAVIS_TAG}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then npm version --no-git-tag-version "${TRAVIS_TAG}"; fi before_deploy: - docker login -u="${QUAY_ID}" -p="${QUAY_TOKEN}" quay.io deploy: - # Deploy alpha builds - - provider: script - script: docker push "quay.io/razee/clustersubscription:${TRAVIS_TAG}" + # Publish npm package with tag "next" on release candidates + - provider: npm + email: "${NPMJS_EMAIL}" + api_key: "${NPMJS_API_KEY}" + tag: next skip_cleanup: true on: tags: true - condition: ${TRAVIS_TAG} =~ ^[0-9]+\.[0-9]+\.[0-9]+_[0-9]{3}$ - - # Deploy released builds + condition: ${TRAVIS_TAG} =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)$ + # Publish docker image on release and release candidates - provider: script script: docker push "quay.io/razee/clustersubscription:${TRAVIS_TAG}" skip_cleanup: true + on: + tags: true + condition: ${TRAVIS_TAG} =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$ + # Publish npm package as "latest" on release + - provider: npm + email: "${NPMJS_EMAIL}" + api_key: "${NPMJS_API_KEY}" + skip_cleanup: true on: tags: true condition: ${TRAVIS_TAG} =~ ^[0-9]+\.[0-9]+\.[0-9]+$ + # Publish GitHub release assets on release - provider: releases file: /tmp/resource.yaml skip_cleanup: true @@ -45,10 +55,3 @@ deploy: on: tags: true condition: ${TRAVIS_TAG} =~ ^[0-9]+\.[0-9]+\.[0-9]+$ - - provider: npm - email: "${NPMJS_EMAIL}" - api_key: "${NPMJS_API_KEY}" - skip_cleanup: true - on: - tags: true - condition: ${TRAVIS_TAG} =~ ^[0-9]+\.[0-9]+\.[0-9]+$ diff --git a/lib/log.js b/lib/log.js index 43c3e0d..1485b9a 100644 --- a/lib/log.js +++ b/lib/log.js @@ -19,7 +19,7 @@ const winston = require('winston'); const log = winston.createLogger({ - level: (process.env.LOG_LEVEL || 'info'), + level: (process.env.LOG_LEVEL || 'info'), format: winston.format.json(), defaultMeta: { service: 'cluster-subscription' }, transports: [ diff --git a/lib/remoteResource.js b/lib/remoteResource.js index 6270b96..726ed78 100644 --- a/lib/remoteResource.js +++ b/lib/remoteResource.js @@ -3,18 +3,26 @@ const Mustache = require('mustache'); const log = require('./log'); const { KubeClass, KubeApiConfig } = require('@razee/kubernetes-util'); +const objectPath = require('object-path'); const kubeApiConfig = KubeApiConfig(); const kc = new KubeClass(kubeApiConfig); -const API_VERSION = 'deploy.razee.io/v1alpha2'; -const KIND = 'RemoteResource'; +const RR_API_VERSION = 'deploy.razee.io/v1alpha2'; const NAMESPACE = process.env.NAMESPACE; const requestsTemplate = `{ "options": { "url": "{{{url}}}", "headers": { - "razee-org-key": "{{orgKey}}" + "razee-org-key": { + "valueFrom": { + "secretKeyRef":{ + "name": "{{{secretName}}}", + "namespace": "{{namespace}}", + "key": "razee-api-org-key" + } + } + } } } }`; @@ -22,19 +30,21 @@ const requestsTemplate = `{ const createRemoteResources = async (razeeApi, apiKey, subscriptions, clusterId) => { log.info('create remote resources subscription list', { subscriptions }); try { - const krm = await kc.getKubeResourceMeta(API_VERSION, KIND, 'update'); return Promise.all(subscriptions.map(async sub => { + const apiKeyBase64 = Buffer.from(apiKey).toString('base64'); const url = `${razeeApi}/${sub.url}`; - const rendered = Mustache.render(requestsTemplate, { url: url, orgKey: apiKey }); + const secretName = `clustersubscription-${sub.subscriptionUuid}-secret`; + const rendered = Mustache.render(requestsTemplate, { url: url, secretName: secretName, namespace: NAMESPACE }); const parsed = JSON.parse(rendered); - const resourceName = `clustersubscription-${sub.subscriptionUuid}`; + //console.log(parsed); + const remoteResourceName = `clustersubscription-${sub.subscriptionUuid}`; const userName = (sub.kubeOwnerName && typeof sub.kubeOwnerName === 'string') ? sub.kubeOwnerName : 'razeedeploy'; - const resourceTemplate = { - 'apiVersion': API_VERSION, - 'kind': KIND, + const remoteResourceJson = { + 'apiVersion': RR_API_VERSION, + 'kind': 'RemoteResource', 'metadata': { 'namespace': NAMESPACE, - 'name': resourceName, + 'name': remoteResourceName, 'annotations': { 'deploy.razee.io/clustersubscription': sub.subscriptionUuid, 'deploy.razee.io/clusterid': clusterId @@ -47,53 +57,94 @@ const createRemoteResources = async (razeeApi, apiKey, subscriptions, clusterId) 'clusterAuth': { 'impersonateUser': userName }, - 'requests': [] + 'requests': + [] } }; - resourceTemplate.spec.requests.push(parsed); - - const opt = { simple: false, resolveWithFullResponse: true }; - - const uri = krm.uri({ name: resourceName, namespace: NAMESPACE }); - log.debug(resourceName); - const get = await krm.get(resourceName, NAMESPACE, opt); - if (get.statusCode === 200) { - // the remote resource already exists so use mergePatch to apply the resource - log.info(`Attempting mergePatch for an existing resource ${uri}`); - const mergeResult = await krm.mergePatch(resourceName, NAMESPACE, resourceTemplate, opt); - if (mergeResult.statusCode === 200) { - log.info('mergePatch successful', { 'statusCode': mergeResult.statusCode, 'statusMessage': mergeResult.statusMessage }); - } else { - log.error('mergePatch error', { 'statusCode': mergeResult.statusCode, 'statusMessage': mergeResult.statusMessage }); - } - } else if (get.statusCode === 404) { - // the remote resource does not exist so use post to apply the resource - log.info(`Attempting post for a new resource ${uri}`); - const postResult = await krm.post(resourceTemplate, opt); - if (postResult.statusCode === 200 || postResult.statusCode === 201) { - log.info('post successful', { 'statusCode': postResult.statusCode, 'statusMessage': postResult.statusMessage }); - } else { - log.error('post error', { 'statusCode': postResult.statusCode, 'statusMessage': postResult.statusMessage }); + remoteResourceJson.spec.requests.push(parsed); + + const secretJson = { + 'apiVersion': 'v1', + 'kind': 'Secret', + 'metadata': { + 'namespace': NAMESPACE, + 'name': secretName, + 'annotations': { + 'deploy.razee.io/clustersubscription': sub.subscriptionUuid, + 'deploy.razee.io/clusterid': clusterId + }, + 'labels': { + 'razee/watch-resource': 'lite' + } + }, + 'data': { + 'razee-api-org-key': apiKeyBase64 } - } else { - log.error(`Get ${get.statusCode} ${uri}`); - } + }; + + await applyResource(secretJson); + await applyResource(remoteResourceJson); + })); } catch (error) { log.error('There was an error creating remote resources', { error }); } +}; + +const applyResource = async (resourceJson) => { + const resourceName = objectPath.get(resourceJson, 'metadata.name', ''); + const resourceNamespace = objectPath.get(resourceJson, 'metadata.namespace', NAMESPACE); + const resourceApiVersion = objectPath.get(resourceJson, 'apiVersion', RR_API_VERSION); + const resourceKind = objectPath.get(resourceJson, 'kind', ''); + const opt = { simple: false, resolveWithFullResponse: true }; + const krm = await kc.getKubeResourceMeta(resourceApiVersion, resourceKind, 'update'); + const uri = krm.uri({ name: resourceName, namespace: resourceNamespace }); + log.debug(resourceName); + const get = await krm.get(resourceName, resourceNamespace, opt); + if (get.statusCode === 200) { + // the remote resource already exists so use mergePatch to apply the resource + log.info(`Attempting mergePatch for an existing resource ${uri}`); + const mergeResult = await krm.mergePatch(resourceName, resourceNamespace, resourceJson, opt); + if (mergeResult.statusCode === 200) { + log.info('mergePatch successful', { 'statusCode': mergeResult.statusCode, 'statusMessage': mergeResult.statusMessage }); + } else { + log.error('mergePatch error', { 'statusCode': mergeResult.statusCode, 'statusMessage': mergeResult.statusMessage }); + } + } else if (get.statusCode === 404) { + // the remote resource does not exist so use post to apply the resource + log.info(`Attempting post for a new resource ${uri}`); + const postResult = await krm.post(resourceJson, opt); + if (postResult.statusCode === 200 || postResult.statusCode === 201) { + log.info('post successful', { 'statusCode': postResult.statusCode, 'statusMessage': postResult.statusMessage }); + } else { + console.log(postResult); + log.error('post error', { 'statusCode': postResult.statusCode, 'statusMessage': postResult.statusMessage }); + } + } else { + log.error(`Get ${get.statusCode} ${uri}`); + } }; + const deleteRemoteResources = async (resources) => { - const krm = await kc.getKubeResourceMeta(API_VERSION, KIND, 'update'); - const selfLinks = resources.map((resource) => krm.uri({ name: resource.metadata.name, namespace: resource.metadata.namespace })); - log.debug('Deleting', { selfLinks }); + const krm = await kc.getKubeResourceMeta(RR_API_VERSION, 'RemoteResource', 'update'); + const rrSelfLinks = resources.map((resource) => krm.uri({ name: resource.metadata.name, namespace: resource.metadata.namespace })); + log.debug('Deleting', { rrSelfLinks }); + await deleteResource(rrSelfLinks, krm); + const krm_secret = await kc.getKubeResourceMeta('v1', 'Secret', 'update'); + const secretSelfLinks = resources.map((resource) => krm_secret.uri({ name: `${resource.metadata.name}-secret`, namespace: resource.metadata.namespace, })); + log.debug('Deleting', { secretSelfLinks }); + await deleteResource(secretSelfLinks, krm_secret); +}; + +const deleteResource = async (selfLinks, krm) => { try { selfLinks.map(async (selfLink) => { log.info(`Delete ${selfLink}`); const opt = { uri: selfLink, simple: false, resolveWithFullResponse: true, method: 'DELETE' }; + console.log(opt); const res = await krm.request(opt); if (res.statusCode === 404) { log.info(`Delete ${res.statusCode} ${opt.uri}`); @@ -108,13 +159,14 @@ const deleteRemoteResources = async (resources) => { } catch (error) { log.error(error); } + }; const getRemoteResources = async (clusterId) => { log.debug('Getting a list of clustersubscription remote resources on this cluster'); let remoteResources = []; try { - const krm = await kc.getKubeResourceMeta(API_VERSION, KIND, 'get'); + const krm = await kc.getKubeResourceMeta(RR_API_VERSION, 'RemoteResource', 'get'); const opt = { simple: false, resolveWithFullResponse: true }; const get = await krm.get('', NAMESPACE, opt); if (get.statusCode === 200) { @@ -140,3 +192,4 @@ const getRemoteResources = async (clusterId) => { exports.createRemoteResources = createRemoteResources; exports.getRemoteResources = getRemoteResources; exports.deleteRemoteResources = deleteRemoteResources; +exports.applyResource = applyResource; diff --git a/package-lock.json b/package-lock.json index 1b89821..e092aa8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3322,8 +3322,8 @@ }, "object-path": { "version": "0.11.5", - "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.5.tgz", - "integrity": "sha512-jgSbThcoR/s+XumvGMTMf81QVBmah+/Q7K7YduKeKVWL7N111unR2d6pZZarSk6kY/caeNxUDyxOvMWyzoU2eg==" + "resolved": "https://na.artifactory.swg-devops.com/artifactory/api/npm/wcp-alchemy-containers-team-npm-virtual/object-path/-/object-path-0.11.5.tgz?dl=https%3A%2F%2Fregistry.npmjs.org%2Fobject-path%2F-%2Fobject-path-0.11.5.tgz", + "integrity": "sha1-1OPPGWAaUUClWhatcSAZqcULV3o=" }, "oidc-token-hash": { "version": "5.0.1", diff --git a/package.json b/package.json index e2bf709..05bfc2e 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "graphql": "^15.5.0", "graphql-tag": "^2.11.0", "mustache": "^4.1.0", + "object-path": "^0.11.5", "subscriptions-transport-ws": "^0.9.18", "touch": "^3.1.0", "winston": "^3.3.3"