diff --git a/.circleci/config.yml b/.circleci/config.yml index 9d907fac..908b76db 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,4 +1,9 @@ -version: 2 +version: 2.1 + +executors: + vm: + machine: + enabled: true references: set_environment_variables: &set_environment_variables @@ -10,18 +15,33 @@ references: echo 'export CI_BUILD_NUM=$CIRCLE_BUILD_NUM' >> ${BASH_ENV} echo 'export CI_TAG=$CIRCLE_TAG' >> ${BASH_ENV} echo 'export PUSH_ALL_VERSION_TAGS=true' >> ${BASH_ENV} - docker_build_and_push: &docker_build_and_push + install_k8s: &install_k8s run: - name: Docker login, build, and push + name: Install K8s command: | - docker-pull -f .circleci/build.config - docker-build -f .circleci/build.config - if [[ -z $CIRCLE_PR_NUMBER ]]; then - docker login quay.io -u="reactiveops+circleci" -p="${quay_token}" - docker-push -f .circleci/build.config - else - echo "Skipping docker push for forked PR" - fi + echo "Installing git and jq" + sudo apt-get install -yqq jq git + + echo "Installing KIND" + curl -sLO https://github.com/kubernetes-sigs/kind/releases/download/0.2.1/kind-linux-amd64 + chmod 0755 kind-linux-amd64 + sudo mv kind-linux-amd64 /usr/local/bin/kind + kind version + + echo "Installing Kubectl" + curl -sLO https://storage.googleapis.com/kubernetes-release/release/v1.12.7/bin/linux/amd64/kubectl + chmod 0755 kubectl + sudo mv kubectl /usr/local/bin/ + kubectl version --client + + + echo "Creating Kubernetes Cluster with Kind" + kind create cluster --wait=90s + docker ps -a + + echo "Setting up kubecfg" + cp $(kind get kubeconfig-path --name=kind) ~/.kube/config + kubectl version # Test scripts update_coverage: &update_coverage @@ -34,7 +54,7 @@ references: else echo "Skipping coverage for forked PR" fi - test_dashboard: &test_dashboard + test_binary_dashboard: &test_binary_dashboard run: name: Test Dashboard command: | @@ -46,6 +66,20 @@ references: curl -f http://localhost:3000/static/css/main.css > /dev/null curl -f http://localhost:3000/results.json > /dev/null curl -f http://localhost:3000/details/security > /dev/null + test_kube_dashboard: &test_kube_dashboard + run: + name: Test Dashboard + command: | + kubectl apply -f ./deploy/dashboard.yaml + sleep 10 + kubectl port-forward --namespace polaris svc/polaris-dashboard 3000:80 & + sleep 5 + curl -f http://localhost:3000 > /dev/null + curl -f http://localhost:3000/health > /dev/null + curl -f http://localhost:3000/favicon.ico > /dev/null + curl -f http://localhost:3000/static/css/main.css > /dev/null + curl -f http://localhost:3000/results.json > /dev/null + curl -f http://localhost:3000/details/security > /dev/null # Release scripts install_goreleaser: &install_goreleaser @@ -56,20 +90,26 @@ references: echo "8dbad6683d6fc9367e637e6eed8e01a0d63c9660 goreleaser.deb" | sha1sum -c sudo dpkg -i goreleaser.deb rm goreleaser.deb + docker_build_and_push: &docker_build_and_push + run: + name: Docker login, build, and push + command: | + docker-pull -f .circleci/build.config + docker-build -f .circleci/build.config + if [[ -z $CIRCLE_PR_NUMBER ]]; then + docker login quay.io -u="reactiveops+circleci" -p="${quay_token}" + docker-push -f .circleci/build.config + else + echo "Skipping docker push for forked PR" + fi release_deploy_configs: &release_deploy_configs run: name: Release deploy configs command: | - git clone --branch polaris-latest https://github.com/reactiveops/charts - mkdir deploy - helm template ./charts/stable/polaris --name polaris --namespace polaris --set templateOnly=true > deploy/dashboard.yaml - helm template ./charts/stable/polaris --name polaris --namespace polaris --set templateOnly=true --set webhook.enable=true --set dashboard.enable=false > deploy/webhook.yaml upload_url=$(curl --silent https://api.github.com/repos/reactiveops/polaris/releases/latest | grep upload_url) upload_url=$(echo $upload_url | sed -e 's/.*\(https.*\){.*$/\1/') curl -X POST "$upload_url?name=dashboard.yaml" --data-binary "@./deploy/dashboard.yaml" -H "Authorization: Bearer $GITHUB_TOKEN" -H "Content-Type: application/x-yaml" curl -X POST "$upload_url?name=webhook.yaml" --data-binary "@./deploy/webhook.yaml" -H "Authorization: Bearer $GITHUB_TOKEN" -H "Content-Type: application/x-yaml" - rm -rf ./charts - rm -rf ./deploy jobs: build: @@ -81,18 +121,26 @@ jobs: - *set_environment_variables - *docker_build_and_push + test_k8s: + working_directory: ~/polaris + resource_class: medium + executor: vm + steps: + - checkout + - *install_k8s + - *test_kube_dashboard + test: working_directory: /go/src/github.com/reactiveops/polaris/ docker: - image: circleci/golang:1.12 - steps: - checkout - run: go get -u golang.org/x/lint/golint - run: go list ./... | grep -v vendor | xargs golint -set_exit_status - run: go list ./... | grep -v vendor | xargs go vet - *update_coverage - - *test_dashboard + - *test_binary_dashboard release_binary: working_directory: /go/src/github.com/reactiveops/polaris/ @@ -106,6 +154,7 @@ jobs: - run: go get -u github.com/gobuffalo/packr/v2/packr2 - run: packr2 - run: goreleaser + - *release_deploy_configs release_images: working_directory: /go/src/github.com/reactiveops/polaris/ @@ -116,13 +165,13 @@ jobs: - setup_remote_docker - *set_environment_variables - *docker_build_and_push - - *release_deploy_configs workflows: version: 2 build: jobs: - test + - test_k8s - build: requires: - test diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d1e40f1b..0f67378f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,33 +58,53 @@ Each new pull request should: ## Creating a new release -### Minor/patch releases -Minor and patch releases only need to change this repo. The Helm chart and deploy scripts +### Patch releases +Patch releases only need to change this repo. The Helm chart and deploy scripts will automatically pull in the latest changes. -To deploy a minor or patch release, follow steps 2 and 3 from "Major releases" below. +If the release involves changes to anything in the `deploy/` folder (e.g. new RBAC permissions), +it needs to be a minor or major release in order to prevent breaking the Helm chart. -### Major releases -Major releases need to change both this repository and the -[Helm chart repo](https://github.com/reactiveops/charts/). - -The steps are: -1. Create a PR in the [charts repo](https://github.com/reactiveops/charts/) - 1. Use a branch named `polaris-latest` - 2. Bump the version number in: - 1. stable/polaris/README.md - 2. stable/polaris/Chart.yaml - 3. stable/polaris/values.yaml - 3. **Don't merge yet!** -2. Create a PR for this repo +1. Create a PR for this repo 1. Bump the version number in: 1. main.go 2. README.md 2. Update CHANGELOG.md 3. Merge your PR +2. Tag the latest branch for this repo + 1. Pull the latest commit for the `master` branch (which you just merged in your PR) + 2. Run `git tag $VERSION && git push --tags` + 3. Make sure CircleCI runs successfully for the new tag - this will push images to quay.io and create a release in GitHub + 1. If CircleCI fails, check with Codeowners ASAP + +### Minor/Major releases +Minor and major releases need to change both this repository and the +[Helm chart repo](https://github.com/reactiveops/charts/). + +The steps are: +1. Modify the [Helm chart](https://github.com/reactiveops/charts/stable/polaris) + 1. Clone the helm charts repo + 1. `git clone https://github.com/reactiveops/charts` + 2. `git checkout -b yourname/update-polaris` + 1. Bump the version number in: + 1. stable/polaris/README.md + 2. stable/polaris/Chart.yaml + 3. stable/polaris/values.yaml + 2. Make any necessary changes to the chart to support the new version of Polaris (e.g. new RBAC permissions) + 3. **Don't merge yet!** +2. Create a PR for this repo + 1. Bump the version number in: + 1. main.go + 2. README.md + 2. Regenerate the deployment files. Assuming you've cloned the charts repo to `./charts`: + 1. `helm template ./charts/stable/polaris/ --name polaris --namespace polaris --set templateOnly=true > deploy/dashboard.yaml` + 2. `helm template ./charts/stable/polaris/ --name polaris --namespace polaris --set templateOnly=true --set webhook.enable=true --set dashboard.enable=false > deploy/webhook.yaml` + 3. Update CHANGELOG.md + 4. Merge your PR 3. Tag the latest branch for this repo 1. Pull the latest for the `master` branch 2. Run `git tag $VERSION && git push --tags` - 3. Wait for CircleCI to finish the build for the tag, which pushes images to quay.io and creates a release in github -4. Merge the PR for the charts repo you created in step 1. + 3. Make sure CircleCI runs successfully for the new tag - this will push images to quay.io and create a release in GitHub + 1. If CircleCI fails, check with Codeowners ASAP +4. Create and merge a PR for your changes to the Helm chart diff --git a/deploy/dashboard.yaml b/deploy/dashboard.yaml new file mode 100644 index 00000000..a70c0b6f --- /dev/null +++ b/deploy/dashboard.yaml @@ -0,0 +1,216 @@ +--- +# Source: polaris/templates/0-namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: polaris +--- +# Source: polaris/templates/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: polaris + namespace: polaris + labels: + app: polaris +data: + config.yaml: | + resources: + cpuRequestsMissing: warning + cpuLimitsMissing: warning + memoryRequestsMissing: warning + memoryLimitsMissing: warning + images: + tagNotSpecified: error + healthChecks: + readinessProbeMissing: warning + livenessProbeMissing: warning + networking: + hostNetworkSet: warning + hostPortSet: warning + security: + hostIPCSet: error + hostPIDSet: error + notReadOnlyRootFileSystem: warning + privilegeEscalationAllowed: error + runAsRootAllowed: warning + runAsPrivileged: error + capabilities: + error: + ifAnyAdded: + - SYS_ADMIN + - NET_ADMIN + - ALL + warning: + ifAnyAddedBeyond: + - CHOWN + - DAC_OVERRIDE + - FSETID + - FOWNER + - MKNOD + - NET_RAW + - SETGID + - SETUID + - SETFCAP + - SETPCAP + - NET_BIND_SERVICE + - SYS_CHROOT + - KILL + - AUDIT_WRITE + +--- +# Source: polaris/templates/dashboard.rbac.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: polaris-dashboard + namespace: polaris + labels: + app: polaris +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: polaris-dashboard + labels: + app: polaris +rules: + - apiGroups: + - 'apps' + - 'extensions' + resources: + - 'deployments' + - 'statefulsets' + verbs: + - 'get' + - 'list' + - apiGroups: + - '' + resources: + - 'nodes' + - 'namespaces' + - 'pods' + verbs: + - 'get' + - 'list' +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: polaris-dashboard + labels: + app: polaris +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: polaris-dashboard +subjects: + - kind: ServiceAccount + name: polaris-dashboard + namespace: polaris +--- +# Source: polaris/templates/dashboard.service.yaml +apiVersion: v1 +kind: Service +metadata: + name: polaris-dashboard + namespace: polaris + labels: + app: polaris +spec: + ports: + - name: dashboard + port: 80 + protocol: TCP + targetPort: 8080 + selector: + app: polaris + component: dashboard + type: ClusterIP +--- +# Source: polaris/templates/dashboard.deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + checksum/config: '6ec4a4dc87403cae67c01438398a5f1d4ef836ffeaf26a33b685c066b940495a' + name: polaris-dashboard + namespace: polaris + labels: + app: polaris + component: dashboard +spec: + replicas: 1 + selector: + matchLabels: + app: polaris + component: dashboard + template: + metadata: + labels: + app: polaris + component: dashboard + spec: + volumes: + - name: config + configMap: + name: polaris + containers: + - command: + - polaris + - --dashboard + - --config + - /opt/app/config.yaml + image: 'quay.io/reactiveops/polaris:0.2' + imagePullPolicy: 'Always' + name: dashboard + ports: + - containerPort: 8080 + livenessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 20 + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi + securityContext: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: true + capabilities: + drop: + - ALL + volumeMounts: + - name: config + mountPath: /opt/app/config.yaml + subPath: config.yaml + readOnly: true + serviceAccountName: polaris-dashboard +--- +# Source: polaris/templates/ingress.yaml + +--- +# Source: polaris/templates/webhook.deployment.yaml + +--- +# Source: polaris/templates/webhook.rbac.yaml + +--- +# Source: polaris/templates/webhook.secret.yaml + +--- +# Source: polaris/templates/webhook.service.yaml + diff --git a/deploy/webhook.yaml b/deploy/webhook.yaml new file mode 100644 index 00000000..910666a6 --- /dev/null +++ b/deploy/webhook.yaml @@ -0,0 +1,284 @@ +--- +# Source: polaris/templates/0-namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: polaris +--- +# Source: polaris/templates/webhook.secret.yaml +apiVersion: v1 +kind: Secret +metadata: + name: polaris-webhook + namespace: polaris + labels: + app: polaris +type: Opaque +stringData: + cert.pem: '' +--- +# Source: polaris/templates/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: polaris + namespace: polaris + labels: + app: polaris +data: + config.yaml: | + resources: + cpuRequestsMissing: warning + cpuLimitsMissing: warning + memoryRequestsMissing: warning + memoryLimitsMissing: warning + images: + tagNotSpecified: error + healthChecks: + readinessProbeMissing: warning + livenessProbeMissing: warning + networking: + hostNetworkSet: warning + hostPortSet: warning + security: + hostIPCSet: error + hostPIDSet: error + notReadOnlyRootFileSystem: warning + privilegeEscalationAllowed: error + runAsRootAllowed: warning + runAsPrivileged: error + capabilities: + error: + ifAnyAdded: + - SYS_ADMIN + - NET_ADMIN + - ALL + warning: + ifAnyAddedBeyond: + - CHOWN + - DAC_OVERRIDE + - FSETID + - FOWNER + - MKNOD + - NET_RAW + - SETGID + - SETUID + - SETFCAP + - SETPCAP + - NET_BIND_SERVICE + - SYS_CHROOT + - KILL + - AUDIT_WRITE + +--- +# Source: polaris/templates/webhook.rbac.yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: polaris-webhook + namespace: polaris + labels: + app: polaris +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRole +metadata: + name: polaris-webhook + labels: + app: polaris +rules: + - apiGroups: + - 'apps' + - 'extensions' + resources: + - 'deployments' + - 'statefulsets' + verbs: + - 'get' + - 'list' + # required by controller-runtime code doing a cluster wide lookup + # when it seems namespace would suffice + - apiGroups: + - '' + resources: + - 'secrets' + - 'services' + verbs: + - 'get' + - 'list' + - 'watch' + - apiGroups: + - 'admissionregistration.k8s.io' + resources: + - 'validatingwebhookconfigurations' + verbs: + - '*' +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: polaris-webhook + labels: + app: polaris +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: polaris-webhook +subjects: + - kind: ServiceAccount + name: polaris-webhook + namespace: polaris +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: Role +metadata: + name: polaris-webhook + namespace: polaris + labels: + app: polaris +rules: + # required for current controller-runtime bootstrap method + - apiGroups: + - '' + resources: + - 'secrets' + - 'services' + verbs: + - '*' +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: polaris-webhook + namespace: polaris + labels: + app: polaris +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: polaris-webhook +subjects: + - kind: ServiceAccount + name: polaris-webhook + namespace: polaris +--- +# Source: polaris/templates/webhook.service.yaml +apiVersion: v1 +kind: Service +metadata: + name: polaris-webhook + namespace: polaris + labels: + app: polaris +spec: + ports: + - name: webhook + port: 443 + protocol: TCP + targetPort: 9876 + selector: + app: polaris + component: webhook + type: ClusterIP +--- +# Source: polaris/templates/webhook.deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + checksum/config: '6ec4a4dc87403cae67c01438398a5f1d4ef836ffeaf26a33b685c066b940495a' + name: polaris-webhook + namespace: polaris + labels: + app: polaris + component: webhook +spec: + replicas: 1 + selector: + matchLabels: + app: polaris + component: webhook + template: + metadata: + labels: + app: polaris + component: webhook + spec: + containers: + - name: webhook + command: + - polaris + - --webhook + - --config + - /opt/app/config.yaml + image: 'quay.io/reactiveops/polaris:0.2' + imagePullPolicy: 'Always' + ports: + - containerPort: 9876 + # These are fairly useless readiness/liveness probes for now + # Follow this issue for potential improvements: + # https://github.com/kubernetes-sigs/controller-runtime/issues/356 + livenessProbe: + exec: + command: + - sh + - -c + - ps -ef | grep polaris + initialDelaySeconds: 5 + periodSeconds: 5 + readinessProbe: + exec: + command: + - sh + - -c + - ps -ef | grep polaris + initialDelaySeconds: 5 + periodSeconds: 5 + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi + securityContext: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: true + capabilities: + drop: + - ALL + volumeMounts: + - name: config + mountPath: /opt/app/config.yaml + subPath: config.yaml + readOnly: true + - name: secret + mountPath: /opt/cert/ + readOnly: true + - name: cr-logs + mountPath: /tmp/ + readOnly: false + serviceAccountName: polaris-webhook + volumes: + - name: config + configMap: + name: polaris + - name: secret + secret: + secretName: polaris-webhook + - name: cr-logs + emptyDir: {} +--- +# Source: polaris/templates/dashboard.deployment.yaml + +--- +# Source: polaris/templates/dashboard.rbac.yaml + +--- +# Source: polaris/templates/dashboard.service.yaml + +--- +# Source: polaris/templates/ingress.yaml +