{"action":"created","comment":{"url":"https://api.github.com/repos/zendesk/samson/pulls/comments/61461558","id":61461558,"diff_hunk":"@@ -0,0 +1,128 @@\n+# executes a deploy and writes log to job output\n+# finishes when cluster is \"Ready\"\n+module Kubernetes\n+  class DeployExecutor\n+    def initialize(output, job:)\n+      @output = output\n+      @job = job\n+    end\n+\n+    def pid\n+      \"Kubernetes-deploy-#{object_id}\"\n+    end\n+\n+    def stop!(_signal)\n+      @stopped = true\n+    end\n+\n+    def execute!(*_commands)\n+      build = find_build\n+      release = create_release(build)\n+      ensure_service(release)\n+      create_deploys(release)\n+\n+      # Wait until deploys are done and show progress\n+      loop do\n+        if @stopped # TODO: write test ... when user clicks stop, this needs to stop\n+          @output.puts \"STOPPED\"\n+          return false\n+        end\n+\n+        status = release.release_docs.map { |release_doc| pod_is_ready?(release_doc) }\n+        if status.all?\n+          @output.puts \"SUCCESS\"\n+          return true\n+        end\n+\n+        sleep 2\n+      end\n+    end\n+\n+    private\n+\n+    # TODO only call once per cluster and filter the output\n+    def pod_is_ready?(release_doc)\n+      group = release_doc.deploy_group\n+      role = release_doc.kubernetes_role.name\n+      pod = group.kubernetes_cluster.client.get_pods(\n+        namespace: group.kubernetes_namespace,\n+        label_selector: {\n+          deploy_group_id: group.id,\n+          project_id: @job.project_id,\n+          release_id: release_doc.kubernetes_release_id,\n+          role: role\n+        }.map { |k, v| \"#{k}=#{v}\" }.join(',')\n+      ).first\n+\n+      phase, conditions = analyze_pod_status(pod)\n+      @output.puts \"#{group.name} #{role}: #{phase} - #{conditions}\"\n+      phase == \"Running\"\n+    end\n+\n+    # find the docker build we are deploying\n+    # TODO: create it and wait ... also show creation logs etc\n+    def find_build\n+      if build = Build.find_by_git_sha(@job.commit)\n+        @output.puts(\"Found build #{build.id} for sha #{@job.commit}\")\n+      else\n+        raise Samson::Hooks::UserError, \"Build for sha #{@job.commit} does not exist, create it before deploying\"\n+      end\n+      build\n+    end\n+\n+    # create a realese, storing all the configuration\n+    def create_release(build)\n+      # build config for every cluster and role we want to deploy to\n+      group_config = @job.deploy.stage.deploy_groups.map do |group|\n+        # raise \"#{group.name} needs to be on kubernetes\" unless group.\n+        roles = Kubernetes::Role.where(project_id: @job.project_id).map do |role|\n+          {id: role.id, replicas: role.replicas} # TODO make replicas configureable\n+        end\n+        {id: group.id, roles: roles}\n+      end\n+\n+      release = Kubernetes::Release.create_release(deploy_groups: group_config, build_id: build.id, user: @job.user)\n+      raise Samson::Hooks::UserError, release.errors.full_messages.inspect unless release.persisted?\n+      @output.puts(\"Created release #{release.id}\\nConfig: #{group_config.inspect}\")\n+      release\n+    end\n+\n+    def analyze_pod_status(pod)\n+      if pod\n+        conditions = pod.status.conditions || []\n+\n+        phase = if pod.status.phase == \"Running\"","path":"plugins/kubernetes/app/models/kubernetes/deploy_executor.rb","position":94,"original_position":94,"commit_id":"bf9bbb1523726aab8d9dc17112922ed9cd8d8771","original_commit_id":"bf9bbb1523726aab8d9dc17112922ed9cd8d8771","user":{"login":"jonmoter","id":1056506,"avatar_url":"https://avatars.githubusercontent.com/u/1056506?v=3","gravatar_id":"","url":"https://api.github.com/users/jonmoter","html_url":"https://github.com/jonmoter","followers_url":"https://api.github.com/users/jonmoter/followers","following_url":"https://api.github.com/users/jonmoter/following{/other_user}","gists_url":"https://api.github.com/users/jonmoter/gists{/gist_id}","starred_url":"https://api.github.com/users/jonmoter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jonmoter/subscriptions","organizations_url":"https://api.github.com/users/jonmoter/orgs","repos_url":"https://api.github.com/users/jonmoter/repos","events_url":"https://api.github.com/users/jonmoter/events{/privacy}","received_events_url":"https://api.github.com/users/jonmoter/received_events","type":"User","site_admin":false},"body":"Use Kubernetes::Api::Pod class to contain logic","created_at":"2016-04-28T16:45:19Z","updated_at":"2016-04-28T16:45:19Z","html_url":"https://github.com/zendesk/samson/pull/891#discussion_r61461558","pull_request_url":"https://api.github.com/repos/zendesk/samson/pulls/891","_links":{"self":{"href":"https://api.github.com/repos/zendesk/samson/pulls/comments/61461558"},"html":{"href":"https://github.com/zendesk/samson/pull/891#discussion_r61461558"},"pull_request":{"href":"https://api.github.com/repos/zendesk/samson/pulls/891"}}},"pull_request":{"url":"https://api.github.com/repos/zendesk/samson/pulls/891","id":67976433,"html_url":"https://github.com/zendesk/samson/pull/891","diff_url":"https://github.com/zendesk/samson/pull/891.diff","patch_url":"https://github.com/zendesk/samson/pull/891.patch","issue_url":"https://api.github.com/repos/zendesk/samson/issues/891","number":891,"state":"open","locked":false,"title":"deploy kubernetes via regular deploy workflow","user":{"login":"grosser","id":11367,"avatar_url":"https://avatars.githubusercontent.com/u/11367?v=3","gravatar_id":"","url":"https://api.github.com/users/grosser","html_url":"https://github.com/grosser","followers_url":"https://api.github.com/users/grosser/followers","following_url":"https://api.github.com/users/grosser/following{/other_user}","gists_url":"https://api.github.com/users/grosser/gists{/gist_id}","starred_url":"https://api.github.com/users/grosser/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/grosser/subscriptions","organizations_url":"https://api.github.com/users/grosser/orgs","repos_url":"https://api.github.com/users/grosser/repos","events_url":"https://api.github.com/users/grosser/events{/privacy}","received_events_url":"https://api.github.com/users/grosser/received_events","type":"User","site_admin":false},"body":"completely working and all tested ... erm or not ;)\r\n\r\n/cc @zendesk/samson\r\n\r\n - fetch build from sha that was entered or fail\r\n - listens to updates sent to the SSE stream and show them in the output\r\n - discussion: should we just create the build while doing this / why do we need to manage builds manually ...\r\n\r\nTODO:\r\n - [X] nicer logging\r\n - [X] stop when done / wait till it is done\r\n - [x] use proper plugin hooks\r\n - [x] make sure pid is updated in the UI when switching over\r\n - [X] read deploy status from k8s-log\r\n - [ ] test everything\r\n - [X] log release errors when it fails to create\r\n - [X] log build not found error\r\n - [ ] override or configure roles\r\n - [x] kubernetes boolean for deploys\r\n - [ ]  validate that all deploy groups have kubernetes cluster when saving stage\r\n\r\n### Risks\r\n- Medium: breaking kubernetes deploys\r\n- Medium: breaking normal deploys\r\n- Medium: introducing some bad GC that makes samson grow\r\n\r\n","created_at":"2016-04-26T23:49:34Z","updated_at":"2016-04-28T16:45:19Z","closed_at":null,"merged_at":null,"merge_commit_sha":"569b155481aff012ae28cbe4168186eedaeaae7c","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/zendesk/samson/pulls/891/commits","review_comments_url":"https://api.github.com/repos/zendesk/samson/pulls/891/comments","review_comment_url":"https://api.github.com/repos/zendesk/samson/pulls/comments{/number}","comments_url":"https://api.github.com/repos/zendesk/samson/issues/891/comments","statuses_url":"https://api.github.com/repos/zendesk/samson/statuses/bf9bbb1523726aab8d9dc17112922ed9cd8d8771","head":{"label":"zendesk:grosser/kuploy","ref":"grosser/kuploy","sha":"bf9bbb1523726aab8d9dc17112922ed9cd8d8771","user":{"login":"zendesk","id":11525,"avatar_url":"https://avatars.githubusercontent.com/u/11525?v=3","gravatar_id":"","url":"https://api.github.com/users/zendesk","html_url":"https://github.com/zendesk","followers_url":"https://api.github.com/users/zendesk/followers","following_url":"https://api.github.com/users/zendesk/following{/other_user}","gists_url":"https://api.github.com/users/zendesk/gists{/gist_id}","starred_url":"https://api.github.com/users/zendesk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zendesk/subscriptions","organizations_url":"https://api.github.com/users/zendesk/orgs","repos_url":"https://api.github.com/users/zendesk/repos","events_url":"https://api.github.com/users/zendesk/events{/privacy}","received_events_url":"https://api.github.com/users/zendesk/received_events","type":"Organization","site_admin":false},"repo":{"id":20208662,"name":"samson","full_name":"zendesk/samson","owner":{"login":"zendesk","id":11525,"avatar_url":"https://avatars.githubusercontent.com/u/11525?v=3","gravatar_id":"","url":"https://api.github.com/users/zendesk","html_url":"https://github.com/zendesk","followers_url":"https://api.github.com/users/zendesk/followers","following_url":"https://api.github.com/users/zendesk/following{/other_user}","gists_url":"https://api.github.com/users/zendesk/gists{/gist_id}","starred_url":"https://api.github.com/users/zendesk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zendesk/subscriptions","organizations_url":"https://api.github.com/users/zendesk/orgs","repos_url":"https://api.github.com/users/zendesk/repos","events_url":"https://api.github.com/users/zendesk/events{/privacy}","received_events_url":"https://api.github.com/users/zendesk/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/zendesk/samson","description":"Web interface for deployments","fork":false,"url":"https://api.github.com/repos/zendesk/samson","forks_url":"https://api.github.com/repos/zendesk/samson/forks","keys_url":"https://api.github.com/repos/zendesk/samson/keys{/key_id}","collaborators_url":"https://api.github.com/repos/zendesk/samson/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/zendesk/samson/teams","hooks_url":"https://api.github.com/repos/zendesk/samson/hooks","issue_events_url":"https://api.github.com/repos/zendesk/samson/issues/events{/number}","events_url":"https://api.github.com/repos/zendesk/samson/events","assignees_url":"https://api.github.com/repos/zendesk/samson/assignees{/user}","branches_url":"https://api.github.com/repos/zendesk/samson/branches{/branch}","tags_url":"https://api.github.com/repos/zendesk/samson/tags","blobs_url":"https://api.github.com/repos/zendesk/samson/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/zendesk/samson/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/zendesk/samson/git/refs{/sha}","trees_url":"https://api.github.com/repos/zendesk/samson/git/trees{/sha}","statuses_url":"https://api.github.com/repos/zendesk/samson/statuses/{sha}","languages_url":"https://api.github.com/repos/zendesk/samson/languages","stargazers_url":"https://api.github.com/repos/zendesk/samson/stargazers","contributors_url":"https://api.github.com/repos/zendesk/samson/contributors","subscribers_url":"https://api.github.com/repos/zendesk/samson/subscribers","subscription_url":"https://api.github.com/repos/zendesk/samson/subscription","commits_url":"https://api.github.com/repos/zendesk/samson/commits{/sha}","git_commits_url":"https://api.github.com/repos/zendesk/samson/git/commits{/sha}","comments_url":"https://api.github.com/repos/zendesk/samson/comments{/number}","issue_comment_url":"https://api.github.com/repos/zendesk/samson/issues/comments{/number}","contents_url":"https://api.github.com/repos/zendesk/samson/contents/{+path}","compare_url":"https://api.github.com/repos/zendesk/samson/compare/{base}...{head}","merges_url":"https://api.github.com/repos/zendesk/samson/merges","archive_url":"https://api.github.com/repos/zendesk/samson/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/zendesk/samson/downloads","issues_url":"https://api.github.com/repos/zendesk/samson/issues{/number}","pulls_url":"https://api.github.com/repos/zendesk/samson/pulls{/number}","milestones_url":"https://api.github.com/repos/zendesk/samson/milestones{/number}","notifications_url":"https://api.github.com/repos/zendesk/samson/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/zendesk/samson/labels{/name}","releases_url":"https://api.github.com/repos/zendesk/samson/releases{/id}","deployments_url":"https://api.github.com/repos/zendesk/samson/deployments","created_at":"2014-05-27T05:55:57Z","updated_at":"2016-04-27T09:18:32Z","pushed_at":"2016-04-28T02:50:56Z","git_url":"git://github.com/zendesk/samson.git","ssh_url":"git@github.com:zendesk/samson.git","clone_url":"https://github.com/zendesk/samson.git","svn_url":"https://github.com/zendesk/samson","homepage":null,"size":174526,"stargazers_count":659,"watchers_count":659,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":110,"mirror_url":null,"open_issues_count":54,"forks":110,"open_issues":54,"watchers":659,"default_branch":"master"}},"base":{"label":"zendesk:master","ref":"master","sha":"079f16bb20edc8b6eef80f200fd4085ecc7d3c09","user":{"login":"zendesk","id":11525,"avatar_url":"https://avatars.githubusercontent.com/u/11525?v=3","gravatar_id":"","url":"https://api.github.com/users/zendesk","html_url":"https://github.com/zendesk","followers_url":"https://api.github.com/users/zendesk/followers","following_url":"https://api.github.com/users/zendesk/following{/other_user}","gists_url":"https://api.github.com/users/zendesk/gists{/gist_id}","starred_url":"https://api.github.com/users/zendesk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zendesk/subscriptions","organizations_url":"https://api.github.com/users/zendesk/orgs","repos_url":"https://api.github.com/users/zendesk/repos","events_url":"https://api.github.com/users/zendesk/events{/privacy}","received_events_url":"https://api.github.com/users/zendesk/received_events","type":"Organization","site_admin":false},"repo":{"id":20208662,"name":"samson","full_name":"zendesk/samson","owner":{"login":"zendesk","id":11525,"avatar_url":"https://avatars.githubusercontent.com/u/11525?v=3","gravatar_id":"","url":"https://api.github.com/users/zendesk","html_url":"https://github.com/zendesk","followers_url":"https://api.github.com/users/zendesk/followers","following_url":"https://api.github.com/users/zendesk/following{/other_user}","gists_url":"https://api.github.com/users/zendesk/gists{/gist_id}","starred_url":"https://api.github.com/users/zendesk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zendesk/subscriptions","organizations_url":"https://api.github.com/users/zendesk/orgs","repos_url":"https://api.github.com/users/zendesk/repos","events_url":"https://api.github.com/users/zendesk/events{/privacy}","received_events_url":"https://api.github.com/users/zendesk/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/zendesk/samson","description":"Web interface for deployments","fork":false,"url":"https://api.github.com/repos/zendesk/samson","forks_url":"https://api.github.com/repos/zendesk/samson/forks","keys_url":"https://api.github.com/repos/zendesk/samson/keys{/key_id}","collaborators_url":"https://api.github.com/repos/zendesk/samson/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/zendesk/samson/teams","hooks_url":"https://api.github.com/repos/zendesk/samson/hooks","issue_events_url":"https://api.github.com/repos/zendesk/samson/issues/events{/number}","events_url":"https://api.github.com/repos/zendesk/samson/events","assignees_url":"https://api.github.com/repos/zendesk/samson/assignees{/user}","branches_url":"https://api.github.com/repos/zendesk/samson/branches{/branch}","tags_url":"https://api.github.com/repos/zendesk/samson/tags","blobs_url":"https://api.github.com/repos/zendesk/samson/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/zendesk/samson/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/zendesk/samson/git/refs{/sha}","trees_url":"https://api.github.com/repos/zendesk/samson/git/trees{/sha}","statuses_url":"https://api.github.com/repos/zendesk/samson/statuses/{sha}","languages_url":"https://api.github.com/repos/zendesk/samson/languages","stargazers_url":"https://api.github.com/repos/zendesk/samson/stargazers","contributors_url":"https://api.github.com/repos/zendesk/samson/contributors","subscribers_url":"https://api.github.com/repos/zendesk/samson/subscribers","subscription_url":"https://api.github.com/repos/zendesk/samson/subscription","commits_url":"https://api.github.com/repos/zendesk/samson/commits{/sha}","git_commits_url":"https://api.github.com/repos/zendesk/samson/git/commits{/sha}","comments_url":"https://api.github.com/repos/zendesk/samson/comments{/number}","issue_comment_url":"https://api.github.com/repos/zendesk/samson/issues/comments{/number}","contents_url":"https://api.github.com/repos/zendesk/samson/contents/{+path}","compare_url":"https://api.github.com/repos/zendesk/samson/compare/{base}...{head}","merges_url":"https://api.github.com/repos/zendesk/samson/merges","archive_url":"https://api.github.com/repos/zendesk/samson/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/zendesk/samson/downloads","issues_url":"https://api.github.com/repos/zendesk/samson/issues{/number}","pulls_url":"https://api.github.com/repos/zendesk/samson/pulls{/number}","milestones_url":"https://api.github.com/repos/zendesk/samson/milestones{/number}","notifications_url":"https://api.github.com/repos/zendesk/samson/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/zendesk/samson/labels{/name}","releases_url":"https://api.github.com/repos/zendesk/samson/releases{/id}","deployments_url":"https://api.github.com/repos/zendesk/samson/deployments","created_at":"2014-05-27T05:55:57Z","updated_at":"2016-04-27T09:18:32Z","pushed_at":"2016-04-28T02:50:56Z","git_url":"git://github.com/zendesk/samson.git","ssh_url":"git@github.com:zendesk/samson.git","clone_url":"https://github.com/zendesk/samson.git","svn_url":"https://github.com/zendesk/samson","homepage":null,"size":174526,"stargazers_count":659,"watchers_count":659,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":110,"mirror_url":null,"open_issues_count":54,"forks":110,"open_issues":54,"watchers":659,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/zendesk/samson/pulls/891"},"html":{"href":"https://github.com/zendesk/samson/pull/891"},"issue":{"href":"https://api.github.com/repos/zendesk/samson/issues/891"},"comments":{"href":"https://api.github.com/repos/zendesk/samson/issues/891/comments"},"review_comments":{"href":"https://api.github.com/repos/zendesk/samson/pulls/891/comments"},"review_comment":{"href":"https://api.github.com/repos/zendesk/samson/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/zendesk/samson/pulls/891/commits"},"statuses":{"href":"https://api.github.com/repos/zendesk/samson/statuses/bf9bbb1523726aab8d9dc17112922ed9cd8d8771"}}}}