diff --git a/.codefresh/codefresh.yaml b/.codefresh/codefresh.yaml index 07f19f82..c33081a5 100644 --- a/.codefresh/codefresh.yaml +++ b/.codefresh/codefresh.yaml @@ -22,6 +22,7 @@ steps: commands: - export VERSION=$(jq -r ".version" package.json) - cf_export VERSION + # - export FILE_VERSION= $(cat ./venonactl/VERSION) - cf_export FILE_VERSION=$(cat ./venonactl/VERSION) when: steps: diff --git a/.codefresh/test-agent.yaml b/.codefresh/test-agent.yaml deleted file mode 100644 index 02f5bf76..00000000 --- a/.codefresh/test-agent.yaml +++ /dev/null @@ -1,82 +0,0 @@ -version: '1.0' - -stages: -- Test -- Notification - -mode: parallel - -steps: - - create_namespace: - stage: Test - title: 'Create namespace in Kuberentes' - image: codefresh/kube-helm - commands: - - kubectl config use-context $KUBE_CONTEXT - - kubectl create namespace $KUBE_NAMESPACE - - install_agent: - stage: Test - title: 'Install agent' - image: codefresh/cli - commands: - # Install agent, runtime and attach it - - codefresh install agent --name $AGENT_NAME --kube-namespace $KUBE_NAMESPACE --install-runtime --kube-context-name $KUBE_CONTEXT --kube-config-path $KUBECONFIG - - codefresh run -b master $TEST_PIPELINE_ID - when: - steps: - - name: create_namespace - on: - - success - - uninstall_agent: - stage: Test - title: 'Uninstall agent' - image: codefresh/cli - commands: - # Uninstall agent, runtime - - codefresh uninstall agent --name $AGENT_NAME --kube-namespace $KUBE_NAMESPACE - - codefresh uninstall runtime --runtime-name $KUBE_CONTEXT/$KUBE_NAMESPACE --kube-namespace $KUBE_NAMESPACE --kube-context-name $KUBE_CONTEXT --kube-config-path $KUBECONFIG - when: - steps: - - name: install_agent - on: - - success - - delete_namespace: - stage: Test - title: 'Delete namespace in Kuberentes' - image: codefresh/kube-helm - commands: - - kubectl delete namespace $KUBE_NAMESPACE - when: - steps: - - name: uninstall_agent - on: - - success - - - reported_failure: - stage: Notification - title: Report failure to Slack - type: slack-message-sender - arguments: - WEBHOOK_URL: ${{SLACK_WEBHOOK_URL}} - MESSAGE: "Venona installation failed, link: $CF_BUILD_URL" - when: - steps: - any: - - name: create_namespace - on: - - failure - - name: create_namespace - on: - - failure - - name: delete_namespace - on: - - failure - - name: uninstall_agent - on: - - failure - diff --git a/.eslintrc.yml b/.eslintrc.yml index 37b3f05a..0d80a269 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -23,7 +23,7 @@ rules: semi: - error - always - "jest/no-disabled-tests": "error" + "jest/no-disabled-tests": "warn" "jest/no-focused-tests": "error" "jest/no-identical-title": "error" "jest/prefer-to-have-length": "warn" diff --git a/.gitignore b/.gitignore index 84cc8ec8..976800d2 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,3 @@ telepresence.log venonactl/dist/* venonactl-linux venonalog.json -.venonaconf diff --git a/README.md b/README.md index ddba15fe..c1658fab 100644 --- a/README.md +++ b/README.md @@ -2,72 +2,6 @@ [![Go Report Card](https://goreportcard.com/badge/github.com/codefresh-io/venona)](https://goreportcard.com/report/github.com/codefresh-io/venona) [![Codefresh build status]( https://g.codefresh.io/api/badges/pipeline/codefresh-inc/codefresh-io%2Fvenona%2Fvenona?type=cf-1)]( https://g.codefresh.io/public/accounts/codefresh-inc/pipelines/codefresh-io/venona/venona) -## Version 1.x.x -Version 1.0.0 is released now, read more about migration from older version [here](#Migration) -We highly suggest to use [Codefresh official CLI](https://codefresh-io.github.io/cli/) to install the agent: -```bash -kubectl create namespace codefresh -codefresh install agent --kube-namespace codefresh --install-runtime -``` - -The last command will: -1. Install the agent on the namespace `codefresh` -2. Install the runtime on the same namespace -3. Attach the runtime to the agent - -It is still possible, for advanced users to install all manually, for example: -One process of Venona can manage multiple runtime environments -NOTE: Please make sure that the process where Venona is installed there is a network connection to the clusters where the runtimes will be installed -```bash -# 1. Create namespace for the agent: -kubectl create namespace codefresh-agent - -# 2. Install the agent on the namespace ( give your agent a unique): -# Print a token that the Venona process will be using. -codefresh create agent $NAME -codefresh install agent --token $TOKEN --kube-namespace codefresh-agent - -# 3. Create namespace for the first runtime: -kubectl create namespace codefresh-runtime-1 - -# 4. Install the first runtime on the namespace -# 5. the runtime name is printed -codefresh install runtime --kube-namespace codefresh-runtime-1 - -# 6. Attach the first runtime to agent: -codefresh attach runtime --agent-name $AGENT_NAME --agent-kube-namespace codefresh-agent --runtime-name $RUNTIME_NAME --kube-namespace codefresh-runtime-1 - -# 7. Restart the venona pod in namespace `codefresh-agent` -kubectl delete pods $VENONA_POD - -# 8. Create namespace for the second runtime -kubectl create namespace codefresh-runtime-2 - -# 9. Install the second runtime on the namespace -codefresh install runtime --kube-namespace codefresh-runtime-2 - -# 10. Attach the second runtime to agent and restart the Venoa pod automatically -codefresh attach runtime --agent-name $AGENT_NAME --agent-kube-namespace codefresh-agent --runtime-name $RUNTIME_NAME --runtime-kube-namespace codefresh-runtime-1 --restart-agent - -``` - -## Migration -Migrating from Venona `< 1.x.x` to `> 1.x.x` is not done automatically, please use the [migration script](https://github.com/codefresh-io/venona/blob/master/scripts/migration.sh) to do that, check out which environment variables are required to run it. -```bash -# This script comes to migrate old versions of Venona installation ( version < 1.x.x ) to new version (version >= 1.0.0 ) -# Please read carefully what the script does. -# There will be a "downtime" in terms of your builds targeted to this runtime environment -# Once the script is finished, all the builds during the downtime will start -# The script will: -# 1. Create new agent entity in Codefresh using Codefresh CLI - give it a name $CODEFRESH_AGENT_NAME, default is "codefresh" -# 2. Install the agent on you cluster pass variables: -# a. $VENONA_KUBE_NAMESPACE - required -# b. $VENONA_KUBE_CONTEXT - default is current-context -# c. $VENONA_KUBECONFIG_PATH - default is $HOME/.kube/config -# 3. Attach runtime to the new agent (downtime ends) - pass $CODEFRESH_RUNTIME_NAME - required -``` - - ## Installation ### Prerequisite: @@ -78,13 +12,42 @@ Migrating from Venona `< 1.x.x` to `> 1.x.x` is not done automatically, please u * [Codefresh](https://codefresh-io.github.io/cli/) - Used to create resource in Codefresh * Authenticated context exist under `$HOME/.cfconfig` or authenticate with [Codefesh CLI](https://codefresh-io.github.io/cli/getting-started/#authenticate) + ### Install venona * Download [venona's](https://github.com/codefresh-io/venona/releases) binary * With homebrew: * `brew tap codefresh-io/venona` * `brew install venona` - +* Create namespace where venona should run
+ > `kubectl create namespace codefresh-runtime` +* Create *new* runtime-environment with Venona's agents installed
+ > `venona install --kube-namespace codefresh-runtime` +* Get the status
+ > `venona status` + > `kubectl get pods -n codefresh-runtime` + +#### Install Options + +| Option Argument | Type | Description | +| -------------------- | -------- | --------------------------------------------------- | +| --build-annotations | stringArray | The kubernetes metadata.annotations as "key=value" to be used by venona build resources (default is no node selector) | +| --build-node-selector | string | The kubernetes node selector "key=value" to be used by venona build resources (default is no node selector) | +| --cluster-name | string | cluster name (if not passed runtime-environment will be created cluster-less); this is a friendly name used for metadata does not need to match the literal cluster name. Limited to 20 Characters. | +| --dry-run | boolean | Set to true to simulate installation | +| -h, --help | boolean | help for install | +| --in-cluster | boolean | Set flag if venona is been installed from inside a cluster | +| --kube-context-name | string | Name of the kubernetes context on which venona should be installed (default is current-context) [$KUBE_CONTEXT] | +| --kube-namespace | string |Name of the namespace on which venona should be installed [$KUBE_NAMESPACE] | +| --kube-node-selector | string | The kubernetes node selector "key=value" to be used by venona resources (default is no node selector) | +| --kubernetes-runner-type | boolean | Set the runner type to kubernetes (alpha feature) | +| --only-runtime-environment | boolean | Set to true to onlky configure namespace as runtime-environment for Codefresh | +| --runtime-environment | string | if --skip-runtime-installation set, will try to configure venona on current runtime-environment | +| --set-default | boolean | Mark the install runtime-environment as default one after installation | +| --skip-runtime-installation | boolean | Set flag if you already have a configured runtime-environment, add --runtime-environment flag with name | +| --storage-class | string | Set a name of your custom storage class, note: this will not install volume provisioning components | +| --tolerations | string | The kubernetes tolerations as JSON string to be used by venona resources (default is no tolerations). If prefixed with "@", loads from a file: @/tmp/tolerations.json | +| --venona-version | string | Version of venona to install (default is the latest) | #### Install on cluster version < 1.10 * Make sure the `PersistentLocalVolumes` [feature gate](https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/) is turned on @@ -115,11 +78,27 @@ Each one has own RBAC needs and therefore, created roles(and cluster-roles) The resource descriptors are avaliable [here](https://github.com/codefresh-io/venona/tree/master/venonactl/templates/kubernetes) List of the resources that will be created * Agent (grouped by `/.*.venona.yaml/`) - * `service-account.re.yaml` - The service account that the Venona pod will use to create the resource on the runtime namespace(the resoucre installed on the runtime namespace) - * `role.re.yaml` - Allow to GET, CREATE and DELETE pods and persistentvolumeclaims - * `role-binding.re.yaml` - The agent is spinning up pods and pvc, this biniding binds `role.venona.yaml` to `service-account.venona.yaml` + * `service-account.venona.yaml` - The service account that the agent's pod will use at the end * `cluster-role-binding.venona.yaml` - The agent discovering K8S apis by calling to `openapi/v2`, this ClusterRoleBinding binds bootstraped ClusterRole by Kubernetes `system:discovery` to `service-account.venona.yaml`. This role has only permissions to make a GET calls to non resources urls + * `role.venona.yaml` - Allow to GET, CREATE and DELETE pods and persistentvolumeclaims + * `role-binding.venona.yaml` - The agent is spinning up pods and pvc, this biniding binds `role.venona.yaml` to `service-account.venona.yaml` * Runtime-environment (grouped by `/.*.re.yaml/`) Kubernetes controller that spins up all required resources to provide a good caching expirience during pipeline execution * `service-account.dind-volume-provisioner.re.yaml` - The service account that the controller will use * `cluster-role.dind-volume-provisioner.re.yaml` Defines all the permission needed for the controller to operate correctly * `cluster-role-binding.dind-volume-provisioner.yaml` - Binds the ClusterRole to `service-account.dind-volume-provisioner.re.yaml` + +### Access the cluster from executed pipeline +After a successfull installation of Venona, you'll be able to run a Codefresh pipeline on the configured cluster. +However, the pipeline itself dosent have any permission to connect to the hosted cluster. +To make it work you need to add the cluster to Codefresh (make sure the service acount has all the permissions you need) +> codefresh create cluster --kube-context CONTEXT_NAME --namespace NAMESPACE --serviceaccount SERVICE_ACCOUNT --behind-firewall + +#### Upgrade +To upgrade existing runtime-environment, a one that was created without Venona's agent, run: +* Find the name of the cluster was linked to that runtime environment
+Example: `codefresh get cluster` +* Install
+Example: `venona install --cluster-name CLUSTER` +* Get the status
+Example: `venona status RUNTIME-ENVIRONMENT` +Example: `kubectl get pods -n NAMESPACE` diff --git a/__mocks__/recursive-readdir.js b/__mocks__/recursive-readdir.js deleted file mode 100644 index d8205362..00000000 --- a/__mocks__/recursive-readdir.js +++ /dev/null @@ -1,15 +0,0 @@ -let files = []; - -const recursive = async (path, ignore, cb) => { - return cb(null, files); -}; - -recursive.__setFiles = (names) => { - files = files.concat(names); -}; - -recursive.__clear = () => { - files = []; -}; - -module.exports = recursive; \ No newline at end of file diff --git a/agent/__tests__/index.spec.js b/agent/__tests__/index.spec.js index d531095f..f83b20ab 100644 --- a/agent/__tests__/index.spec.js +++ b/agent/__tests__/index.spec.js @@ -1,31 +1,24 @@ - +const _ = require('lodash'); const Promise = require('bluebird'); const scheduler = require('node-schedule'); -const fs = require('fs'); -const path = require('path'); -const _ = require('lodash'); -const recursive = require('recursive-readdir'); const Agent = require('./../'); const Codefresh = require('./../../services/Codefresh'); +const Kubernetes = require('./../../services/Kubernetes'); const Logger = require('./../../services/Logger'); const { Server } = require('./../../server'); const TaskPullerJob = require('./../../jobs/TaskPullerJob/TaskPuller.job'); const StatusReporterJob = require('./../../jobs/StatusReporterJob/StatusReporter.job'); -const Kubernetes = require('./../../services/Kubernetes'); jest.mock('./../../services/Codefresh'); -jest.mock('./../../services/Logger'); jest.mock('./../../services/Kubernetes'); +jest.mock('./../../services/Logger'); jest.mock('./../../server'); -jest.mock('fs'); -jest.mock('recursive-readdir'); const buildTestConfig = () => ({ metadata: { name: 'agent', version: '1.0', mode: 'mode', - venonaConfDir: '/path/to/venona/config/dir' }, server: { port: '9000', @@ -60,13 +53,6 @@ const buildTestConfig = () => ({ } }); -const loadActualJobs = () => { - recursive.__setFiles([ - path.join(__dirname, '../../', 'jobs/StatusReporterJob/StatusReporter.job.js'), - path.join(__dirname, '../../', 'jobs/TaskPullerJob/TaskPuller.job.js'), - ]); -}; - beforeEach(() => { Server.mockImplementationOnce(() => ({ init: jest.fn(), @@ -74,149 +60,181 @@ beforeEach(() => { Codefresh.mockImplementationOnce(() => ({ init: jest.fn(), })); - recursive.__setFiles([]); - fs.readdir.mockImplementationOnce((path, cb) => { - cb(null, []); - }); -}); - -afterEach(() => { - recursive.__clear(); + Kubernetes.mockImplementationOnce(() => ({ + init: jest.fn(), + })); + Kubernetes.buildFromConfig = jest.fn(Kubernetes); + Kubernetes.buildFromInCluster = jest.fn(Kubernetes); }); describe('Agent unit test', () => { + describe('Constructing new Agent', () => { + + describe('positive', () => { + it('Should construct successfully', () => { + const agent = new Agent(buildTestConfig()); + expect(Object.keys(agent).sort()).toEqual([ + 'kubernetesAPI', + 'codefreshAPI', + 'logger', + 'jobs', + 'queue', + 'server', + ].sort()); + }); + + it('Should create logger during construction', () => { + new Agent(buildTestConfig()); + expect(Logger.create).toHaveBeenCalled(); + }); + + it('Should create logger during construction with specific keys', () => { + new Agent(buildTestConfig()); + const callsArguments = Logger.create.mock.calls[0]; + expect(Object.keys(callsArguments[0])).toEqual(['name', 'version', 'mode']); + expect(Object.keys(callsArguments[1])).toEqual(['prettyPrint', 'level']); + }); + + it('Should call logger with message during construction', () => { + new Agent(buildTestConfig()); + const callsArguments = Logger.create.mock.instances[1].info.mock.calls[0][0]; + expect(callsArguments).toEqual('Starting agent'); + }); + + it('Should Codefresh API service during construction just once', () => { + new Agent(buildTestConfig()); + const totalCallsToCodefreshConstructor = Codefresh.mock.calls; + expect(totalCallsToCodefreshConstructor).toHaveLength(1); + }); + + it('Should construct CodefreshAPI service with specific keys', () => { + new Agent(buildTestConfig()); + const callsArguments = Codefresh.mock.calls[0]; + expect(callsArguments).toHaveLength(2); + expect(Object.keys(callsArguments[0])).toEqual(['name', 'version', 'mode']); + expect(Object.keys(callsArguments[1])).toEqual(['baseURL', 'token']); + }); + + it('Should construct KubernetesAPI service with specific keys', () => { + new Agent(buildTestConfig()); + const callsArguments = Kubernetes.buildFromConfig.mock.calls[0]; + expect(callsArguments).toHaveLength(2); + expect(Object.keys(callsArguments[0])).toEqual(['name', 'version', 'mode']); + expect(Object.keys(callsArguments[1])).toEqual(['config']); + }); + + it('Should construct KubernetesAPI service when agent running from inside a cluster', () => { + Kubernetes.buildFromInCluster = jest.fn(); + new Agent(_.merge(buildTestConfig(), { metadata: { name: 'fake-name', mode: 'InCluster' } })); + const callsArguments = Kubernetes.buildFromInCluster.mock.calls[0]; + expect(Kubernetes.buildFromInCluster).toHaveBeenCalled(); + expect(Object.keys(callsArguments[0])).toEqual(['name', 'version', 'mode']); + }); + + it('Should construct Server with specific keys', () => { + new Agent(buildTestConfig()); + const callsArguments = Server.mock.calls[0]; + expect(callsArguments).toHaveLength(3); + expect(Object.keys(callsArguments[0])).toEqual(['name', 'version', 'mode']); + expect(Object.keys(callsArguments[1])).toEqual(['port']); + expect(Object.keys(callsArguments[2])).toEqual(['info', 'child', 'error']); + }); + + }); + + describe('negative', () => { + it('Should throw an error in case the agent was not constructed correctly', () => { + try { + Logger.create.mockImplementationOnce(() => { + throw new Error('error'); + }); + new Agent(buildTestConfig()); + } catch (err) { + expect(err.toString()).toEqual('Error: error'); + } + }); + }); + + }); describe('Initializing agent', () => { describe('positive', () => { - it('Should report all services been initialized', async () => { - const agent = new Agent(); - await agent.init(buildTestConfig()); - const loggerSuccessMessage = Logger.create.mock.instances[1].info.mock.calls[2][0]; - expect(loggerSuccessMessage).toEqual('All services has been initialized'); - + it('Should report all services been initialized', () => { + return new Agent(buildTestConfig()) + .init() + .then(() => { + const loggerSuccessMessage = Logger.create.mock.instances[1].info.mock.calls[2][0]; + expect(loggerSuccessMessage).toEqual('All services has been initialized'); + }); }); - it('Should call to Server initialization process during agent initialization', async () => { + it('Should call to Server initialization process during agent initialization', () => { const serverInitSpy = jest.fn(); Server.mockReset(); Server.mockImplementationOnce(() => ({ init: serverInitSpy, })); - const agent = new Agent(); - await agent.init(buildTestConfig()); - expect(serverInitSpy).toHaveBeenCalledTimes(1); - expect(serverInitSpy).toHaveBeenCalledWith(); - + return new Agent(buildTestConfig()) + .init() + .then(() => { + expect(serverInitSpy).toHaveBeenCalledTimes(1); + expect(serverInitSpy).toHaveBeenCalledWith(); + }); }); - it('Should call to CodefreshAPI initialization process during agent initialization', async () => { + it('Should call to CodefreshAPI initialization process during agent initialization', () => { const codefreshInitSpy = jest.fn(); Codefresh.mockReset(); Codefresh.mockImplementation(() => ({ init: codefreshInitSpy, })); - jest.unmock('recursive-readdir'); - const agent = new Agent(); - await agent.init(buildTestConfig()); - expect(codefreshInitSpy).toHaveBeenCalledTimes(1); - expect(codefreshInitSpy).toHaveBeenCalledWith(); + return new Agent(buildTestConfig()) + .init() + .then(() => { + expect(codefreshInitSpy).toHaveBeenCalledTimes(1); + expect(codefreshInitSpy).toHaveBeenCalledWith(); + }); + }); + it('Should call to KubernetesAPI initialization process during agent initialization', () => { + const kubernetesInitSpy = jest.fn(); + Kubernetes.mockReset(); + Kubernetes.mockImplementation(() => ({ + init: kubernetesInitSpy, + })); + return new Agent(buildTestConfig()) + .init() + .then(() => { + expect(kubernetesInitSpy).toHaveBeenCalledTimes(1); + expect(kubernetesInitSpy).toHaveBeenCalledWith(); + }); }); - it('Should call to _startJob for both supported tasks', async () => { - loadActualJobs(); - const agent = new Agent(); + it('Should call to _startJob for both supported tasks', () => { + const agent = new Agent(buildTestConfig()); agent._startJob = jest.fn(); - await agent.init(buildTestConfig()); - expect(agent._startJob).toHaveBeenCalledTimes(2); - expect(agent._startJob).toHaveBeenNthCalledWith(1, StatusReporterJob); - expect(agent._startJob).toHaveBeenNthCalledWith(2, TaskPullerJob); - + return agent + .init() + .then(() => { + expect(agent._startJob).toHaveBeenCalledTimes(2); + expect(agent._startJob).toHaveBeenNthCalledWith(1, StatusReporterJob); + expect(agent._startJob).toHaveBeenNthCalledWith(2, TaskPullerJob); + }); }); }); - - describe('Prepare server', () => { + + describe('negative', () => { + it('Should throw an error when initialization crashed', () => { Server.mockReset(); Server.mockImplementationOnce(() => ({ init: jest.fn().mockRejectedValue(new Error('Error!')), })); - return expect(new Agent().init(buildTestConfig())).rejects.toThrow('Failed to initialize agent with error, message: Error!'); + return expect(new Agent(buildTestConfig()).init()).rejects.toThrow('Failed to initialize agent with error message'); }); }); - describe('Prepare runtimes', () => { - it('Should fail to init in case failed to read metadata.venonaConfDir directory', async () => { - fs.readdir.mockReset(); - fs.readdir.mockImplementationOnce((path, cb) => cb(new Error('Failed to read directory'))); - const agent = new Agent(); - return expect(agent.init(buildTestConfig())).rejects.toThrow('Failed to initialize agent with error, message: Failed to read directory'); - }); - it('Should log warning that nasted directory under metadata.venonaConfDir is not going to be read', async () => { - fs.readdir.mockReset(); - fs.readdir.mockImplementationOnce((path, cb) => cb(null, [ - 'sub-dir', - ])); - fs.stat.mockImplementation((path, cb) => { - if (path.includes('sub-di')) { - cb(null, { - isDirectory: () => true, - }); - } - }); - - const agent = new Agent(); - await agent.init(buildTestConfig()); - expect(agent.logger.warn).toHaveBeenCalledTimes(1); - expect(agent.logger.warn).toHaveBeenCalledWith('Directory "sub-dir" ignored, Venona loading only files that are mached to regexp /.*\\.runtime\\.yaml/'); - }); - it('Should log warning that file that is not matched to the regexp is ignored from being loaded', async () => { - fs.readdir.mockReset(); - fs.readdir.mockImplementationOnce((path, cb) => cb(null, [ - 'file', - ])); - fs.stat.mockImplementation((path, cb) => { - cb(null, { - isDirectory: () => false, - }); - }); - - const agent = new Agent(); - await agent.init(buildTestConfig()); - expect(agent.logger.warn).toHaveBeenCalledTimes(1); - expect(agent.logger.warn).toHaveBeenCalledWith('File "file" ignored, Venona loading only files that are mached to regexp /.*\\.runtime\\.yaml/'); - }); - it('Should log warning in case found duplication of runtimes', async () => { - const db = { - 'a.runtime.yaml': 'name: runtime', - 'b.runtime.yaml': 'name: runtime', - }; - fs.readdir.mockReset(); - fs.readdir.mockImplementationOnce((path, cb) => { - cb(null, _.keys(db)); - }); - fs.stat.mockImplementation((path, cb) => { - cb(null, { - isDirectory: () => false, - }); - }); - fs.readFile.mockImplementation((location, cb) => { - cb(null, db[path.basename(location)]); - }); - fs.access.mockImplementation((path, cb) => { - cb(null); - }); - Kubernetes.mockImplementation(() => ({ - init: Promise.resolve, - })); - Kubernetes.buildFromConfig = jest.fn(Kubernetes); - - const agent = new Agent(); - await expect(agent.init(buildTestConfig())).resolves.toEqual(); - expect(agent.logger.warn).toHaveBeenCalledWith('Ignoring runtime "runtime", already been configured, conflict with file: "/path/to/venona/config/dir/b.runtime.yaml"'); - }); - }); }); describe('Queue', () => { @@ -226,8 +244,7 @@ describe('Agent unit test', () => { cb(); }); const spy = jest.fn(); - const agent = new Agent(); - await agent.init(buildTestConfig()); + const agent = new Agent(buildTestConfig()); const FakeJob = function() { this.exec = spy; return this; @@ -243,8 +260,7 @@ describe('Agent unit test', () => { cb(); }); const error = new Error('my error'); - const agent = new Agent(); - await agent.init(buildTestConfig()); + const agent = new Agent(buildTestConfig()); const FakeJob = function() { this.exec = jest.fn().mockRejectedValue(error); return this; @@ -264,8 +280,7 @@ describe('Agent unit test', () => { cb(); }); const error = new Error('my error'); - const agent = new Agent(); - await agent.init(buildTestConfig()); + const agent = new Agent(buildTestConfig()); const FakeJob = function() { this.exec = jest.fn(() => { throw (error); @@ -282,12 +297,13 @@ describe('Agent unit test', () => { expect(handleErrorSpy).toHaveBeenCalledWith(error); }); + it.skip('Should call cb with an error in case a job timedout', () => {}); + it('Should call cb in case the job was resolved', async () => { scheduler.scheduleJob = jest.fn((_ex, cb) => { cb(); }); - const agent = new Agent(); - await agent.init(buildTestConfig()); + const agent = new Agent(buildTestConfig()); const FakeJob = function() { this.exec = jest.fn(); return this; @@ -306,8 +322,7 @@ describe('Agent unit test', () => { scheduler.scheduleJob = jest.fn((_ex, cb) => { cb(); }); - const agent = new Agent(); - await agent.init(buildTestConfig()); + const agent = new Agent(buildTestConfig()); const FakeJob = function() { this.exec = jest.fn(); return this; @@ -324,10 +339,9 @@ describe('Agent unit test', () => { describe('Auto jobs load', () => { it('Should load only jobs that related to agent', async () => { - loadActualJobs(); - const agent = new Agent(); + const agent = new Agent(buildTestConfig()); agent._startJob = jest.fn(); - await agent.init(buildTestConfig()); + await agent._loadJobs(); expect(agent._startJob).toHaveBeenCalledTimes(2); }); }); @@ -338,8 +352,7 @@ describe('Agent unit test', () => { scheduler.scheduleJob = jest.fn((_ex, cb) => { cb(); }); - const agent = new Agent(); - await agent.init(buildTestConfig()); + const agent = new Agent(buildTestConfig()); const FakeJob = function() { this.exec = jest.fn(); return this; @@ -350,12 +363,11 @@ describe('Agent unit test', () => { expect(agent.logger.child).toHaveBeenCalledTimes(1); }); - it('Should pass all services and runtimes the Job constructor', async () => { + it('Should pass all services to the Job constructor', async () => { scheduler.scheduleJob = jest.fn((_ex, cb) => { cb(); }); - const agent = new Agent(); - await agent.init(buildTestConfig()); + const agent = new Agent(buildTestConfig()); const FakeJob = jest.fn().mockImplementation(() => { this.exec = jest.fn(); return this; @@ -366,15 +378,14 @@ describe('Agent unit test', () => { }); agent._startJob(FakeJob); await Promise.delay(100); - expect(FakeJob).toHaveBeenCalledWith(agent.codefreshAPI, {}, newTaskLogger); + expect(FakeJob).toHaveBeenCalledWith(agent.codefreshAPI, agent.kubernetesAPI, newTaskLogger); }); it('Should push the job to the queue', async () => { scheduler.scheduleJob = jest.fn((_ex, cb) => { cb(); }); - const agent = new Agent(); - await agent.init(buildTestConfig()); + const agent = new Agent(buildTestConfig()); const FakeJob = function() { this.exec = jest.fn(); return this; diff --git a/agent/index.js b/agent/index.js index e31e8284..995fe678 100644 --- a/agent/index.js +++ b/agent/index.js @@ -3,80 +3,32 @@ const Chance = require('chance'); const scheduler = require('node-schedule'); const path = require('path'); const _ = require('lodash'); -const fs = require('fs'); -const yaml = require('js-yaml'); const Queue = require('async/priorityQueue'); const recursive = require('recursive-readdir'); const Codefresh = require('./../services/Codefresh'); const Kubernetes = require('./../services/Kubernetes'); const Logger = require('./../services/Logger'); const { Server } = require('./../server'); -const { LOGGER_NAMESPACES } = require('./../constants'); +const { LOGGER_NAMESPACES, AGENT_MODES } = require('./../constants'); -const ERROR_MESSAGES = {}; +const ERROR_MESSAGES = { +}; class Agent { - - async init(config = {}) { - try { - this.logger = Logger.create(config.metadata, config.logger); - this.logger.info('Initializing agent'); - this.server = new Server(config.metadata, config.server, this.logger.child({ - namespace: LOGGER_NAMESPACES.SERVER, - })); - this.codefreshAPI = new Codefresh(config.metadata, config.codefresh); - this.jobs = config.jobs; - this.queue = Queue(this._queueRunner.bind(this), config.jobs.queue.concurrency); - this.queue.drain = this._onEmptyQueue.bind(this); - await Promise.all([ - this.server.init(), - this.codefreshAPI.init(), - this._prepareRuntimes(config), - ]); - this.logger.info('All services has been initialized'); - await this._loadJobs(); - } catch(err) { - const message = `Failed to initialize agent with error, message: ${err.message}`; - this.logger.error(message); - throw new Error(message); - } - } - - async _prepareRuntimes(config = {}) { - this.logger.info(`Reading Venona config file directory: ${config.metadata.venonaConfDir}`); - const cnf = await this._readFromVenonaConfDir(config.metadata.venonaConfDir); - this.runtimes = {}; - _.map(cnf, (runtimecnf) => { - if (this.runtimes[runtimecnf.name]) { - this.logger.warn(`Ignoring runtime "${runtimecnf.name}", already been configured, conflict with file: "${runtimecnf.file}"`); - return; - } - this.runtimes[runtimecnf.name] = { - spec: _.omit(runtimecnf, 'name'), - }; - }); - await Promise.all(_.map(this.runtimes, async (runtime, name) => { - this.logger.info(`Initializing Kubernetes client for runtime: ${name}`); - const opt = { - config: { - url: runtime.spec.host, - auth: { - bearer: runtime.spec.token - }, - ca: runtime.spec.crt - }, - }; - const client = Kubernetes.buildFromConfig(config.metadata, opt); - try { - await client.init(); - runtime.kubernetesAPI = client; - this.logger.info(`Runtime ${name} was loaded successfuly`); - } catch(err) { - this.logger.error(`Failed to initiate runtime: ${name}, error: ${err.message}`); - runtime.metadata.error = err.message; - } + constructor(config = {}) { + this.logger = Logger.create(config.metadata, config.logger); + this.logger.info('Starting agent'); + this.server = new Server(config.metadata, config.server, this.logger.child({ + namespace: LOGGER_NAMESPACES.SERVER, })); + this.codefreshAPI = new Codefresh(config.metadata, config.codefresh); + this.kubernetesAPI = config.metadata.mode === AGENT_MODES.IN_CLUSTER + ? Kubernetes.buildFromInCluster(config.metadata) + : Kubernetes.buildFromConfig(config.metadata, config.kubernetes); + this.jobs = config.jobs; + this.queue = Queue(this._queueRunner.bind(this), config.jobs.queue.concurrency); + this.queue.drain = this._onEmptyQueue.bind(this); } _onEmptyQueue() { @@ -92,6 +44,7 @@ class Agent { async _loadJobs() { const ignorePaths = [(file, stats) => { + return !(new RegExp(/.*job.js/g).test(file)) && !stats.isDirectory(); }]; return Promise @@ -100,32 +53,25 @@ class Agent { .map(Job => this._startJob(Job)); } - async _readFromVenonaConfDir(dir) { - const runtimeRegexp = /.*\.runtime\.yaml/; - const data = await Promise.fromCallback(cb => fs.readdir(dir, cb)); - const runtimes = await Promise.all(_.map(data, async(f) => { - const fullPath = path.join(dir, f); - const stat = await Promise.fromCallback(cb => fs.stat(fullPath, cb)); - if (stat.isDirectory()) { - this.logger.warn(`Directory "${f}" ignored, Venona loading only files that are mached to regexp ${runtimeRegexp.toString()}`); - return Promise.resolve(); - } - if (runtimeRegexp.test(f)) { - await Promise.fromCallback(cb => fs.access(fullPath, cb)); - const venonaConf = await Promise.fromCallback(cb => fs.readFile(fullPath, cb)); - return { - ...yaml.safeLoad(venonaConf.toString()), - file: fullPath, - }; - } else { - this.logger.warn(`File "${f}" ignored, Venona loading only files that are mached to regexp ${runtimeRegexp.toString()}`); - return Promise.resolve(); - } - })); - return _.compact(runtimes); - } + async init() { + try { + this.logger.info('Initializing agent'); + await Promise.all([ + this.server.init(), + this.codefreshAPI.init(), + this.kubernetesAPI.init(), + ]); + this.logger.info('All services has been initialized'); + await this._loadJobs(); + } catch(err) { + const message = `Failed to initialize agent with error message: ${err.message}`; + this.logger.error(message); + throw new Error(message); + } + } + _startJob(Job) { const cron = _.get(this, `jobs.${Job.name}.cronExpression`, this.jobs.DEFAULT_CRON); this.logger.info(`Preparing job: ${Job.name} with cron: ${cron}`); @@ -135,7 +81,7 @@ class Agent { job: Job.name, uid: new Chance().guid(), }); - const job = new Job(this.codefreshAPI, this.runtimes, taskLogger); + const job = new Job(this.codefreshAPI, this.kubernetesAPI, taskLogger); this.logger.info(`Pushing job: ${Job.name} to queue`); this.queue.push(job, 1, this._handleJobError(job)); }); diff --git a/config/index.js b/config/index.js index 14382980..df518707 100644 --- a/config/index.js +++ b/config/index.js @@ -6,8 +6,7 @@ function build() { return { metadata: { version, - id: process.env.AGENT_ID, - venonaConfDir: process.env.VENONA_CONFIG_DIR, + mode: process.env.AGENT_MODE, }, logger: { ...(!process.env.LOGGER_MODE && { @@ -21,9 +20,16 @@ function build() { port: process.env.PORT || '9000', }, kubernetes: { + config: { + url: process.env.KUBERNETES_HOST, + auth: { + bearer: process.env.KUBERNETES_AUTH_BEARER_TOKEN, + }, + ca: process.env.KUBERNETES_CA_CERT, + }, metadata: { name: process.env.SELF_POD_NAME, - namespace: process.env.SELF_POD_NAMESPACE + namepsace: process.env.SELF_POD_NAMESPACE, } }, codefresh: { diff --git a/constants.js b/constants.js index 89879d46..8feb7b05 100644 --- a/constants.js +++ b/constants.js @@ -1,4 +1,7 @@ - +const AGENT_MODES = { + // The agent will run inside a cluster as a pod + IN_CLUSTER: 'InCluster', +}; const TASK_PRIORITY = { HIGH: 1, @@ -22,6 +25,7 @@ const CRON = { }; module.exports = { + AGENT_MODES, LOGGER_NAMESPACES, LOGGER_MODES, CRON, diff --git a/index.js b/index.js index 5b38a7c8..0906bdbe 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,11 @@ const Agent = require('./agent'); const buildConfig = require('./config'); -const agent = new Agent(); +const agent = new Agent(buildConfig()); (async () => { try { - await agent.init(buildConfig()); + await agent.init(); } catch (err) { setTimeout(() => { process.exit(1); diff --git a/jobs/BaseJob.js b/jobs/BaseJob.js index c34019a9..2dd907f1 100644 --- a/jobs/BaseJob.js +++ b/jobs/BaseJob.js @@ -1,9 +1,7 @@ -const _ = require('lodash'); - class Job { - constructor(codefreshAPI, runtimes, logger) { + constructor(codefreshAPI, kubernetesAPI, logger) { this.codefreshAPI = codefreshAPI; - this.runtimes = runtimes; + this.kubernetesAPI = kubernetesAPI; this.logger = logger; } @@ -19,17 +17,6 @@ class Job { async validate() { throw new Error('not implemented'); } - - async getKubernetesService(runtime) { - if (!_.has(this, `runtimes[${runtime}]`)) { - throw new Error(`Kubernetes client for runtime "${runtime}" was not found`); - } - if (!_.has(this, `runtimes[${runtime}].kubernetesAPI`)) { - const err = _.get(this, `runtimes[${runtime}].metadata.error`, ''); - throw new Error(`Kubernetes client for runtime "${runtime}" was not created due to error: ${err}`); - } - return this.runtimes[runtime].kubernetesAPI; - } } module.exports = Job; diff --git a/jobs/TaskPullerJob/TaskPuller.job.js b/jobs/TaskPullerJob/TaskPuller.job.js index 01e04d03..0ab14893 100644 --- a/jobs/TaskPullerJob/TaskPuller.job.js +++ b/jobs/TaskPullerJob/TaskPuller.job.js @@ -57,7 +57,7 @@ class TaskPullerJob extends Base { task: Task.name, taskUid: new Chance().guid() }); - const task = new Task(this.codefreshAPI, this.runtimes, logger); + const task = new Task(this.codefreshAPI, this.kubernetesAPI, logger); return task.exec(taskSpec); }; } diff --git a/jobs/TaskPullerJob/tasks/CreatePod.task.js b/jobs/TaskPullerJob/tasks/CreatePod.task.js index ace48ffa..50425ff7 100644 --- a/jobs/TaskPullerJob/tasks/CreatePod.task.js +++ b/jobs/TaskPullerJob/tasks/CreatePod.task.js @@ -1,6 +1,5 @@ const Joi = require('joi'); const Base = require('../../BaseJob'); -const _ = require('lodash'); const { TASK_PRIORITY } = require('../../../constants'); const ERROR_MESSAGES = { @@ -11,8 +10,7 @@ class CreatePodTask extends Base { async run(task) { this.logger.info('Running CreatePod task'); try { - const service = await this.getKubernetesService(_.get(task, 'metadata.reName')); - const pod = await service.createPod(this.logger, task.spec); + const pod = await this.kubernetesAPI.createPod(this.logger, task.spec); return pod; } catch (err) { const message = `${ERROR_MESSAGES.FAILED_TO_EXECUTE_TASK}: ${err.message}`; diff --git a/jobs/TaskPullerJob/tasks/CreatePvc.task.js b/jobs/TaskPullerJob/tasks/CreatePvc.task.js index 9f08221e..a939d74c 100644 --- a/jobs/TaskPullerJob/tasks/CreatePvc.task.js +++ b/jobs/TaskPullerJob/tasks/CreatePvc.task.js @@ -1,6 +1,5 @@ const Joi = require('joi'); const Base = require('../../BaseJob'); -const _ = require('lodash'); const { TASK_PRIORITY } = require('../../../constants'); const ERROR_MESSAGES = { @@ -11,8 +10,7 @@ class CreatePvcTask extends Base { async run(task) { this.logger.info('Running CreatePvc task'); try { - const service = await this.getKubernetesService(_.get(task, 'metadata.reName')); - const pvc = await service.createPvc(this.logger, task.spec); + const pvc = await this.kubernetesAPI.createPvc(this.logger, task.spec); return pvc; } catch (err) { const message = `${ERROR_MESSAGES.FAILED_TO_EXECUTE_TASK}: ${err.message}`; diff --git a/jobs/TaskPullerJob/tasks/DeletePod.task.js b/jobs/TaskPullerJob/tasks/DeletePod.task.js index 386e1355..41270f92 100644 --- a/jobs/TaskPullerJob/tasks/DeletePod.task.js +++ b/jobs/TaskPullerJob/tasks/DeletePod.task.js @@ -11,8 +11,7 @@ class DeletePodTask extends Base { async run(task) { this.logger.info('Running DeletePod task'); try { - const service = await this.getKubernetesService(_.get(task, 'metadata.reName')); - await service.deletePod(this.logger, task.spec.namespace, task.spec.name); + await this.kubernetesAPI.deletePod(this.logger, task.spec.namespace, task.spec.name); } catch (err) { // we treat 404 as if the operation succeeded if (_.get(err, 'code') !== 404) { diff --git a/jobs/TaskPullerJob/tasks/DeletePvc.task.js b/jobs/TaskPullerJob/tasks/DeletePvc.task.js index c5f3b9ad..d45c8f77 100644 --- a/jobs/TaskPullerJob/tasks/DeletePvc.task.js +++ b/jobs/TaskPullerJob/tasks/DeletePvc.task.js @@ -11,8 +11,7 @@ class DeletePvcTask extends Base { async run(task) { this.logger.info('Running DeletePvc task'); try { - const service = await this.getKubernetesService(_.get(task, 'metadata.reName')); - await service.deletePvc(this.logger, task.spec.namespace, task.spec.name); + await this.kubernetesAPI.deletePvc(this.logger, task.spec.namespace, task.spec.name); } catch (err) { // we treat 404 as if the operation succeeded if (_.get(err, 'code') !== 404) { diff --git a/jobs/TaskPullerJob/tasks/__tests__/CreatePod.task.spec.js b/jobs/TaskPullerJob/tasks/__tests__/CreatePod.task.spec.js index 6ba43464..33543bd2 100644 --- a/jobs/TaskPullerJob/tasks/__tests__/CreatePod.task.spec.js +++ b/jobs/TaskPullerJob/tasks/__tests__/CreatePod.task.spec.js @@ -14,16 +14,8 @@ describe('CreatePod task unit tests', () => { const kubernetesAPIMock = { createPod: jest.fn().mockRejectedValue(new Error('Error!!!')), }; - const taskDef = { - metadata: { - reName: 'runtime' - } - }; - const task = new CreatePodTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const taskDef = {}; + const task = new CreatePodTask(_.noop(), kubernetesAPIMock, logger); return expect(task.run(taskDef)).rejects.toThrowError('Failed to run task CreatePod: Error!!!'); }); }); @@ -37,16 +29,9 @@ describe('CreatePod task unit tests', () => { createPod: spy, }; const taskDef = { - metadata: { - reName: 'runtime' - }, spec: {} }; - const task = new CreatePodTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new CreatePodTask(_.noop(), kubernetesAPIMock, logger); return task.run(taskDef) .then(() => { const loggerMacher = expect.objectContaining({ @@ -71,15 +56,10 @@ describe('CreatePod task unit tests', () => { createPod: spy, }; const taskDef = { - metadata: { - reName: 'runtime' - }, + runtime: {}, + dockerDaemon: {}, }; - const task = new CreatePodTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new CreatePodTask(_.noop(), kubernetesAPIMock, logger); return expect(task.run(taskDef)).resolves.toEqual(spyResult); }); diff --git a/jobs/TaskPullerJob/tasks/__tests__/CreatePvc.task.spec.js b/jobs/TaskPullerJob/tasks/__tests__/CreatePvc.task.spec.js index 186f5cde..22e5e3f1 100644 --- a/jobs/TaskPullerJob/tasks/__tests__/CreatePvc.task.spec.js +++ b/jobs/TaskPullerJob/tasks/__tests__/CreatePvc.task.spec.js @@ -14,16 +14,8 @@ describe('CreatePvc task unit tests', () => { const kubernetesAPIMock = { createPvc: jest.fn().mockRejectedValue(new Error('Error!!!')), }; - const taskDef = { - metadata: { - reName: 'runtime' - } - }; - const task = new CreatePvcTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const taskDef = {}; + const task = new CreatePvcTask(_.noop(), kubernetesAPIMock, logger); return expect(task.run(taskDef)).rejects.toThrowError('Failed to run task CreatePvc: Error!!!'); }); @@ -31,16 +23,8 @@ describe('CreatePvc task unit tests', () => { it('should throw error in case the task in not valid', () => { const logger = createLogger(); const kubernetesAPIMock = {}; - const taskDef = { - metadata: { - reName: 'runtime' - } - }; - const task = new CreatePvcTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const taskDef = {}; + const task = new CreatePvcTask(_.noop(), kubernetesAPIMock, logger); return expect(task.validate(taskDef)).rejects.toThrowError('child "spec" fails because ["spec" is required]'); }); }); @@ -55,16 +39,9 @@ describe('CreatePvc task unit tests', () => { createPvc: spy, }; const taskDef = { - metadata: { - reName: 'runtime' - }, spec: {} }; - const task = new CreatePvcTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new CreatePvcTask(_.noop(), kubernetesAPIMock, logger); return task.run(taskDef) .then(() => { const loggerMacher = expect.objectContaining({ @@ -89,15 +66,10 @@ describe('CreatePvc task unit tests', () => { createPvc: spy, }; const taskDef = { - metadata: { - reName: 'runtime' - } + runtime: {}, + dockerDaemon: {}, }; - const task = new CreatePvcTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new CreatePvcTask(_.noop(), kubernetesAPIMock, logger); return expect(task.run(taskDef)).resolves.toEqual(spyResult); }); diff --git a/jobs/TaskPullerJob/tasks/__tests__/DeletePod.task.spec.js b/jobs/TaskPullerJob/tasks/__tests__/DeletePod.task.spec.js index 875c956b..f51ff62c 100644 --- a/jobs/TaskPullerJob/tasks/__tests__/DeletePod.task.spec.js +++ b/jobs/TaskPullerJob/tasks/__tests__/DeletePod.task.spec.js @@ -7,9 +7,6 @@ jest.mock('./../../../../services/Logger'); const getValidTaskDef = () => { return { - metadata: { - reName: 'runtime' - }, spec: { namespace: 'namespace', name: 'docker-daemon-name' @@ -27,11 +24,7 @@ describe('DeletePod task unit tests', () => { deletePod: jest.fn().mockRejectedValue(new Error('Error!!!')), }; - const task = new DeletePodTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new DeletePodTask(_.noop(), kubernetesAPIMock, logger); return expect(task.exec(getValidTaskDef())).rejects.toThrowError('Failed to run task DeletePod: Error!!!'); }); @@ -45,11 +38,7 @@ describe('DeletePod task unit tests', () => { const taskDef = getValidTaskDef(); delete taskDef.spec; - const task = new DeletePodTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new DeletePodTask(_.noop(), kubernetesAPIMock, logger); return expect(task.exec(taskDef)).rejects.toThrowError('child "spec" fails because ["spec" is required]'); }); @@ -62,11 +51,7 @@ describe('DeletePod task unit tests', () => { const taskDef = getValidTaskDef(); delete taskDef.spec.namespace; - const task = new DeletePodTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new DeletePodTask(_.noop(), kubernetesAPIMock, logger); return expect(task.exec(taskDef)).rejects.toThrowError('child "spec" fails because [child "namespace" fails because ["namespace" is required]]'); }); @@ -79,11 +64,7 @@ describe('DeletePod task unit tests', () => { const taskDef = getValidTaskDef(); delete taskDef.spec.name; - const task = new DeletePodTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new DeletePodTask(_.noop(), kubernetesAPIMock, logger); return expect(task.exec(taskDef)).rejects.toThrowError('child "spec" fails because [child "name" fails because ["name" is required]]'); }); }); @@ -98,11 +79,7 @@ describe('DeletePod task unit tests', () => { }; const taskDef = getValidTaskDef(); - const task = new DeletePodTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new DeletePodTask(_.noop(), kubernetesAPIMock, logger); return task.exec(taskDef) .then(() => { const loggerMacher = expect.objectContaining({ @@ -122,11 +99,7 @@ describe('DeletePod task unit tests', () => { deletePod: spy, }; const taskDef = getValidTaskDef(); - const task = new DeletePodTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new DeletePodTask(_.noop(), kubernetesAPIMock, logger); return expect(task.exec(taskDef)).resolves.toEqual('OK'); }); @@ -139,11 +112,7 @@ describe('DeletePod task unit tests', () => { }; const taskDef = getValidTaskDef(); - const task = new DeletePodTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new DeletePodTask(_.noop(), kubernetesAPIMock, logger); return expect(task.exec(taskDef)).resolves.toEqual('OK'); }); diff --git a/jobs/TaskPullerJob/tasks/__tests__/DeletePvc.task.spec.js b/jobs/TaskPullerJob/tasks/__tests__/DeletePvc.task.spec.js index d882bdcd..75029909 100644 --- a/jobs/TaskPullerJob/tasks/__tests__/DeletePvc.task.spec.js +++ b/jobs/TaskPullerJob/tasks/__tests__/DeletePvc.task.spec.js @@ -7,9 +7,6 @@ jest.mock('./../../../../services/Logger'); const getValidTaskDef = () => { return { - metadata: { - reName: 'runtime' - }, spec: { namespace: 'namespace', name: 'docker-daemon-name' @@ -27,11 +24,7 @@ describe('DeletePvc task unit tests', () => { deletePvc: jest.fn().mockRejectedValue(new Error('Error!!!')), }; - const task = new DeletePvcTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new DeletePvcTask(_.noop(), kubernetesAPIMock, logger); return expect(task.exec(getValidTaskDef())).rejects.toThrowError('Failed to run task DeletePvc: Error!!!'); }); @@ -45,11 +38,7 @@ describe('DeletePvc task unit tests', () => { const taskDef = getValidTaskDef(); delete taskDef.spec; - const task = new DeletePvcTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new DeletePvcTask(_.noop(), kubernetesAPIMock, logger); return expect(task.exec(taskDef)).rejects.toThrowError('child "spec" fails because ["spec" is required]'); }); @@ -62,11 +51,7 @@ describe('DeletePvc task unit tests', () => { const taskDef = getValidTaskDef(); delete taskDef.spec.namespace; - const task = new DeletePvcTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new DeletePvcTask(_.noop(), kubernetesAPIMock, logger); return expect(task.exec(taskDef)).rejects.toThrowError('child "spec" fails because [child "namespace" fails because ["namespace" is required]]'); }); @@ -79,11 +64,7 @@ describe('DeletePvc task unit tests', () => { const taskDef = getValidTaskDef(); delete taskDef.spec.name; - const task = new DeletePvcTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new DeletePvcTask(_.noop(), kubernetesAPIMock, logger); return expect(task.exec(taskDef)).rejects.toThrowError('child "spec" fails because [child "name" fails because ["name" is required]]'); }); }); @@ -98,11 +79,7 @@ describe('DeletePvc task unit tests', () => { }; const taskDef = getValidTaskDef(); - const task = new DeletePvcTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new DeletePvcTask(_.noop(), kubernetesAPIMock, logger); return task.exec(taskDef) .then(() => { const loggerMacher = expect.objectContaining({ @@ -122,11 +99,7 @@ describe('DeletePvc task unit tests', () => { deletePvc: spy, }; const taskDef = getValidTaskDef(); - const task = new DeletePvcTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new DeletePvcTask(_.noop(), kubernetesAPIMock, logger); return expect(task.exec(taskDef)).resolves.toEqual('OK'); }); @@ -139,11 +112,7 @@ describe('DeletePvc task unit tests', () => { }; const taskDef = getValidTaskDef(); - const task = new DeletePvcTask(_.noop(), { - 'runtime': { - kubernetesAPI: kubernetesAPIMock, - }, - }, logger); + const task = new DeletePvcTask(_.noop(), kubernetesAPIMock, logger); return expect(task.exec(taskDef)).resolves.toEqual('OK'); }); diff --git a/jobs/__tests__/BaseJob.spec.js b/jobs/__tests__/BaseJob.spec.js index ac44113c..21b5b56e 100644 --- a/jobs/__tests__/BaseJob.spec.js +++ b/jobs/__tests__/BaseJob.spec.js @@ -1,43 +1,20 @@ const _ = require('lodash'); -const Job = require('./../BaseJob'); +const BaseJob = require('./../BaseJob'); const { create: createLogger } = require('../../services/Logger'); jest.mock('./../../services/Logger'); describe('BaseJob unit tests', () => { - it('Should construct', () => { - const job = new Job(_.noop(), _.noop(), createLogger()); - expect(Object.keys(job).sort()).toEqual(['codefreshAPI', 'runtimes', 'logger'].sort()); + describe('positive', () => { + it('Should construct', () => { + const task = new BaseJob(_.noop(), _.noop(), createLogger()); + expect(Object.keys(task).sort()).toEqual(['codefreshAPI', 'kubernetesAPI', 'logger'].sort()); + }); }); - it('Should return the requested runtime', async () => { - const kubernetesAPI = {}; - const runtimes = { - 'runtime': { - name: 'runtime', - kubernetesAPI, - }, - }; - const job = new Job(_.noop(), runtimes, createLogger()); - await expect(job.getKubernetesService('runtime')).resolves.toEqual(kubernetesAPI); - }); + describe('negative', () => { - it('Should throw an error in case the runtime was not found', async () => { - const runtimes = {}; - const job = new Job(_.noop(), runtimes, createLogger()); - await expect(job.getKubernetesService('runtime')).rejects.toThrowError('Kubernetes client for runtime "runtime" was not found'); }); - it('Should throw an error in case the runtime\'s KubernetesAPI was not initialized', async () => { - const runtimes = { - 'runtime': { - metadata: { - error: 'failed to initialize runtime' - } - } - }; - const job = new Job(_.noop(), runtimes, createLogger()); - await expect(job.getKubernetesService('runtime')).rejects.toThrowError('Kubernetes client for runtime "runtime" was not created due to error: failed to initialize runtime'); - }); }); diff --git a/package.json b/package.json index cf32b062..84be3f9d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "venona", - "version": "1.0.2", + "version": "0.30.3", "description": "Codefresh agent to run on Codefresh's runtime environment and execute pipeline", "main": "index.js", "scripts": { @@ -16,9 +16,8 @@ "chance": "^1.0.16", "express": "^4.16.4", "joi": "^14.3.1", - "js-yaml": "^3.13.1", "kubernetes-client": "6.4.0", - "lodash": "^4.17.15", + "lodash": "^4.17.11", "node-schedule": "^1.3.0", "pino": "^5.9.0", "pino-pretty": "^2.2.4", diff --git a/scripts/migration.sh b/scripts/migration.sh deleted file mode 100755 index c32354c3..00000000 --- a/scripts/migration.sh +++ /dev/null @@ -1,90 +0,0 @@ -# This script comes to migrate old versions of Venona installation ( version < 1.x.x ) to new version (version >= 1.0.0 ) -# Please read carefully what the script does. -# There will be a "downtime" in terms of your builds targeted to this runtime environment -# Once the script is finished, all the builds during the downtime will start -# The script will: -# 1. Create new agent entity in Codefresh using Codefresh CLI - give it a name $CODEFRESH_AGENT_NAME, default is "codefresh" -# 2. Install the agent on you cluster pass variables: -# a. $VENONA_KUBE_NAMESPACE - required -# b. $VENONA_KUBE_CONTEXT - default is current-context -# c. $VENONA_KUBECONFIG_PATH - default is $HOME/.kube/config -# 3. Attach runtime to the new agent (downtime ends) - pass $CODEFRESH_RUNTIME_NAME - required - -set -e - -echoAndRun() { - info "Running command: $1" - eval $1 -} - -info() { echo "INFO [$(date)] ---> $1"; } -fatal() { echo "ERROR [$(date)] ---> $1" ; exit 1; } - - -DEFAULT_KUBECONFIG="$HOME/.kube/config" - - -if [ -z "$CODEFRESH_AGENT_NAME" ] -then - info "CODEFRESH_AGENT_NAME is not set, using default name: codefresh" - CODEFRESH_AGENT_NAME="codefresh" -else - info "CODEFRESH_AGENT_NAME is set to $CODEFRESH_AGENT_NAME" -fi - -if [ -z "$CODEFRESH_RUNTIME_NAME" ] -then - fatal "CODEFRESH_RUNTIME_NAME is not set, exiting" -fi - -if [ -z "$VENONA_KUBE_NAMESPACE" ] -then - fatal "VENONA_KUBE_NAMESPACE is not set, exiting" -fi - -if [ -z "$VENONA_KUBECONFIG_PATH" ] -then - info "VENONA_KUBECONFIG_PATH is not set, using \$KUBECONFIG if exist or $DEFAULT_KUBECONFIG" - VENONA_KUBECONFIG_PATH=${KUBECONFIG:=$DEFAULT_KUBECONFIG} - info "VENONA_KUBECONFIG_PATH=$VENONA_KUBECONFIG_PATH" -else - info "VENONA_KUBECONFIG_PATH is set to $VENONA_KUBECONFIG_PATH" -fi -if [ -z "$VENONA_KUBE_CONTEXT" ] -then - info "VENONA_KUBE_CONTEXT is not set, using current-context" - VENONA_KUBE_CONTEXT=$(kubectl --kubeconfig $VENONA_KUBECONFIG_PATH config current-context) - info "VENONA_KUBE_CONTEXT=$VENONA_KUBE_CONTEXT" -else - info "VENONA_KUBE_CONTEXT is set to $VENONA_KUBE_CONTEXT" -fi - - - -kube="kubectl --kubeconfig $VENONA_KUBECONFIG_PATH --cluster $VENONA_KUBE_CONTEXT -n $VENONA_KUBE_NAMESPACE" - -info "Testing connection to runtime cluster" -runtimeTestCmd="$kube get deploy venona" -echoAndRun "$runtimeTestCmd" - -info "Crating new agent in Codefresh" -token=$(echoAndRun "codefresh create agent $CODEFRESH_AGENT_NAME" | awk 'FNR==3') - -info "Deleting current Venona process" -pod=$(eval "$kube get pods -l app=venona -o go-template='{{range .items }}{{ .metadata.name }}{{end}}'") -echoAndRun "$kube delete deploy --wait=true venona" -echoAndRun "$kube wait --for=delete pod/$pod --timeout 60s" -echoAndRun "$kube delete secret venona" - -info "Installing agent on namespace using token $token" -echoAndRun "codefresh install agent --token $token --kube-namespace $VENONA_KUBE_NAMESPACE --kube-context-name $VENONA_KUBE_CONTEXT --kube-config-path $VENONA_KUBECONFIG_PATH --verbose" - -info "Attaching old runtime to new agent" -pod=$(eval "$kube get pods -l app=venona -o go-template='{{range .items }}{{ .metadata.name }}{{end}}'") -echoAndRun "codefresh attach runtime --runtime-name $CODEFRESH_RUNTIME_NAME --agent-name $CODEFRESH_AGENT_NAME --runtime-kube-context-name $VENONA_KUBE_CONTEXT --agent-kube-context-name $VENONA_KUBE_CONTEXT --runtime-kube-namespace $VENONA_KUBE_NAMESPACE --agent-kube-namespace $VENONA_KUBE_NAMESPACE --agent-kube-config-path $VENONA_KUBECONFIG_PATH --runtime-kube-config-path $VENONA_KUBECONFIG_PATH --restart-agent --verbose" -echoAndRun "$kube wait --for=delete pod/$pod --timeout 60s" - -pod=$(eval "$kube get pods -l app=venona -o go-template='{{range .items }}{{ .metadata.name }}{{end}}'") -echoAndRun "$kube wait --for=condition=Ready pod/$pod --timeout 60s" - -info "Migration finished" diff --git a/services/Codefresh.js b/services/Codefresh.js index d73b1141..ab3baba4 100644 --- a/services/Codefresh.js +++ b/services/Codefresh.js @@ -11,7 +11,6 @@ class Codefresh { constructor(metadata, options) { this.options = options; this.metadata = metadata; - this.agentId = this.metadata.id; this.defaults = { baseUrl: utils.getPropertyOrError(options, 'baseURL', ERROR_MESSAGES.MISSING_BASE_URL), headers: { @@ -34,7 +33,7 @@ class Codefresh { pullTasks(logger) { logger.info('Calling Codefresh API to fetch jobs'); - const url = `/api/agent/${this.agentId}/tasks`; + const url = '/api/agent/tasks'; return this._call({ url, method: 'GET', @@ -43,7 +42,7 @@ class Codefresh { reportStatus(logger, status) { logger.info({ status }, 'Calling Codefresh API to report status'); - const url = `/api/agent/${this.agentId}/status`; + const url = '/api/agent/status'; return this._call({ url, method: 'PUT', diff --git a/services/Kubernetes.js b/services/Kubernetes.js index ea31ebb1..a1e56a2a 100644 --- a/services/Kubernetes.js +++ b/services/Kubernetes.js @@ -1,13 +1,10 @@ -const { Client } = require('kubernetes-client'); +const { Client, config } = require('kubernetes-client'); const utils = require('./../utils'); const ERROR_MESSAGES = { MISSING_KUBERNETES_URL: 'Failed to construct Kubernetes API service, missing Kubernetes URL', MISSING_KUBERNETES_BEARER_TOKEN: 'Failed to construct Kubernetes API service, missing Kubernetes bearer token', MISSING_KUBERNETES_CA_CERTIFICATE: 'Failed to construct Kubernetes API service, missing Kubernetes ca certificate', - MISSING_NAMESPACE: 'Failed to get Kubernetes namespace', - MISSING_VENONA_CONF: 'Failed to read venona configuration', - FAILED_TO_INIT: 'Failed to complete Kubernetes service initialization', FAILED_TO_CREATE_POD: 'Failed to create Kubernetes pod', FAILED_TO_DELETE_POD: 'Failed to delete Kubernetes pod', @@ -15,14 +12,18 @@ const ERROR_MESSAGES = { FAILED_TO_DELETE_PVC: 'Failed to delete Kubernetes pvc', }; + class Kubernetes { - // Do not use this constructor, use Kubernetes.buildFromConfig - // to create new instance constructor(metadata, client) { this.metadata = metadata; this.client = client; } + static buildFromInCluster(metadata) { + const client = new Client({ config: config.getInCluster() }); + return new this(metadata, client); + } + static buildFromConfig(metadata, options) { const url = utils.getPropertyOrError(options, 'config.url', ERROR_MESSAGES.MISSING_KUBERNETES_URL); const bearer = utils.getPropertyOrError(options, 'config.auth.bearer', ERROR_MESSAGES.MISSING_KUBERNETES_BEARER_TOKEN); @@ -31,9 +32,9 @@ class Kubernetes { config: { url, auth: { - bearer + bearer: Buffer.from(bearer, 'base64'), }, - ca + ca: Buffer.from(ca, 'base64'), }, }); return new this(metadata, client); diff --git a/services/__mocks__/Logger.js b/services/__mocks__/Logger.js index 527d1de4..a01c84d1 100644 --- a/services/__mocks__/Logger.js +++ b/services/__mocks__/Logger.js @@ -2,7 +2,6 @@ const create = jest.fn(() => ({ info: jest.fn(), - warn: jest.fn(), child: jest.fn(create), error: jest.fn(), })); diff --git a/services/__tests__/Codefresh.spec.js b/services/__tests__/Codefresh.spec.js index 4d2939a3..6108e832 100644 --- a/services/__tests__/Codefresh.spec.js +++ b/services/__tests__/Codefresh.spec.js @@ -5,7 +5,7 @@ const { create: createLogger } = require('./../../services/Logger'); jest.mock('request-promise'); jest.mock('./../../services/Logger'); -const getFakeMetadata = () => ({ name: 'unit-test', id: 'fake-agent-id' }); +const getFakeMetadata = () => ({ name: 'unit-test' }); const getFakeConfig = () => ({ baseURL: 'fake-url', token: 'fake-token' }); @@ -19,7 +19,7 @@ describe('Codefresh API unit tests', () => { it('Should set values on this', () => { const api = createCodefreshAPI(); - expect(Object.keys(api)).toStrictEqual(['options', 'metadata', 'agentId', 'defaults']); + expect(Object.keys(api)).toStrictEqual(['options', 'metadata', 'defaults']); }); }); @@ -94,7 +94,7 @@ describe('Codefresh API unit tests', () => { return createCodefreshAPI() .pullTasks(createLogger(getFakeMetadata())) .then(() => { - expect(spy.mock.calls[0][0]).toHaveProperty('url', '/api/agent/fake-agent-id/tasks'); + expect(spy.mock.calls[0][0]).toHaveProperty('url', '/api/agent/tasks'); }); }); }); @@ -110,7 +110,7 @@ describe('Codefresh API unit tests', () => { }, }) .then(() => { - expect(spy.mock.calls[0][0]).toHaveProperty('url', '/api/agent/fake-agent-id/status'); + expect(spy.mock.calls[0][0]).toHaveProperty('url', '/api/agent/status'); expect(spy.mock.calls[0][0]).toHaveProperty('method', 'PUT'); expect(spy.mock.calls[0][0]).toHaveProperty('body'); }); diff --git a/services/__tests__/Kubernetes.spec.js b/services/__tests__/Kubernetes.spec.js index 195456f4..4596ad4c 100644 --- a/services/__tests__/Kubernetes.spec.js +++ b/services/__tests__/Kubernetes.spec.js @@ -33,7 +33,14 @@ describe('Kubernetes API unit tests', () => { expect(paramsToConstructor).toHaveProperty('config.url'); }); - it('Should throw error when url is not given', () => expect(() => Kubernetes.buildFromConfig(getFakeMetadata(), { config: {} })).toThrow('Failed to construct Kubernetes API service, missing Kubernetes URL')); + it('Should construct form in-cluster', () => { + Kubernetes.buildFromInCluster(); + const callToClientConstructor = kube.Client.prototype.constructor.mock.calls; + expect(callToClientConstructor).toHaveLength(1); + expect(callToClientConstructor[0][0]).toHaveProperty('config'); + }); + + it('Should throw error when url is not given', () => expect(() => Kubernetes.buildFromConfig(getFakeMetadata(), { config: {} })).toThrow('Failed to construct Kubernetes API service, ')); it('Should throw error when bearer token is not given', () => expect(() => Kubernetes.buildFromConfig(getFakeMetadata(), { config: { url: 'ok' } })).toThrow('Failed to construct Kubernetes API service, missing Kubernetes bearer token')); diff --git a/venonactl/VERSION b/venonactl/VERSION index 6d7de6e6..e8262eb5 100644 --- a/venonactl/VERSION +++ b/venonactl/VERSION @@ -1 +1 @@ -1.0.2 +0.30.3 diff --git a/venonactl/cmd/attach.go b/venonactl/cmd/attach.go deleted file mode 100644 index 6ad912bb..00000000 --- a/venonactl/cmd/attach.go +++ /dev/null @@ -1,125 +0,0 @@ -package cmd - -/* -Copyright 2019 The Codefresh Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import ( - "github.com/codefresh-io/venona/venonactl/pkg/plugins" - "github.com/codefresh-io/venona/venonactl/pkg/store" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -var attachRuntimeCmdOptions struct { - runtimeEnvironmentName string - kube struct { - namespace string - inCluster bool - context string - kubePath string - serviceAccount string - } - kubeVenona struct { - namespace string - kubePath string - context string - } - restartAgent bool -} - -var attachRuntimeCmd = &cobra.Command{ - Use: "attach", - Short: "Attach Codefresh runtime to agent", - Run: func(cmd *cobra.Command, args []string) { - - s := store.GetStore() - lgr := createLogger("Attach-runtime", verbose) - buildBasicStore(lgr) - extendStoreWithKubeClient(lgr) - - s.CodefreshAPI = &store.CodefreshAPI{} - s.AgentAPI = &store.AgentAPI{} - - if attachRuntimeCmdOptions.kubeVenona.kubePath == "" { - attachRuntimeCmdOptions.kubeVenona.kubePath = kubeConfigPath - } - if attachRuntimeCmdOptions.kubeVenona.namespace == "" { - attachRuntimeCmdOptions.kubeVenona.namespace = attachRuntimeCmdOptions.kube.namespace - } - if attachRuntimeCmdOptions.kubeVenona.context == "" { - attachRuntimeCmdOptions.kubeVenona.context = attachRuntimeCmdOptions.kube.context - } - - attachRuntimeCmdOptions.kube.serviceAccount = "venona" - - if attachRuntimeCmdOptions.kube.kubePath == "" { - attachRuntimeCmdOptions.kube.kubePath = kubeConfigPath - } - - fillKubernetesAPI(lgr, attachRuntimeCmdOptions.kubeVenona.context, attachRuntimeCmdOptions.kubeVenona.namespace, false) - - builder := plugins.NewBuilder(lgr) - - builderInstallOpt := &plugins.InstallOptions{ - ClusterNamespace: attachRuntimeCmdOptions.kubeVenona.namespace, - RuntimeEnvironment: attachRuntimeCmdOptions.runtimeEnvironmentName, - RuntimeClusterName: attachRuntimeCmdOptions.kube.namespace, - RuntimeServiceAccount: attachRuntimeCmdOptions.kube.serviceAccount, - RestartAgent: attachRuntimeCmdOptions.restartAgent, - } - - // runtime - builderInstallOpt.KubeBuilder = getKubeClientBuilder(attachRuntimeCmdOptions.kube.context, attachRuntimeCmdOptions.kube.namespace, attachRuntimeCmdOptions.kube.kubePath, false) - - // agent - builderInstallOpt.AgentKubeBuilder = getKubeClientBuilder(attachRuntimeCmdOptions.kubeVenona.context, - attachRuntimeCmdOptions.kubeVenona.namespace, - attachRuntimeCmdOptions.kubeVenona.kubePath, - false) - - builder.Add(plugins.RuntimeAttachType) - - var err error - values := s.BuildValues() - for _, p := range builder.Get() { - values, err = p.Install(builderInstallOpt, values) - if err != nil { - dieOnError(err) - } - } - lgr.Info("Attach to runtime completed Successfully") - - }, -} - -func init() { - rootCmd.AddCommand(attachRuntimeCmd) - viper.BindEnv("kube-namespace", "KUBE_NAMESPACE") - viper.BindEnv("kube-context", "KUBE_CONTEXT") - - attachRuntimeCmd.Flags().StringVar(&attachRuntimeCmdOptions.kube.namespace, "kube-namespace", viper.GetString("kube-namespace"), "Name of the namespace on which venona should be installed [$KUBE_NAMESPACE]") - attachRuntimeCmd.Flags().StringVar(&attachRuntimeCmdOptions.kube.context, "kube-context-name", viper.GetString("kube-context"), "Name of the kubernetes context on which venona should be installed (default is current-context) [$KUBE_CONTEXT]") - attachRuntimeCmd.Flags().StringVar(&attachRuntimeCmdOptions.kube.kubePath, "kube-config-path", viper.GetString("kubeconfig"), "Path to kubeconfig file (default is $HOME/.kube/config) [$KUBECONFIG]") - - attachRuntimeCmd.Flags().StringVar(&attachRuntimeCmdOptions.runtimeEnvironmentName, "runtime-name", viper.GetString("runtime-name"), "Name of the runtime as in codefresh") - - attachRuntimeCmd.Flags().StringVar(&attachRuntimeCmdOptions.kubeVenona.namespace, "kube-namespace-agent", viper.GetString("kube-namespace-agent"), "Name of the namespace where venona is installed [$KUBE_NAMESPACE]") - attachRuntimeCmd.Flags().StringVar(&attachRuntimeCmdOptions.kubeVenona.context, "kube-context-name-agent", viper.GetString("kube-context-agent"), "Name of the kubernetes context on which venona is installed (default is current-context) [$KUBE_CONTEXT]") - attachRuntimeCmd.Flags().StringVar(&attachRuntimeCmdOptions.kubeVenona.kubePath, "kube-config-path-agent", viper.GetString("kubeconfig-agent"), "Path to kubeconfig file (default is $HOME/.kube/config) for agent [$KUBECONFIG]") - attachRuntimeCmd.Flags().BoolVar(&attachRuntimeCmdOptions.restartAgent, "restart-agent", viper.GetBool("restart-agent"), "Restart agent after attach operation") - - -} diff --git a/venonactl/cmd/cmdutils.go b/venonactl/cmd/cmdutils.go index 17c441d5..03ac0e3b 100644 --- a/venonactl/cmd/cmdutils.go +++ b/venonactl/cmd/cmdutils.go @@ -1,17 +1,12 @@ package cmd import ( - "encoding/base64" - "errors" "fmt" - "io/ioutil" "os" "os/user" "path" "strings" - "encoding/json" - "github.com/codefresh-io/go-sdk/pkg/codefresh" sdkUtils "github.com/codefresh-io/go-sdk/pkg/utils" "github.com/codefresh-io/venona/venonactl/pkg/certs" @@ -20,9 +15,6 @@ import ( "github.com/codefresh-io/venona/venonactl/pkg/plugins" "github.com/codefresh-io/venona/venonactl/pkg/store" "github.com/olekukonko/tablewriter" - "gopkg.in/yaml.v2" - k8sApi "k8s.io/api/core/v1" - "k8s.io/client-go/tools/clientcmd" ) var ( @@ -187,78 +179,3 @@ func createLogger(command string, verbose bool) logger.Logger { LogToFile: logFile, }) } - -type nodeSelector map[string]string - -func parseNodeSelector(s string) (nodeSelector, error) { - if s == "" { - return nodeSelector{}, nil - } - v := strings.Split(s, "=") - if len(v) != 2 { - return nil, errors.New("node selector must be in form \"key=value\"") - } - return nodeSelector{v[0]: v[1]}, nil -} - -func loadTolerationsFromFile(filename string) string { - data, err := ioutil.ReadFile(filename) - if err != nil { - dieOnError(err) - } - - return string(data) -} - -func parseTolerations(s string) (string, error) { - if s == "" { - return "", nil - } - var data []k8sApi.Toleration - err := json.Unmarshal([]byte(s), &data) - if err != nil { - return "", fmt.Errorf("can not parse tolerations: %s", err) - } - y, err := yaml.Marshal(&data) - if err != nil { - return "", fmt.Errorf("can not marshel tolerations to yaml: %s", err) - } - d := fmt.Sprintf("\n%s", string(y)) - return d, nil -} - -func fillKubernetesAPI(lgr logger.Logger, context string, namespace string, inCluster bool) { - - s := store.GetStore() - if context == "" { - config := clientcmd.GetConfigFromFileOrDie(s.KubernetesAPI.ConfigPath) - context = config.CurrentContext - lgr.Debug("Kube Context is not set, using current context", "Kube-Context-Name", context) - } - if namespace == "" { - namespace = "default" - } - - s.KubernetesAPI.InCluster = inCluster - s.KubernetesAPI.ContextName = context - s.KubernetesAPI.Namespace = namespace - -} - -func extendStoreWithAgentAPI(logger logger.Logger, token string, agentID string) { - s := store.GetStore() - logger.Debug("Using agent's token", "Token", token) - s.AgentAPI = &store.AgentAPI{ - Token: base64.StdEncoding.EncodeToString([]byte(token)), - Id: agentID, - } -} - -// String returns a k8s compliant string representation of the nodeSelector. Only a single value is supported. -func (ns nodeSelector) String() string { - var s string - for k, v := range ns { - s = fmt.Sprintf("%s: %s", k, v) - } - return s -} diff --git a/venonactl/cmd/delete.go b/venonactl/cmd/delete.go new file mode 100644 index 00000000..3a708087 --- /dev/null +++ b/venonactl/cmd/delete.go @@ -0,0 +1,129 @@ +package cmd + +/* +Copyright 2019 The Codefresh Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import ( + "errors" + "fmt" + "os" + + "github.com/codefresh-io/venona/venonactl/pkg/store" + + "github.com/codefresh-io/venona/venonactl/pkg/plugins" + "github.com/spf13/cobra" +) + +type DeletionError struct { + err error + operation string + name string +} + +var deleteCmdOptions struct { + kube struct { + inCluster bool + context string + } + revertTo string +} + +// deleteCmd represents the status command +var deleteCmd = &cobra.Command{ + Use: "delete [names]", + Short: "Delete a Codefresh's runtime-environment", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("requires name of the runtime-environment") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + s := store.GetStore() + lgr := createLogger("Delete", verbose) + buildBasicStore(lgr) + extendStoreWithCodefershClient(lgr) + extendStoreWithKubeClient(lgr) + var errors []DeletionError + s.KubernetesAPI.InCluster = deleteCmdOptions.kube.inCluster + for _, name := range args { + builder := plugins.NewBuilder(lgr) + + re, err := s.CodefreshAPI.Client.RuntimeEnvironments().Get(name) + errors = collectError(errors, err, name) + + if deleteCmdOptions.revertTo != "" { + _, err := s.CodefreshAPI.Client.RuntimeEnvironments().Default(deleteCmdOptions.revertTo) + errors = collectError(errors, err, name) + } + deleted, err := s.CodefreshAPI.Client.RuntimeEnvironments().Delete(name) + errors = collectError(errors, err, name) + deleteOptions := &plugins.DeleteOptions{} + if deleted { + contextName := re.RuntimeScheduler.Cluster.ClusterProvider.Selector + if contextName != "" { + contextName = deleteCmdOptions.kube.context + } + s.KubernetesAPI.ContextName = contextName + s.KubernetesAPI.Namespace = re.RuntimeScheduler.Cluster.Namespace + + builder.Add(plugins.RuntimeEnvironmentPluginType) + if isUsingDefaultStorageClass(re.RuntimeScheduler.Pvcs.Dind.StorageClassName) { + builder.Add(plugins.VolumeProvisionerPluginType) + } + + if re.Metadata.Agent { + builder.Add(plugins.VenonaPluginType) + } + + deleteOptions.KubeBuilder = getKubeClientBuilder(contextName, s.KubernetesAPI.Namespace, s.KubernetesAPI.ConfigPath, s.KubernetesAPI.InCluster) + deleteOptions.ClusterNamespace = re.RuntimeScheduler.Cluster.Namespace + for _, p := range builder.Get() { + err := p.Delete(deleteOptions, s.BuildValues()) + collectError(errors, err, name) + } + + lgr.Info("Deletion completed", "Name", name) + } + + } + + if len(errors) > 0 { + for _, e := range errors { + lgr.Error(fmt.Sprintf("Error %s", e.err.Error()), "Name", e.name, "Operation", e.operation) + } + os.Exit(1) + } + lgr.Info("Deletion completed") + }, +} + +func init() { + rootCmd.AddCommand(deleteCmd) + deleteCmd.Flags().StringVar(&deleteCmdOptions.kube.context, "kube-context-name", "", "Set name to overwrite the context name saved in Codefresh") + deleteCmd.Flags().StringVar(&deleteCmdOptions.revertTo, "revert-to", "", "Set to the name of the runtime-environment to set as default") + deleteCmd.Flags().BoolVar(&deleteCmdOptions.kube.inCluster, "in-cluster", false, "Set flag if venona is been installed from inside a cluster") +} + +func collectError(errors []DeletionError, err error, reName string) []DeletionError { + if err == nil { + return errors + } + return append(errors, DeletionError{ + err: err, + name: reName, + }) +} diff --git a/venonactl/cmd/install-agent.go b/venonactl/cmd/install-agent.go deleted file mode 100644 index 0a311a28..00000000 --- a/venonactl/cmd/install-agent.go +++ /dev/null @@ -1,152 +0,0 @@ -package cmd - -/* -Copyright 2019 The Codefresh Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import ( - "fmt" - - "github.com/codefresh-io/venona/venonactl/pkg/store" - "github.com/codefresh-io/venona/venonactl/pkg/plugins" - "github.com/codefresh-io/venona/venonactl/pkg/logger" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -var installAgentCmdOptions struct { - dryRun bool - kube struct { - namespace string - inCluster bool - context string - nodeSelector string - } - venona struct { - version string - } - agentToken string - agentID string - kubernetesRunnerType bool - tolerations string -} - -var installAgentCmd = &cobra.Command{ - Use: "agent", - Short: "Install Codefresh's agent ", - Run: func(cmd *cobra.Command, args []string) { - s := store.GetStore() - lgr := createLogger("Install-agent", verbose) - buildBasicStore(lgr) - extendStoreWithAgentAPI(lgr, installAgentCmdOptions.agentToken, installAgentCmdOptions.agentID) - extendStoreWithKubeClient(lgr) - fillCodefreshAPI(lgr) - builder := plugins.NewBuilder(lgr) - - if cfAPIHost == "" { - cfAPIHost = "https://g.codefresh.io" - } - builderInstallOpt := &plugins.InstallOptions{ - CodefreshHost: cfAPIHost, - } - - if installAgentCmdOptions.agentToken == "" { - dieOnError(fmt.Errorf("Agent token is required in order to install agent")) - } - - if installAgentCmdOptions.agentID == "" { - dieOnError(fmt.Errorf("Agent id is required in order to install agent")) - } - - fillKubernetesAPI(lgr, installAgentCmdOptions.kube.context, installAgentCmdOptions.kube.namespace, false) - - if installAgentCmdOptions.tolerations != "" { - var tolerationsString string - - if installAgentCmdOptions.tolerations[0] == '@' { - tolerationsString = loadTolerationsFromFile(installAgentCmdOptions.tolerations[1:]) - } else { - tolerationsString = installAgentCmdOptions.tolerations - } - - tolerations, err := parseTolerations(tolerationsString) - if err != nil { - dieOnError(err) - } - - s.KubernetesAPI.Tolerations = tolerations - } - - if installAgentCmdOptions.venona.version != "" { - version := installAgentCmdOptions.venona.version - lgr.Info("Version set manually", "version", version) - s.Image.Tag = version - s.Version.Latest.Version = version - } - - - kns, err := parseNodeSelector(installAgentCmdOptions.kube.nodeSelector) - if err != nil { - dieOnError(err) - } - s.KubernetesAPI.NodeSelector = kns.String() - - builderInstallOpt.ClusterName = s.KubernetesAPI.ContextName - builderInstallOpt.KubeBuilder = getKubeClientBuilder(builderInstallOpt.ClusterName, s.KubernetesAPI.Namespace, s.KubernetesAPI.ConfigPath, s.KubernetesAPI.InCluster) - builderInstallOpt.ClusterNamespace = s.KubernetesAPI.Namespace - - - builder.Add(plugins.VenonaPluginType) - - values := s.BuildValues() - for _, p := range builder.Get() { - values, err = p.Install(builderInstallOpt, values) - if err != nil { - dieOnError(err) - } - } - lgr.Info("Agent installation completed Successfully") - - }, - -} - -func init() { - installCommand.AddCommand(installAgentCmd) - - viper.BindEnv("kube-namespace", "KUBE_NAMESPACE") - viper.BindEnv("kube-context", "KUBE_CONTEXT") - installAgentCmd.Flags().StringVar(&installAgentCmdOptions.agentToken, "agentToken", "", "Agent token created by codefresh") - installAgentCmd.Flags().StringVar(&installAgentCmdOptions.agentID, "agentId", "", "Agent id created by codefresh") - installAgentCmd.Flags().StringVar(&installAgentCmdOptions.venona.version, "venona-version", "", "Version of venona to install (default is the latest)") - installAgentCmd.Flags().StringVar(&installAgentCmdOptions.kube.namespace, "kube-namespace", viper.GetString("kube-namespace"), "Name of the namespace on which venona should be installed [$KUBE_NAMESPACE]") - installAgentCmd.Flags().StringVar(&installAgentCmdOptions.kube.context, "kube-context-name", viper.GetString("kube-context"), "Name of the kubernetes context on which venona should be installed (default is current-context) [$KUBE_CONTEXT]") - installAgentCmd.Flags().StringVar(&installAgentCmdOptions.kube.nodeSelector, "kube-node-selector", "", "The kubernetes node selector \"key=value\" to be used by venona resources (default is no node selector)") - installAgentCmd.Flags().StringVar(&installAgentCmdOptions.tolerations, "tolerations", "", "The kubernetes tolerations as JSON string to be used by venona resources (default is no tolerations)") - - installAgentCmd.Flags().BoolVar(&installAgentCmdOptions.kube.inCluster, "in-cluster", false, "Set flag if venona is been installed from inside a cluster") - installAgentCmd.Flags().BoolVar(&installAgentCmdOptions.dryRun, "dry-run", false, "Set to true to simulate installation") - installAgentCmd.Flags().BoolVar(&installAgentCmdOptions.kubernetesRunnerType, "kubernetes-runner-type", false, "Set the runner type to kubernetes (alpha feature)") -} - - - -func fillCodefreshAPI(logger logger.Logger) { - s := store.GetStore() - s.CodefreshAPI = &store.CodefreshAPI{ - Host: cfAPIHost, - } - -} \ No newline at end of file diff --git a/venonactl/cmd/install-runtime.go b/venonactl/cmd/install-runtime.go deleted file mode 100644 index 1a53c07a..00000000 --- a/venonactl/cmd/install-runtime.go +++ /dev/null @@ -1,151 +0,0 @@ -package cmd - -/* -Copyright 2019 The Codefresh Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import ( - "fmt" - - "github.com/codefresh-io/venona/venonactl/pkg/plugins" - "github.com/codefresh-io/venona/venonactl/pkg/store" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -var installRuntimeCmdOptions struct { - codefreshToken string - dryRun bool - kube struct { - namespace string - inCluster bool - context string - } - storageClass string - runtimeEnvironmentName string - kubernetesRunnerType bool - tolerations string -} - -var installRuntimeCmd = &cobra.Command{ - Use: "runtime", - Short: "Install Codefresh's runtime", - Run: func(cmd *cobra.Command, args []string) { - - s := store.GetStore() - lgr := createLogger("Install-runtime", verbose) - buildBasicStore(lgr) - extendStoreWithAgentAPI(lgr, installRuntimeCmdOptions.codefreshToken, "") - extendStoreWithKubeClient(lgr) - - if installRuntimeCmdOptions.runtimeEnvironmentName == "" { - dieOnError(fmt.Errorf("Codefresh envrionment name is required")) - } - if cfAPIHost == "" { - cfAPIHost = "https://g.codefresh.io" - } - // This is temporarily and used for signing - s.CodefreshAPI = &store.CodefreshAPI{ - Host: cfAPIHost, - } - - if installRuntimeCmdOptions.tolerations != "" { - var tolerationsString string - - if installRuntimeCmdOptions.tolerations[0] == '@' { - tolerationsString = loadTolerationsFromFile(installRuntimeCmdOptions.tolerations[1:]) - } else { - tolerationsString = installRuntimeCmdOptions.tolerations - } - - tolerations, err := parseTolerations(tolerationsString) - if err != nil { - dieOnError(err) - } - - s.KubernetesAPI.Tolerations = tolerations - } - - builder := plugins.NewBuilder(lgr) - isDefault := isUsingDefaultStorageClass(installRuntimeCmdOptions.storageClass) - - builderInstallOpt := &plugins.InstallOptions{ - StorageClass: installRuntimeCmdOptions.storageClass, - IsDefaultStorageClass: isDefault, - DryRun: installRuntimeCmdOptions.dryRun, - KubernetesRunnerType: installRuntimeCmdOptions.kubernetesRunnerType, - CodefreshHost: cfAPIHost, - CodefreshToken: installRuntimeCmdOptions.codefreshToken, - RuntimeEnvironment: installRuntimeCmdOptions.runtimeEnvironmentName, - ClusterNamespace: installRuntimeCmdOptions.kube.namespace, - } - - if installRuntimeCmdOptions.kubernetesRunnerType { - builder.Add(plugins.EnginePluginType) - } - - if isDefault { - builderInstallOpt.StorageClass = plugins.DefaultStorageClassNamePrefix - } - - fillKubernetesAPI(lgr, installRuntimeCmdOptions.kube.context, installRuntimeCmdOptions.kube.namespace, installRuntimeCmdOptions.kube.inCluster) - - if installRuntimeCmdOptions.dryRun { - s.DryRun = installRuntimeCmdOptions.dryRun - lgr.Info("Running in dry-run mode") - } - - // s.ClusterInCodefresh = installRuntimeCmdOptions.clusterNameInCodefresh - - builder.Add(plugins.RuntimeEnvironmentPluginType) - - if isDefault { - builder.Add(plugins.VolumeProvisionerPluginType) - } else { - lgr.Info("Custom StorageClass is set, skipping installation of default volume provisioner") - } - - builderInstallOpt.KubeBuilder = getKubeClientBuilder(s.KubernetesAPI.ContextName, s.KubernetesAPI.Namespace, s.KubernetesAPI.ConfigPath, s.KubernetesAPI.InCluster) - var err error - values := s.BuildValues() - for _, p := range builder.Get() { - values, err = p.Install(builderInstallOpt, values) - if err != nil { - dieOnError(err) - } - } - lgr.Info("Runtime installation completed Successfully") - - }, -} - -func init() { - installCommand.AddCommand(installRuntimeCmd) - - viper.BindEnv("kube-namespace", "KUBE_NAMESPACE") - viper.BindEnv("kube-context", "KUBE_CONTEXT") - - installRuntimeCmd.Flags().StringVar(&installRuntimeCmdOptions.codefreshToken, "codefreshToken", "", "Codefresh token") - installRuntimeCmd.Flags().StringVar(&installRuntimeCmdOptions.runtimeEnvironmentName, "runtimeName", viper.GetString("runtimeName"), "Name of the runtime as in codefresh") - installRuntimeCmd.Flags().StringVar(&installRuntimeCmdOptions.kube.namespace, "kube-namespace", viper.GetString("kube-namespace"), "Name of the namespace on which venona should be installed [$KUBE_NAMESPACE]") - installRuntimeCmd.Flags().StringVar(&installRuntimeCmdOptions.kube.context, "kube-context-name", viper.GetString("kube-context"), "Name of the kubernetes context on which venona should be installed (default is current-context) [$KUBE_CONTEXT]") - installRuntimeCmd.Flags().StringVar(&installRuntimeCmdOptions.storageClass, "storage-class", "", "Set a name of your custom storage class, note: this will not install volume provisioning components") - - installRuntimeCmd.Flags().BoolVar(&installRuntimeCmdOptions.kube.inCluster, "in-cluster", false, "Set flag if venona is been installed from inside a cluster") - installRuntimeCmd.Flags().BoolVar(&installRuntimeCmdOptions.dryRun, "dry-run", false, "Set to true to simulate installation") - installRuntimeCmd.Flags().BoolVar(&installRuntimeCmdOptions.kubernetesRunnerType, "kubernetes-runner-type", false, "Set the runner type to kubernetes (alpha feature)") - installRuntimeCmd.Flags().StringVar(&installRuntimeCmdOptions.tolerations, "tolerations", "", "The kubernetes tolerations as JSON string to be used by venona resources (default is no tolerations)") - -} diff --git a/venonactl/cmd/install-venona.go b/venonactl/cmd/install-venona.go deleted file mode 100644 index 1010dca0..00000000 --- a/venonactl/cmd/install-venona.go +++ /dev/null @@ -1,59 +0,0 @@ -package cmd - -/* -Copyright 2019 The Codefresh Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import ( - "github.com/spf13/cobra" -) - -const ( - clusterNameMaxLength = 20 - namespaceMaxLength = 20 -) - -var installCmdOptions struct { - dryRun bool - clusterNameInCodefresh string - kube struct { - namespace string - inCluster bool - context string - nodeSelector string - } - storageClass string - venona struct { - version string - } - setDefaultRuntime bool - installOnlyRuntimeEnvironment bool - skipRuntimeInstallation bool - runtimeEnvironmentName string - kubernetesRunnerType bool - buildNodeSelector string - buildAnnotations []string - tolerationJSONString string -} - -// installVenonaCmd represents the install command -var installVenonaCmd = &cobra.Command{ - Use: "all", - Short: "Install Codefresh's resource on cluster", -} - -func init() { - installCommand.AddCommand(installVenonaCmd) -} diff --git a/venonactl/cmd/install.go b/venonactl/cmd/install.go index 933b1be6..0eac949c 100644 --- a/venonactl/cmd/install.go +++ b/venonactl/cmd/install.go @@ -2,6 +2,7 @@ package cmd /* Copyright 2019 The Codefresh Authors. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -16,15 +17,276 @@ limitations under the License. */ import ( + "encoding/json" + "errors" + "fmt" + "gopkg.in/yaml.v2" + "io/ioutil" + "strings" + + "k8s.io/client-go/tools/clientcmd" + + "github.com/codefresh-io/venona/venonactl/pkg/store" + "github.com/codefresh-io/venona/venonactl/pkg/plugins" "github.com/spf13/cobra" + "github.com/spf13/viper" + k8sApi "k8s.io/api/core/v1" ) -var installCommand = &cobra.Command{ +const ( + clusterNameMaxLength = 20 + namespaceMaxLength = 20 +) + +var installCmdOptions struct { + dryRun bool + clusterNameInCodefresh string + kube struct { + namespace string + inCluster bool + context string + nodeSelector string + } + storageClass string + venona struct { + version string + } + setDefaultRuntime bool + installOnlyRuntimeEnvironment bool + skipRuntimeInstallation bool + runtimeEnvironmentName string + kubernetesRunnerType bool + buildNodeSelector string + buildAnnotations []string + tolerations string +} + +// installCmd represents the install command +var installCmd = &cobra.Command{ Use: "install", - Short: "Install agent and runtime components", + Short: "Install Codefresh's runtime-environment", + Run: func(cmd *cobra.Command, args []string) { + s := store.GetStore() + lgr := createLogger("Install", verbose) + buildBasicStore(lgr) + extendStoreWithCodefershClient(lgr) + extendStoreWithKubeClient(lgr) + + builder := plugins.NewBuilder(lgr) + isDefault := isUsingDefaultStorageClass(installCmdOptions.storageClass) + + builderInstallOpt := &plugins.InstallOptions{ + CodefreshHost: s.CodefreshAPI.Host, + CodefreshToken: s.CodefreshAPI.Token, + MarkAsDefault: installCmdOptions.setDefaultRuntime, + StorageClass: installCmdOptions.storageClass, + IsDefaultStorageClass: isDefault, + DryRun: installCmdOptions.dryRun, + KubernetesRunnerType: installCmdOptions.kubernetesRunnerType, + } + + if installCmdOptions.kubernetesRunnerType { + builder.Add(plugins.EnginePluginType) + } + + if isDefault { + builderInstallOpt.StorageClass = plugins.DefaultStorageClassNamePrefix + } + + if installCmdOptions.kube.context == "" { + config := clientcmd.GetConfigFromFileOrDie(s.KubernetesAPI.ConfigPath) + installCmdOptions.kube.context = config.CurrentContext + lgr.Debug("Kube Context is not set, using current context", "Kube-Context-Name", installCmdOptions.kube.context) + } + if installCmdOptions.kube.namespace == "" { + installCmdOptions.kube.namespace = "default" + } + + s.KubernetesAPI.InCluster = installCmdOptions.kube.inCluster + s.KubernetesAPI.ContextName = installCmdOptions.kube.context + s.KubernetesAPI.Namespace = installCmdOptions.kube.namespace + + kns, err := parseNodeSelector(installCmdOptions.kube.nodeSelector) + if err != nil { + dieOnError(err) + } + s.KubernetesAPI.NodeSelector = kns.String() + + if installCmdOptions.tolerations != "" { + var tolerationsString string + + if installCmdOptions.tolerations[0] == '@' { + tolerationsString = loadTolerationsFromFile(installCmdOptions.tolerations[1:]) + } else { + tolerationsString = installCmdOptions.tolerations + } + + tolerations, err := parseTolerations(tolerationsString) + if err != nil { + dieOnError(err) + } + + s.KubernetesAPI.Tolerations = tolerations + } + + if installCmdOptions.dryRun { + s.DryRun = installCmdOptions.dryRun + lgr.Info("Running in dry-run mode") + } + if installCmdOptions.venona.version != "" { + version := installCmdOptions.venona.version + lgr.Info("Version set manually", "version", version) + s.Image.Tag = version + s.Version.Latest.Version = version + } + s.ClusterInCodefresh = installCmdOptions.clusterNameInCodefresh + if installCmdOptions.installOnlyRuntimeEnvironment == true && installCmdOptions.skipRuntimeInstallation == true { + dieOnError(fmt.Errorf("Cannot use both flags skip-runtime-installation and only-runtime-environment")) + } + if installCmdOptions.installOnlyRuntimeEnvironment == true { + builder.Add(plugins.RuntimeEnvironmentPluginType) + } else if installCmdOptions.skipRuntimeInstallation == true { + if installCmdOptions.runtimeEnvironmentName == "" { + dieOnError(fmt.Errorf("runtime-environment flag is required when using flag skip-runtime-installation")) + } + s.RuntimeEnvironment = installCmdOptions.runtimeEnvironmentName + lgr.Info("Skipping installation of runtime environment, installing venona only") + builder.Add(plugins.VenonaPluginType) + } else { + builder. + Add(plugins.RuntimeEnvironmentPluginType). + Add(plugins.VenonaPluginType) + } + if isDefault { + builder.Add(plugins.VolumeProvisionerPluginType) + } else { + lgr.Info("Custom StorageClass is set, skipping installation of default volume provisioner") + } + + builderInstallOpt.ClusterName = s.KubernetesAPI.ContextName + builderInstallOpt.RegisterWithAgent = true + if s.ClusterInCodefresh != "" { + builderInstallOpt.ClusterName = s.ClusterInCodefresh + builderInstallOpt.RegisterWithAgent = false + } + builderInstallOpt.KubeBuilder = getKubeClientBuilder(builderInstallOpt.ClusterName, s.KubernetesAPI.Namespace, s.KubernetesAPI.ConfigPath, s.KubernetesAPI.InCluster) + builderInstallOpt.ClusterNamespace = s.KubernetesAPI.Namespace + + annotations := make(map[string]string) + for _, annotation := range installCmdOptions.buildAnnotations { + v := strings.Split(annotation, "=") + if len(v) != 2 { + dieOnError(errors.New("annotations must be in form \"key=value\"")) + } + annotations[v[0]] = v[1] + } + + builderInstallOpt.Annotations = annotations + + bns, err := parseNodeSelector(installCmdOptions.buildNodeSelector) + if err != nil { + dieOnError(err) + } + s.CodefreshAPI.BuildNodeSelector = bns + builderInstallOpt.BuildNodeSelector = bns + + err = validateInstallOptions(builderInstallOpt) + if err != nil { + dieOnError(err) + } + + values := s.BuildValues() + for _, p := range builder.Get() { + values, err = p.Install(builderInstallOpt, values) + if err != nil { + dieOnError(err) + } + } + lgr.Info("Installation completed Successfully") + }, } func init() { - rootCmd.AddCommand(installCommand) + rootCmd.AddCommand(installCmd) + + viper.BindEnv("kube-namespace", "KUBE_NAMESPACE") + viper.BindEnv("kube-context", "KUBE_CONTEXT") + + installCmd.Flags().StringVar(&installCmdOptions.clusterNameInCodefresh, "cluster-name", "", "cluster name (if not passed runtime-environment will be created cluster-less); this is a friendly name used for metadata does not need to match the literal cluster name. Limited to 20 Characters.") + installCmd.Flags().StringVar(&installCmdOptions.venona.version, "venona-version", "", "Version of venona to install (default is the latest)") + installCmd.Flags().StringVar(&installCmdOptions.runtimeEnvironmentName, "runtime-environment", "", "if --skip-runtime-installation set, will try to configure venona on current runtime-environment") + installCmd.Flags().StringVar(&installCmdOptions.kube.namespace, "kube-namespace", viper.GetString("kube-namespace"), "Name of the namespace on which venona should be installed [$KUBE_NAMESPACE]") + installCmd.Flags().StringVar(&installCmdOptions.kube.context, "kube-context-name", viper.GetString("kube-context"), "Name of the kubernetes context on which venona should be installed (default is current-context) [$KUBE_CONTEXT]") + installCmd.Flags().StringVar(&installCmdOptions.storageClass, "storage-class", "", "Set a name of your custom storage class, note: this will not install volume provisioning components") + installCmd.Flags().StringVar(&installCmdOptions.kube.nodeSelector, "kube-node-selector", "", "The kubernetes node selector \"key=value\" to be used by venona resources (default is no node selector)") + installCmd.Flags().StringVar(&installCmdOptions.buildNodeSelector, "build-node-selector", "", "The kubernetes node selector \"key=value\" to be used by venona build resources (default is no node selector)") + installCmd.Flags().StringArrayVar(&installCmdOptions.buildAnnotations, "build-annotations", []string{}, "The kubernetes metadata.annotations as \"key=value\" to be used by venona build resources (default is no node selector)") + installCmd.Flags().StringVar(&installCmdOptions.tolerations, "tolerations", "", `The kubernetes tolerations as JSON string to be used by venona resources (default is no tolerations). If prefixed with "@", loads from a file: @/tmp/tolerations.json`) + + installCmd.Flags().BoolVar(&installCmdOptions.skipRuntimeInstallation, "skip-runtime-installation", false, "Set flag if you already have a configured runtime-environment, add --runtime-environment flag with name") + installCmd.Flags().BoolVar(&installCmdOptions.kube.inCluster, "in-cluster", false, "Set flag if venona is been installed from inside a cluster") + installCmd.Flags().BoolVar(&installCmdOptions.installOnlyRuntimeEnvironment, "only-runtime-environment", false, "Set to true to onlky configure namespace as runtime-environment for Codefresh") + installCmd.Flags().BoolVar(&installCmdOptions.dryRun, "dry-run", false, "Set to true to simulate installation") + installCmd.Flags().BoolVar(&installCmdOptions.setDefaultRuntime, "set-default", false, "Mark the install runtime-environment as default one after installation") + installCmd.Flags().BoolVar(&installCmdOptions.kubernetesRunnerType, "kubernetes-runner-type", false, "Set the runner type to kubernetes (alpha feature)") + +} + +type nodeSelector map[string]string + +func parseNodeSelector(s string) (nodeSelector, error) { + if s == "" { + return nodeSelector{}, nil + } + v := strings.Split(s, "=") + if len(v) != 2 { + return nil, errors.New("node selector must be in form \"key=value\"") + } + return nodeSelector{v[0]: v[1]}, nil +} + +func loadTolerationsFromFile(filename string) string { + data, err := ioutil.ReadFile(filename) + if err != nil { + dieOnError(err) + } + + return string(data) +} + +func parseTolerations(s string) (string, error) { + if s == "" { + return "", nil + } + var data []k8sApi.Toleration + err := json.Unmarshal([]byte(s), &data) + if err != nil { + return "", fmt.Errorf("can not parse tolerations: %s", err) + } + y, err := yaml.Marshal(&data) + if err != nil { + return "", fmt.Errorf("can not marshel tolerations to yaml: %s", err) + } + d := fmt.Sprintf("\n%s", string(y)) + return d, nil +} + +func validateInstallOptions(opts *plugins.InstallOptions) error { + if len(opts.ClusterName) > clusterNameMaxLength { + return errors.New(fmt.Sprintf("cluster name length is limited to %d", clusterNameMaxLength)) + } + if len(opts.ClusterNamespace) > namespaceMaxLength { + return errors.New(fmt.Sprintf("cluster namespace length is limited to %d", namespaceMaxLength)) + } + return nil +} + +// String returns a k8s compliant string representation of the nodeSelector. Only a single value is supported. +func (ns nodeSelector) String() string { + var s string + for k, v := range ns { + s = fmt.Sprintf("%s: %s", k, v) + } + return s } diff --git a/venonactl/cmd/uninstall-agent.go b/venonactl/cmd/uninstall-agent.go deleted file mode 100644 index 20526a7c..00000000 --- a/venonactl/cmd/uninstall-agent.go +++ /dev/null @@ -1,69 +0,0 @@ -package cmd - - -import ( - "fmt" - - "github.com/codefresh-io/venona/venonactl/pkg/store" - - "github.com/codefresh-io/venona/venonactl/pkg/plugins" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -var uninstallAgentCmdOptions struct { - kube struct { - context string - namespace string - kubePath string - } -} - -var uninstallAgentCmd = &cobra.Command{ - Use: "agent", - Short: "Uninstall Codefresh's agent", - Run: func(cmd *cobra.Command, args []string) { - s := store.GetStore() - lgr := createLogger("UninstallAgent", verbose) - buildBasicStore(lgr) - extendStoreWithKubeClient(lgr) - - s.CodefreshAPI = &store.CodefreshAPI{} - s.AgentAPI = &store.AgentAPI{} - - - builder := plugins.NewBuilder(lgr) - if uninstallAgentCmdOptions.kube.context == "" { - dieOnError(fmt.Errorf("Context name is required in order to uninstall agent")) - } - if uninstallAgentCmdOptions.kube.namespace == "" { - dieOnError(fmt.Errorf("Namespace name is required to in order to uninstall agent")) - } - - - deleteOptions := &plugins.DeleteOptions{} - s.KubernetesAPI.ContextName = uninstallAgentCmdOptions.kube.context - s.KubernetesAPI.Namespace = uninstallAgentCmdOptions.kube.namespace - - builder.Add(plugins.VenonaPluginType) - deleteOptions.KubeBuilder = getKubeClientBuilder(s.KubernetesAPI.ContextName, s.KubernetesAPI.Namespace, s.KubernetesAPI.ConfigPath, false) - deleteOptions.ClusterNamespace = uninstallAgentCmdOptions.kube.namespace - for _, p := range builder.Get() { - err := p.Delete(deleteOptions, s.BuildValues()) - if err != nil { - dieOnError(err) - } - } - - lgr.Info("Deletion of agent is completed") - }, -} - -func init() { - uninstallCommand.AddCommand(uninstallAgentCmd) - viper.BindEnv("kube-namespace", "KUBE_NAMESPACE") - viper.BindEnv("kube-context", "KUBE_CONTEXT") - uninstallAgentCmd.Flags().StringVar(&uninstallAgentCmdOptions.kube.context, "kube-context-name", "", "Name of the kubernetes context on which venona should be uninstalled (default is current-context) [$KUBE_CONTEXT]") - uninstallAgentCmd.Flags().StringVar(&uninstallAgentCmdOptions.kube.namespace, "kube-namespace", "", "Name of the namespace on which venona should be uninstalled [$KUBE_NAMESPACE]") - -} \ No newline at end of file diff --git a/venonactl/cmd/uninstall-runtime.go b/venonactl/cmd/uninstall-runtime.go deleted file mode 100644 index d5df776f..00000000 --- a/venonactl/cmd/uninstall-runtime.go +++ /dev/null @@ -1,115 +0,0 @@ -package cmd - - -import ( - "fmt" - - "github.com/codefresh-io/venona/venonactl/pkg/store" - - "github.com/codefresh-io/venona/venonactl/pkg/plugins" - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -var uninstallRunimeCmdOptions struct { - kube struct { - context string - namespace string - kubePath string - } - kubeVenona struct { - namespace string - kubePath string - context string - } - runtimeEnvironmentName string - storageClassName string - restartAgent bool -} - -var uninstallRuntimeCmd = &cobra.Command{ - Use: "runtime", - Short: "Uninstall Codefresh's runtime", - Run: func(cmd *cobra.Command, args []string) { - s := store.GetStore() - lgr := createLogger("UninstallRuntime", verbose) - buildBasicStore(lgr) - extendStoreWithKubeClient(lgr) - - s.CodefreshAPI = &store.CodefreshAPI{} - s.AgentAPI = &store.AgentAPI{} - - - builder := plugins.NewBuilder(lgr) - if uninstallRunimeCmdOptions.kube.context == "" { - dieOnError(fmt.Errorf("Context name is required in order to uninstall agent")) - } - if uninstallRunimeCmdOptions.kube.namespace == "" { - dieOnError(fmt.Errorf("Namespace name is required to in order to uninstall agent")) - } - - - if uninstallRunimeCmdOptions.kubeVenona.kubePath == "" { - uninstallRunimeCmdOptions.kubeVenona.kubePath = kubeConfigPath - } - if uninstallRunimeCmdOptions.kubeVenona.namespace == "" { - uninstallRunimeCmdOptions.kubeVenona.namespace = uninstallRunimeCmdOptions.kube.namespace - } - if uninstallRunimeCmdOptions.kubeVenona.context == "" { - uninstallRunimeCmdOptions.kubeVenona.context = uninstallRunimeCmdOptions.kube.context - } - - if uninstallRunimeCmdOptions.kube.kubePath == "" { - uninstallRunimeCmdOptions.kube.kubePath = kubeConfigPath - } - - deleteOptions := &plugins.DeleteOptions{} - // runtime - deleteOptions.KubeBuilder = getKubeClientBuilder(uninstallRunimeCmdOptions.kube.context, - uninstallRunimeCmdOptions.kube.namespace, - uninstallRunimeCmdOptions.kube.kubePath, - false) - - // agent - deleteOptions.AgentKubeBuilder = getKubeClientBuilder(uninstallRunimeCmdOptions.kubeVenona.context, - uninstallRunimeCmdOptions.kubeVenona.namespace, - uninstallRunimeCmdOptions.kubeVenona.kubePath, - false) - - builder.Add(plugins.RuntimeEnvironmentPluginType) - if isUsingDefaultStorageClass(uninstallRunimeCmdOptions.storageClassName) { - builder.Add(plugins.VolumeProvisionerPluginType) - } - builder.Add(plugins.RuntimeAttachType) - deleteOptions.ClusterNamespace = uninstallRunimeCmdOptions.kube.namespace - deleteOptions.AgentNamespace = uninstallRunimeCmdOptions.kubeVenona.namespace - deleteOptions.RuntimeEnvironment = uninstallRunimeCmdOptions.runtimeEnvironmentName - for _, p := range builder.Get() { - err := p.Delete(deleteOptions, s.BuildValues()) - if err != nil { - dieOnError(err) - } - } - - lgr.Info("Deletion of runtime is completed") - }, -} - -func init() { - uninstallCommand.AddCommand(uninstallRuntimeCmd) - viper.BindEnv("kube-namespace", "KUBE_NAMESPACE") - viper.BindEnv("kube-context", "KUBE_CONTEXT") - uninstallRuntimeCmd.Flags().StringVar(&uninstallRunimeCmdOptions.kube.namespace, "kube-namespace", viper.GetString("kube-namespace"), "Name of the namespace on which venona should be installed [$KUBE_NAMESPACE]") - uninstallRuntimeCmd.Flags().StringVar(&uninstallRunimeCmdOptions.kube.context, "kube-context-name", viper.GetString("kube-context"), "Name of the kubernetes context on which venona should be installed (default is current-context) [$KUBE_CONTEXT]") - uninstallRuntimeCmd.Flags().StringVar(&uninstallRunimeCmdOptions.kube.kubePath, "kube-config-path", viper.GetString("kubeconfig"), "Path to kubeconfig file (default is $HOME/.kube/config) [$KUBECONFIG]") - - uninstallRuntimeCmd.Flags().StringVar(&uninstallRunimeCmdOptions.runtimeEnvironmentName, "runtime-name", viper.GetString("runtime-name"), "Name of the runtime as in codefresh") - - uninstallRuntimeCmd.Flags().StringVar(&uninstallRunimeCmdOptions.kubeVenona.namespace, "kube-namespace-agent", viper.GetString("kube-namespace-agent"), "Name of the namespace where venona is installed [$KUBE_NAMESPACE]") - uninstallRuntimeCmd.Flags().StringVar(&uninstallRunimeCmdOptions.kubeVenona.context, "kube-context-name-agent", viper.GetString("kube-context-agent"), "Name of the kubernetes context on which venona is installed (default is current-context) [$KUBE_CONTEXT]") - uninstallRuntimeCmd.Flags().StringVar(&uninstallRunimeCmdOptions.kubeVenona.kubePath, "kube-config-path-agent", viper.GetString("kubeconfig-agent"), "Path to kubeconfig file (default is $HOME/.kube/config) for agent [$KUBECONFIG]") - uninstallRuntimeCmd.Flags().BoolVar(&uninstallRunimeCmdOptions.restartAgent, "restart-agent", viper.GetBool("restart-agent"), "Restart agent after attach operation") - - uninstallRuntimeCmd.Flags().StringVar(&uninstallRunimeCmdOptions.storageClassName, "storage-class-name", viper.GetString("storage-class-name"), "Storage class name of the runtime to be uninstalled") - -} \ No newline at end of file diff --git a/venonactl/cmd/uninstall.go b/venonactl/cmd/uninstall.go deleted file mode 100644 index d598b4ce..00000000 --- a/venonactl/cmd/uninstall.go +++ /dev/null @@ -1,30 +0,0 @@ -package cmd - -/* -Copyright 2019 The Codefresh Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import ( - - "github.com/spf13/cobra" -) - -var uninstallCommand = &cobra.Command{ - Use: "uninstall", - Short: "Uninstall agent and runtime components", -} - -func init() { - rootCmd.AddCommand(uninstallCommand) -} diff --git a/venonactl/cmd/upgrade.go b/venonactl/cmd/upgrade.go index 4a2f625d..0d522ccb 100644 --- a/venonactl/cmd/upgrade.go +++ b/venonactl/cmd/upgrade.go @@ -17,8 +17,10 @@ limitations under the License. */ import ( - "os" + "errors" + "github.com/codefresh-io/venona/venonactl/pkg/plugins" + "github.com/codefresh-io/venona/venonactl/pkg/store" "github.com/spf13/cobra" ) @@ -33,10 +35,60 @@ var upgradeCmdOpt struct { var upgradeCmd = &cobra.Command{ Use: "upgrade [name]", Short: "Upgrade existing runtime-environment", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("requires name of the runtime-environment") + } + + if len(args) > 1 { + return errors.New("Cannot upgrade multiple runtimes once") + } + return nil + }, Run: func(cmd *cobra.Command, args []string) { - lgr := createLogger("Upgrade", true) - lgr.Warn("Upgrade is not supported from version < 1.0.0 to version >= 1.x.x, please run the migration script: https://github.com/codefresh-io/venona/blob/master/scripts/migration.sh to upgrade to the latest version") - os.Exit(0) + s := store.GetStore() + lgr := createLogger("Upgrade", verbose) + buildBasicStore(lgr) + extendStoreWithCodefershClient(lgr) + extendStoreWithKubeClient(lgr) + builder := plugins.NewBuilder(lgr) + builderUpgradeOpt := &plugins.UpgradeOptions{ + CodefreshHost: s.CodefreshAPI.Host, + CodefreshToken: s.CodefreshAPI.Token, + DryRun: upgradeCmdOpt.dryRun, + Name: s.AppName, + } + + re, _ := s.CodefreshAPI.Client.RuntimeEnvironments().Get(args[0]) + contextName := re.RuntimeScheduler.Cluster.ClusterProvider.Selector + if upgradeCmdOpt.kube.context != "" { + contextName = upgradeCmdOpt.kube.context + } + s.KubernetesAPI.ContextName = contextName + s.KubernetesAPI.Namespace = re.RuntimeScheduler.Cluster.Namespace + + builderUpgradeOpt.ClusterNamespace = s.KubernetesAPI.Namespace + + if upgradeCmdOpt.dryRun { + lgr.Info("Running in dry-run mode") + } else { + builder.Add(plugins.VenonaPluginType) + if isUsingDefaultStorageClass(re.RuntimeScheduler.Pvcs.Dind.StorageClassName) { + builder.Add(plugins.VolumeProvisionerPluginType) + } + builder.Add(plugins.RuntimeEnvironmentPluginType) + } + + builderUpgradeOpt.KubeBuilder = getKubeClientBuilder(upgradeCmdOpt.kube.context, s.KubernetesAPI.Namespace, s.KubernetesAPI.ConfigPath, s.KubernetesAPI.InCluster) + + var err error + values := s.BuildValues() + for _, p := range builder.Get() { + values, err = p.Upgrade(builderUpgradeOpt, values) + if err != nil { + dieOnError(err) + } + } }, } diff --git a/venonactl/go.mod b/venonactl/go.mod index 892b865a..5af5c712 100644 --- a/venonactl/go.mod +++ b/venonactl/go.mod @@ -41,6 +41,4 @@ require ( replace git.apache.org/thrift.git => github.com/apache/thrift v0.0.0-20181218151757-9b75e4fe745a -replace github.com/census-instrumentation/opencensus-proto => github.com/census-instrumentation/opencensus-proto v0.0.3-0.20181214143942-ba49f56771b8 - go 1.13 diff --git a/venonactl/go.sum b/venonactl/go.sum index fac40908..58b36833 100644 --- a/venonactl/go.sum +++ b/venonactl/go.sum @@ -20,14 +20,12 @@ github.com/apache/thrift v0.0.0-20181218151757-9b75e4fe745a/go.mod h1:cp2SuWMxlE github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/census-instrumentation/opencensus-proto v0.0.3-0.20181214143942-ba49f56771b8 h1:vvX9PxDdQSEmcYSMjZpKbCRRTyHrvfr08Ue9/zk7HV4= -github.com/census-instrumentation/opencensus-proto v0.0.3-0.20181214143942-ba49f56771b8/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.1.0-0.20181214143942-ba49f56771b8 h1:gUqsFVdUKoRHNg8fkFd8gB5OOEa/g5EwlAHznb4zjbI= +github.com/census-instrumentation/opencensus-proto v0.1.0-0.20181214143942-ba49f56771b8/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/codefresh-io/go-sdk v0.17.0 h1:0fwE4K0QeqM2VfyjiALzkH2se4HbLh9BiQCuzQnJH/U= github.com/codefresh-io/go-sdk v0.17.0/go.mod h1:b6hK9euSW+MDXUDHU1+YgP8vzcij749I31ZIZSXed+I= -github.com/codefresh-io/venona v0.30.2 h1:FgLNURnChYvamwBVdfQDthIRdfaOb0Zyvc2EdnajaPQ= -github.com/codefresh-io/venona v1.0.0 h1:w8fxc2+kKiTPYpHH5AFAyyDtefcnNKtlW8sHAP9Gvvo= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= @@ -80,7 +78,6 @@ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1 github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.0.0-20190206021053-df38e1611dbe h1:wFrfOm62oPv9R8kEABWXx63s2Akmt68WF1ngahljxFU= github.com/gophercloud/gophercloud v0.0.0-20190206021053-df38e1611dbe/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7 h1:6TSoaYExHper8PYsJu23GWVNOyYRCSnIFyxKgLSZ54w= @@ -106,7 +103,6 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE= github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -153,6 +149,10 @@ github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTm github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= +github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -177,26 +177,34 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.0 h1:O9FblXGxoTc51M+cqr74Bm2Tmt4PvkA5iu/j8HrkNuY= github.com/spf13/afero v1.2.0/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.1/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk= github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -254,12 +262,21 @@ golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8 h1:41hwlulw1prEMBxLQSlMSux1zxJf07B3WPsdjJlKZxE= +golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191002091554-b397fe3ad8ed h1:5TJcLJn2a55mJjzYk0yOoqN8X1OdvBDUnaZaKKyQtkY= +golang.org/x/sys v0.0.0-20191002091554-b397fe3ad8ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191218084908-4a24b4065292 h1:Y8q0zsdcgAd+JU8VUA8p8Qv2YhuY9zevDG2ORt5qBUI= +golang.org/x/sys v0.0.0-20191218084908-4a24b4065292/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -295,6 +312,8 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/venonactl/pkg/kube/kube.go b/venonactl/pkg/kube/kube.go index 903d0600..a9051be0 100644 --- a/venonactl/pkg/kube/kube.go +++ b/venonactl/pkg/kube/kube.go @@ -10,7 +10,6 @@ import ( type ( Kube interface { BuildClient() (*kubernetes.Clientset, error) - BuildConfig() (clientcmd.ClientConfig) } kube struct { @@ -43,23 +42,17 @@ func (k *kube) BuildClient() (*kubernetes.Clientset, error) { if k.inCluster { config, err = rest.InClusterConfig() } else { - config, err = k.BuildConfig().ClientConfig() + config, err = clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + &clientcmd.ClientConfigLoadingRules{ExplicitPath: k.pathToKubeConfig}, + &clientcmd.ConfigOverrides{ + CurrentContext: k.contextName, + Context: clientcmdapi.Context{ + Namespace: k.namespace, + }, + }).ClientConfig() } if err != nil { return nil, err } return kubernetes.NewForConfig(config) } - -func (k *kube) BuildConfig() (clientcmd.ClientConfig) { - config := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( - &clientcmd.ClientConfigLoadingRules{ExplicitPath: k.pathToKubeConfig}, - &clientcmd.ConfigOverrides{ - CurrentContext: k.contextName, - Context: clientcmdapi.Context{ - Namespace: k.namespace, - }, - }) - return config - -} diff --git a/venonactl/pkg/plugins/engine.go b/venonactl/pkg/plugins/engine.go index 71b9c677..1eb6acd0 100644 --- a/venonactl/pkg/plugins/engine.go +++ b/venonactl/pkg/plugins/engine.go @@ -87,7 +87,7 @@ func (u *enginePlugin) Delete(deleteOpt *DeleteOptions, v Values) error { matchPattern: engineFilesPattern, operatorType: EnginePluginType, } - return uninstall(opt) + return delete(opt) } func (u *enginePlugin) Upgrade(opt *UpgradeOptions, v Values) (Values, error) { diff --git a/venonactl/pkg/plugins/plugin.go b/venonactl/pkg/plugins/plugin.go index c1c07aca..2c694c40 100644 --- a/venonactl/pkg/plugins/plugin.go +++ b/venonactl/pkg/plugins/plugin.go @@ -8,7 +8,6 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" ) const ( @@ -17,7 +16,6 @@ const ( VolumeProvisionerPluginType = "volume-provisioner" EnginePluginType = "engine" DefaultStorageClassNamePrefix = "dind-local-volumes-venona" - RuntimeAttachType = "runtime-attach" ) type ( @@ -33,10 +31,6 @@ type ( Get() []Plugin } - KubeClientBuilder interface { - BuildClient() (*kubernetes.Clientset, error) - } - pb struct { logger logger.Logger plugins []Plugin @@ -55,34 +49,18 @@ type ( IsDefaultStorageClass bool KubeBuilder interface { BuildClient() (*kubernetes.Clientset, error) - BuildConfig() clientcmd.ClientConfig - - } - AgentKubeBuilder interface { - BuildClient() (*kubernetes.Clientset, error) } DryRun bool KubernetesRunnerType bool BuildNodeSelector map[string]string Annotations map[string]string - RuntimeEnvironment string - RuntimeClusterName string - RuntimeServiceAccount string - RestartAgent bool } DeleteOptions struct { KubeBuilder interface { BuildClient() (*kubernetes.Clientset, error) } - AgentKubeBuilder interface { - BuildClient() (*kubernetes.Clientset, error) - } - ClusterNamespace string // runtime - AgentNamespace string // agent - RuntimeEnvironment string - RestartAgent bool - + ClusterNamespace string } UpgradeOptions struct { @@ -180,11 +158,6 @@ func build(t string, logger logger.Logger) Plugin { } } - if t == RuntimeAttachType { - return &runtimeAttachPlugin{ - logger: logger.New("Plugin", RuntimeAttachType), - } - } return nil } @@ -244,7 +217,7 @@ func status(opt *statusOptions) ([][]string, error) { return rows, nil } -func uninstall(opt *deleteOptions) error { +func delete(opt *deleteOptions) error { kubeObjects, err := KubeObjectsFromTemplates(opt.templates, opt.templateValues, opt.matchPattern, opt.logger) if err != nil { diff --git a/venonactl/pkg/plugins/runtime-attach.go b/venonactl/pkg/plugins/runtime-attach.go deleted file mode 100644 index 6cee25e7..00000000 --- a/venonactl/pkg/plugins/runtime-attach.go +++ /dev/null @@ -1,263 +0,0 @@ -package plugins - -import ( - "encoding/base64" - "fmt" - "strings" - - "github.com/codefresh-io/venona/venonactl/pkg/logger" - templates "github.com/codefresh-io/venona/venonactl/pkg/templates/kubernetes" - "gopkg.in/yaml.v2" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type runtimeAttachPlugin struct { - logger logger.Logger -} - -type RuntimeConfiguration struct { - Crt string `yaml:"crt"` - Token string `yaml:"token"` - Host string `yaml:"host"` - Name string `yaml:"name"` - Type string `yaml:"type"` -} - -type venonaConf struct { - Runtimes map[string]RuntimeConfiguration `yaml:"runtimes,omitempty"` -} - -const ( - runtimeAttachFilesPattern = ".*.runtime-attach.yaml" - runtimeSecretName = "venonaconf" -) - -func buildRuntimeConfig(opt *InstallOptions, v Values) (RuntimeConfiguration, error) { - - config, err := opt.KubeBuilder.BuildConfig().ClientConfig() - if err != nil { - return RuntimeConfiguration{}, fmt.Errorf("Failed to get client config on runtime cluster: %v", err) - } - - cs, err := opt.KubeBuilder.BuildClient() - if err != nil { - return RuntimeConfiguration{}, fmt.Errorf("Failed to create client on runtime cluster: %v", err) - } - - // get default service account for the namespace - var getOpt metav1.GetOptions - sa, err := cs.CoreV1().ServiceAccounts(opt.RuntimeClusterName).Get(opt.RuntimeServiceAccount, getOpt) - if err != nil { - return RuntimeConfiguration{}, fmt.Errorf("Failed to read service account runtime cluster: %v", err) - } - - secretRef := sa.Secrets[0] - secret, err := cs.CoreV1().Secrets(opt.RuntimeClusterName).Get(secretRef.Name, getOpt) - if err != nil { - return RuntimeConfiguration{}, fmt.Errorf("Failed to get secret from service account on runtime cluster: %v", err) - } - - crt := secret.Data["ca.crt"] - token := secret.Data["token"] - - rc := RuntimeConfiguration{ - Crt: string(crt), - Token: string(token), - Host: config.Host, - Name: opt.RuntimeEnvironment, - Type: "runtime", - } - - return rc, nil -} - -func readCurrentVenonaConf(agentKubeBuilder KubeClientBuilder, clusterNamespace string) (venonaConf, error) { - - cs, err := agentKubeBuilder.BuildClient() - if err != nil { - return venonaConf{}, fmt.Errorf("Failed to create client on venona cluster: %v", err) - } - secret, err := cs.CoreV1().Secrets(clusterNamespace).Get(runtimeSecretName, metav1.GetOptions{}) - - conf := &venonaConf{ - Runtimes: make(map[string]RuntimeConfiguration), - } - for k, v := range secret.Data { - cnf := RuntimeConfiguration{} - if err := yaml.Unmarshal(v, &cnf); err != nil { - return venonaConf{}, fmt.Errorf("Failed to unmarshal yaml with error: %s", err.Error()) - } - conf.Runtimes[k] = cnf - } - return *conf, nil - -} - -func (u *runtimeAttachPlugin) Install(opt *InstallOptions, v Values) (Values, error) { - cs, err := opt.AgentKubeBuilder.BuildClient() // on the agent cluster - if err != nil { - u.logger.Error(fmt.Sprintf("Cannot create kubernetes clientset: %v ", err)) - return nil, err - } - - // read current venona conf - currentVenonaConf, err := readCurrentVenonaConf(opt.AgentKubeBuilder, opt.ClusterNamespace) - if err != nil { - u.logger.Error(fmt.Sprintf("Cannot read venonaconf: %v ", err)) - return nil, err - } - - // new runtime configuration - rc, err := buildRuntimeConfig(opt, v) - if err != nil { - return nil, err - } - if currentVenonaConf.Runtimes == nil { - currentVenonaConf.Runtimes = make(map[string]RuntimeConfiguration) - } - // normalize the key in the secret to make sure we are not violating kube naming conventions - name := strings.ReplaceAll(opt.RuntimeEnvironment, "/", ".") - name = strings.ReplaceAll(name, "@", ".") - currentVenonaConf.Runtimes[fmt.Sprintf("%s.runtime.yaml", name)] = rc - runtimes := map[string]string{} - for name, runtime := range currentVenonaConf.Runtimes { - // marshel prior persist - d, err := yaml.Marshal(runtime) - if err != nil { - u.logger.Error(fmt.Sprintf("Cannot marshal merged venonaconf: %v ", err)) - return nil, err - } - - runtimes[name] = base64.StdEncoding.EncodeToString([]byte(d)) - } - v["venonaConf"] = runtimes - - cs.CoreV1().Secrets(opt.ClusterNamespace).Delete(runtimeSecretName, &metav1.DeleteOptions{}) - - err = install(&installOptions{ - logger: u.logger, - templates: templates.TemplatesMap(), - templateValues: v, - kubeClientSet: cs, - namespace: opt.ClusterNamespace, - matchPattern: runtimeAttachFilesPattern, - operatorType: RuntimeAttachType, - dryRun: opt.DryRun, - }) - - if err != nil { - return nil, err - } - - if opt.RestartAgent { - list, err := cs.CoreV1().Pods(opt.ClusterNamespace).List(metav1.ListOptions{LabelSelector: fmt.Sprintf("app=%v", v["AppName"])}) - if err != nil { - u.logger.Error(fmt.Sprintf("Cannot find agent pod: %v ", err)) - return nil, err - } - podName := list.Items[0].ObjectMeta.Name - err = cs.CoreV1().Pods(opt.ClusterNamespace).Delete(podName, &metav1.DeleteOptions{}) - if err != nil { - u.logger.Error(fmt.Sprintf("Cannot delete agent pod: %v ", err)) - return nil, err - } - - } - - return v, nil - -} - -func (u *runtimeAttachPlugin) Status(statusOpt *StatusOptions, v Values) ([][]string, error) { - - cs, err := statusOpt.KubeBuilder.BuildClient() - if err != nil { - u.logger.Error(fmt.Sprintf("Cannot create kubernetes clientset: %v ", err)) - return nil, err - } - opt := &statusOptions{ - templates: templates.TemplatesMap(), - templateValues: v, - kubeClientSet: cs, - namespace: statusOpt.ClusterNamespace, - matchPattern: runtimeAttachFilesPattern, - operatorType: RuntimeAttachType, - logger: u.logger, - } - return status(opt) - -} - -func (u *runtimeAttachPlugin) Delete(deleteOpt *DeleteOptions, v Values) error { - cs, err := deleteOpt.AgentKubeBuilder.BuildClient() - if err != nil { - u.logger.Error(fmt.Sprintf("Cannot create kubernetes clientset: %v ", err)) - return err - } - // Delete the entry from venonaconf - if this is the only , delete the secret - - // read current venona conf - currentVenonaConf, err := readCurrentVenonaConf(deleteOpt.AgentKubeBuilder, deleteOpt.AgentNamespace) - if err != nil { - u.logger.Error(fmt.Sprintf("Cannot read venonaconf: %v ", err)) - return err - } - name := strings.ReplaceAll(deleteOpt.RuntimeEnvironment, "/", ".") - name = fmt.Sprintf("%s.runtime.yaml", name) - if _, ok := currentVenonaConf.Runtimes[name]; ok { - delete(currentVenonaConf.Runtimes, name) - } - - // If only one runtime is defined, remove the secret , otherwise remove the entry and persist - shouldDelete := true - if len(currentVenonaConf.Runtimes) > 0 { - - runtimes := map[string]string{} - for name, runtime := range currentVenonaConf.Runtimes { - // marshel prior persist - d, err := yaml.Marshal(runtime) - if err != nil { - u.logger.Error(fmt.Sprintf("Cannot marshal merged venonaconf: %v ", err)) - return err - } - - runtimes[name] = base64.StdEncoding.EncodeToString([]byte(d)) - } - - shouldDelete = false - v["venonaConf"] = runtimes - - cs.CoreV1().Secrets(deleteOpt.AgentNamespace).Delete(runtimeSecretName, &metav1.DeleteOptions{}) - - err = install(&installOptions{ - logger: u.logger, - templates: templates.TemplatesMap(), - templateValues: v, - kubeClientSet: cs, - namespace: deleteOpt.AgentNamespace, - matchPattern: runtimeAttachFilesPattern, - operatorType: RuntimeAttachType, - }) - return err - - } - - if shouldDelete { - opt := &deleteOptions{ - templates: templates.TemplatesMap(), - templateValues: v, - kubeClientSet: cs, - namespace: deleteOpt.AgentNamespace, - matchPattern: runtimeAttachFilesPattern, - operatorType: RuntimeAttachType, - logger: u.logger, - } - return uninstall(opt) - } - return nil - -} - -func (u *runtimeAttachPlugin) Upgrade(_ *UpgradeOptions, v Values) (Values, error) { - return v, nil -} diff --git a/venonactl/pkg/plugins/runtime-environment.go b/venonactl/pkg/plugins/runtime-environment.go index fd6c727f..06764da0 100644 --- a/venonactl/pkg/plugins/runtime-environment.go +++ b/venonactl/pkg/plugins/runtime-environment.go @@ -71,7 +71,6 @@ func (u *runtimeEnvironmentPlugin) Install(opt *InstallOptions, v Values) (Value return nil, err } - v["RuntimeEnvironment"] = opt.RuntimeEnvironment err = install(&installOptions{ logger: u.logger, templates: templates.TemplatesMap(), @@ -86,6 +85,12 @@ func (u *runtimeEnvironmentPlugin) Install(opt *InstallOptions, v Values) (Value return nil, err } + re, err := cf.Register() + if err != nil { + return nil, err + } + v["RuntimeEnvironment"] = re.Metadata.Name + return v, nil } @@ -111,7 +116,7 @@ func (u *runtimeEnvironmentPlugin) Delete(deleteOpt *DeleteOptions, v Values) er cs, err := deleteOpt.KubeBuilder.BuildClient() if err != nil { u.logger.Error(fmt.Sprintf("Cannot create kubernetes clientset: %v ", err)) - return err + return nil } opt := &deleteOptions{ templates: templates.TemplatesMap(), @@ -122,7 +127,7 @@ func (u *runtimeEnvironmentPlugin) Delete(deleteOpt *DeleteOptions, v Values) er operatorType: RuntimeEnvironmentPluginType, logger: u.logger, } - return uninstall(opt) + return delete(opt) } func (u *runtimeEnvironmentPlugin) Upgrade(_ *UpgradeOptions, v Values) (Values, error) { diff --git a/venonactl/pkg/plugins/venona.go b/venonactl/pkg/plugins/venona.go index f8666ca6..273c57e1 100644 --- a/venonactl/pkg/plugins/venona.go +++ b/venonactl/pkg/plugins/venona.go @@ -39,27 +39,25 @@ const ( // Install venona agent func (u *venonaPlugin) Install(opt *InstallOptions, v Values) (Values, error) { - if v["AgentToken"] == "" { - u.logger.Debug("Generating token for agent") - tokenName := fmt.Sprintf("generated-%s", time.Now().Format("20060102150405")) - u.logger.Debug(fmt.Sprintf("Token candidate name: %s", tokenName)) - - client := codefresh.New(&codefresh.ClientOptions{ - Auth: codefresh.AuthOptions{ - Token: opt.CodefreshToken, - }, - Host: opt.CodefreshHost, - }) - - token, err := client.Tokens().Create(tokenName, v["RuntimeEnvironment"].(string)) - if err != nil { - return nil, err - } - u.logger.Debug("Token created") - v["AgentToken"] = base64.StdEncoding.EncodeToString([]byte(token.Value)) - if err != nil { - return nil, err - } + u.logger.Debug("Generating token for agent") + tokenName := fmt.Sprintf("generated-%s", time.Now().Format("20060102150405")) + u.logger.Debug(fmt.Sprintf("Token candidate name: %s", tokenName)) + + client := codefresh.New(&codefresh.ClientOptions{ + Auth: codefresh.AuthOptions{ + Token: opt.CodefreshToken, + }, + Host: opt.CodefreshHost, + }) + + token, err := client.Tokens().Create(tokenName, v["RuntimeEnvironment"].(string)) + if err != nil { + return nil, err + } + u.logger.Debug("Token created") + v["AgentToken"] = base64.StdEncoding.EncodeToString([]byte(token.Value)) + if err != nil { + return nil, err } cs, err := opt.KubeBuilder.BuildClient() @@ -113,7 +111,7 @@ func (u *venonaPlugin) Delete(deleteOpt *DeleteOptions, v Values) error { matchPattern: venonaFilesPattern, operatorType: VenonaPluginType, } - return uninstall(opt) + return delete(opt) } func (u *venonaPlugin) Upgrade(opt *UpgradeOptions, v Values) (Values, error) { @@ -165,7 +163,7 @@ func (u *venonaPlugin) Upgrade(opt *UpgradeOptions, v Values) (Values, error) { matchPattern: fileName, operatorType: VenonaPluginType, } - err := uninstall(delOpt) + err := delete(delOpt) if err != nil { return nil, err } diff --git a/venonactl/pkg/plugins/volume-provisioner.go b/venonactl/pkg/plugins/volume-provisioner.go index c0af5164..e670ea00 100644 --- a/venonactl/pkg/plugins/volume-provisioner.go +++ b/venonactl/pkg/plugins/volume-provisioner.go @@ -73,7 +73,7 @@ func (u *volumeProvisionerPlugin) Delete(deleteOpt *DeleteOptions, v Values) err cs, err := deleteOpt.KubeBuilder.BuildClient() if err != nil { u.logger.Error(fmt.Sprintf("Cannot create kubernetes clientset: %v ", err)) - return err + return nil } opt := &deleteOptions{ templates: templates.TemplatesMap(), @@ -84,7 +84,7 @@ func (u *volumeProvisionerPlugin) Delete(deleteOpt *DeleteOptions, v Values) err matchPattern: volumeProvisionerFilesPattern, operatorType: VolumeProvisionerPluginType, } - return uninstall(opt) + return delete(opt) } func (u *volumeProvisionerPlugin) Upgrade(opt *UpgradeOptions, v Values) (Values, error) { diff --git a/venonactl/pkg/store/store.go b/venonactl/pkg/store/store.go index b501cc83..f29ffa36 100644 --- a/venonactl/pkg/store/store.go +++ b/venonactl/pkg/store/store.go @@ -28,8 +28,6 @@ type ( KubernetesAPI *KubernetesAPI - AgentAPI *AgentAPI - ClusterInCodefresh string DryRun bool @@ -55,11 +53,6 @@ type ( BuildNodeSelector map[string]string } - AgentAPI struct { - Token string - Id string - } - Image struct { Name string Tag string @@ -106,12 +99,9 @@ func (s *Values) BuildValues() map[string]interface{} { "Tag": "v18", }, "Namespace": s.KubernetesAPI.Namespace, - "ConfigPath": s.KubernetesAPI.ConfigPath, - "Context": s.KubernetesAPI.ContextName, "NodeSelector": s.KubernetesAPI.NodeSelector, - "Tolerations": s.KubernetesAPI.Tolerations, - "AgentToken": s.AgentAPI.Token, - "AgentId": s.AgentAPI.Id, + "Tolerations": s.KubernetesAPI.Tolerations, + "AgentToken": "", "ServerCert": map[string]string{ "Cert": "", "Key": "", diff --git a/venonactl/pkg/templates/kubernetes/deployment.venona.yaml b/venonactl/pkg/templates/kubernetes/deployment.venona.yaml index 19efb02b..4a156937 100644 --- a/venonactl/pkg/templates/kubernetes/deployment.venona.yaml +++ b/venonactl/pkg/templates/kubernetes/deployment.venona.yaml @@ -3,7 +3,7 @@ kind: Deployment metadata: labels: app: {{ .AppName }} - version: {{ .Version }} + version: {{ .Version }} name: {{ .AppName }} namespace: {{ .Namespace }} spec: @@ -24,10 +24,7 @@ spec: app: {{ .AppName }} version: {{ .Version }} spec: - volumes: - - name: venonaconf - secret: - secretName: venonaconf + serviceAccountName: {{ .AppName }} {{ if ne .NodeSelector "" }} nodeSelector: {{ .NodeSelector }} @@ -53,15 +50,7 @@ spec: value: {{ .Mode }} - name: AGENT_NAME value: {{ .AppName }} - - name: AGENT_ID - value: {{ .AgentId }} - - name: VENONA_CONFIG_DIR - value: "/etc/secrets" image: {{ .Image.Name }}:{{ .Image.Tag }} - volumeMounts: - - name: venonaconf - mountPath: "/etc/secrets" - readOnly: true imagePullPolicy: Always name: {{ .AppName }} restartPolicy: Always diff --git a/venonactl/pkg/templates/kubernetes/role-binding.re.yaml b/venonactl/pkg/templates/kubernetes/role-binding.venona.yaml similarity index 89% rename from venonactl/pkg/templates/kubernetes/role-binding.re.yaml rename to venonactl/pkg/templates/kubernetes/role-binding.venona.yaml index 2fec7de1..9ab2db99 100644 --- a/venonactl/pkg/templates/kubernetes/role-binding.re.yaml +++ b/venonactl/pkg/templates/kubernetes/role-binding.venona.yaml @@ -6,7 +6,6 @@ metadata: subjects: - kind: ServiceAccount name: {{ .AppName }} - namespace: {{ .Namespace }} roleRef: kind: Role name: {{ .AppName }} diff --git a/venonactl/pkg/templates/kubernetes/role.re.yaml b/venonactl/pkg/templates/kubernetes/role.venona.yaml similarity index 100% rename from venonactl/pkg/templates/kubernetes/role.re.yaml rename to venonactl/pkg/templates/kubernetes/role.venona.yaml diff --git a/venonactl/pkg/templates/kubernetes/secret.runtime-attach.yaml b/venonactl/pkg/templates/kubernetes/secret.runtime-attach.yaml deleted file mode 100644 index 2861f979..00000000 --- a/venonactl/pkg/templates/kubernetes/secret.runtime-attach.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: Secret -type: Opaque -metadata: - name: {{ .AppName }}conf - namespace: {{ .Namespace }} -data: -{{ range $key, $value := .venonaConf }} - {{ $key }}: {{ $value }} -{{ end }} \ No newline at end of file diff --git a/venonactl/pkg/templates/kubernetes/service-account.re.yaml b/venonactl/pkg/templates/kubernetes/service-account.venona.yaml similarity index 100% rename from venonactl/pkg/templates/kubernetes/service-account.re.yaml rename to venonactl/pkg/templates/kubernetes/service-account.venona.yaml diff --git a/venonactl/pkg/templates/kubernetes/templates.go b/venonactl/pkg/templates/kubernetes/templates.go index ff756228..29faab5d 100644 --- a/venonactl/pkg/templates/kubernetes/templates.go +++ b/venonactl/pkg/templates/kubernetes/templates.go @@ -232,7 +232,7 @@ kind: Deployment metadata: labels: app: {{ .AppName }} - version: {{ .Version }} + version: {{ .Version }} name: {{ .AppName }} namespace: {{ .Namespace }} spec: @@ -253,10 +253,7 @@ spec: app: {{ .AppName }} version: {{ .Version }} spec: - volumes: - - name: venonaconf - secret: - secretName: venonaconf + serviceAccountName: {{ .AppName }} {{ if ne .NodeSelector "" }} nodeSelector: {{ .NodeSelector }} @@ -264,7 +261,7 @@ spec: {{ if ne .Tolerations "" }} tolerations: {{ .Tolerations | indent 8 }} - {{ end }} + {{ end }} containers: - env: - name: SELF_DEPLOYMENT_NAME @@ -282,15 +279,7 @@ spec: value: {{ .Mode }} - name: AGENT_NAME value: {{ .AppName }} - - name: AGENT_ID - value: {{ .AgentId }} - - name: VENONA_CONFIG_DIR - value: "/etc/secrets" image: {{ .Image.Name }}:{{ .Image.Tag }} - volumeMounts: - - name: venonaconf - mountPath: "/etc/secrets" - readOnly: true imagePullPolicy: Always name: {{ .AppName }} restartPolicy: Always @@ -341,7 +330,7 @@ spec: ` - templatesMap["role-binding.re.yaml"] = `kind: RoleBinding + templatesMap["role-binding.venona.yaml"] = `kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: {{ .AppName }} @@ -349,13 +338,12 @@ metadata: subjects: - kind: ServiceAccount name: {{ .AppName }} - namespace: {{ .Namespace }} roleRef: kind: Role name: {{ .AppName }} apiGroup: rbac.authorization.k8s.io` - templatesMap["role.re.yaml"] = `kind: Role + templatesMap["role.venona.yaml"] = `kind: Role apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: {{ .AppName }} @@ -366,17 +354,6 @@ rules: verbs: ["get", "create", "delete"] ` - templatesMap["secret.runtime-attach.yaml"] = `apiVersion: v1 -kind: Secret -type: Opaque -metadata: - name: {{ .AppName }}conf - namespace: {{ .Namespace }} -data: -{{ range $key, $value := .venonaConf }} - {{ $key }}: {{ $value }} -{{ end }}` - templatesMap["secret.venona.yaml"] = `apiVersion: v1 kind: Secret type: Opaque @@ -402,7 +379,7 @@ metadata: name: engine namespace: {{ .Namespace }}` - templatesMap["service-account.re.yaml"] = `apiVersion: v1 + templatesMap["service-account.venona.yaml"] = `apiVersion: v1 kind: ServiceAccount metadata: name: {{ .AppName }} @@ -420,16 +397,5 @@ parameters: volumeBackend: local ` - templatesMap["venonaconf.secret.venona.yaml"] = `apiVersion: v1 -kind: Secret -type: Opaque -metadata: - name: {{ .AppName }}conf - namespace: {{ .Namespace }} -data: -{{ range $key, $value := .venonaConf }} - {{ $key }}: {{ $value }} -{{ end }}` - return templatesMap } diff --git a/venonactl/pkg/templates/kubernetes/venonaconf.secret.venona.yaml b/venonactl/pkg/templates/kubernetes/venonaconf.secret.venona.yaml deleted file mode 100644 index 2861f979..00000000 --- a/venonactl/pkg/templates/kubernetes/venonaconf.secret.venona.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: v1 -kind: Secret -type: Opaque -metadata: - name: {{ .AppName }}conf - namespace: {{ .Namespace }} -data: -{{ range $key, $value := .venonaConf }} - {{ $key }}: {{ $value }} -{{ end }} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 4282fdc6..337a5c6d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2339,14 +2339,6 @@ js-yaml@^3.10.0, js-yaml@^3.12.0, js-yaml@^3.7.0: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^3.13.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -2557,11 +2549,6 @@ lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5 version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" -lodash@^4.17.15: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== - long-timeout@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/long-timeout/-/long-timeout-0.1.1.tgz#9721d788b47e0bcb5a24c2e2bee1a0da55dab514"