{"action":"created","comment":{"url":"https://api.github.com/repos/openshift/origin/pulls/comments/41713935","id":41713935,"diff_hunk":"@@ -0,0 +1,122 @@\n+'use strict';\n+\n+angular.module(\"openshiftConsole\")\n+  .factory(\"MetricsService\", function($http, $q, APIDiscovery, Logger) {\n+    var COUNTER_TEMPLATE = \"/counters/{containerName}%2F{podUID}%2F{metric}/data\";\n+    var GAUGE_TEMPLATE = \"/gauges/{containerName}%2F{podUID}%2F{metric}/data\";\n+    var RATE_TEMPLATE = \"/counters/{containerName}%2F{podUID}%2F{metric}/rate\";\n+\n+    // URL template to show for each type of metric.\n+    var templateByMetric = {\n+      \"cpu/usage\": COUNTER_TEMPLATE,\n+      \"memory/usage\": GAUGE_TEMPLATE,\n+      \"memory/page_faults\": RATE_TEMPLATE,\n+      \"memory/major_page_faults\": RATE_TEMPLATE,\n+      uptime: RATE_TEMPLATE\n+    };\n+\n+    var metricsURL;\n+    function getMetricsURL() {\n+      if (angular.isDefined(metricsURL)) {\n+        return $q.when(metricsURL);\n+      }\n+\n+      return APIDiscovery.getMetricsURL().then(function(url) {\n+        // Remove trailing slash if present.\n+        metricsURL = (url || '').replace(/\\/$/, \"\");\n+        return metricsURL;\n+      });\n+    }\n+\n+    function normalizeMetricsData(data, metric) {\n+      // Track the previous value for CPU usage calculations.\n+      var lastValue;\n+      if (!data.length) {\n+        return;\n+      }\n+\n+      angular.forEach(data, function(point) {\n+        // Calculate a timestamp based on the midtime if missing.\n+        if (!point.timestamp) {\n+          point.timestamp = point.start + (point.end - point.start) / 2;\n+        }\n+\n+        // Set point.value to the average or 0 if missing.\n+        if (!point.value || point.value === \"NaN\") {\n+          point.value = point.avg || 0;\n+        }\n+\n+        // Convert cumulative CPU usage in nanoseconds to millicores.\n+        if (metric === 'cpu/usage') {\n+          var value = point.value;\n+          if (angular.isDefined(lastValue)) {\n+            var timeInMillis = point.end - point.start;\n+            // Find the usage for just this bucket by comparing it to the last value.\n+            // Values are in nanoseconds. Calculate usage in millis.\n+            var usageInMillis = (value - lastValue) / 1000000;\n+            // Convert to millicores.\n+            point.value = (usageInMillis / timeInMillis) * 1000;\n+          }\n+\n+          lastValue = value;\n+        }\n+      });\n+\n+      // Remove the first value since it can't be used CPU utilization.\n+      // We want the same number of data points for all charts.\n+      data.shift();\n+      return data;\n+    }\n+\n+    return {\n+      // Check if the metrics service is available. The service is considered\n+      // available if a metrics URL is set. Returns a promise resolved with a\n+      // boolean value.\n+      isAvailable: function() {\n+        return getMetricsURL().then(function(url) {\n+          return !!url;\n+        });\n+      },\n+\n+      // Get metrics data for a container.\n+      //\n+      // Keyword Args\n+      //   pod:            the pod object\n+      //   containerName:  the container name\n+      //   metric:         the metric to check, e.g. \"memory/usage\"\n+      //   start:          start time in millis\n+      //   end:            end time in millis\n+      //\n+      // Returns a promise resolved with the metrics data.\n+      get: function(kwArgs) {\n+        return getMetricsURL().then(function(metricsURL) {\n+          var reqURL,\n+              template = metricsURL + templateByMetric[kwArgs.metric];\n+\n+          reqURL = URI.expand(template, {\n+            podUID: kwArgs.pod.metadata.uid,\n+            containerName: kwArgs.containerName,\n+            metric: kwArgs.metric\n+          }).toString();\n+\n+          Logger.log(\"Requesting metrics:\", reqURL);","path":"assets/app/scripts/services/metrics.js","position":102,"original_position":102,"commit_id":"564bcb1fc9e80d62312027f10ee88ec1c680bdff","original_commit_id":"564bcb1fc9e80d62312027f10ee88ec1c680bdff","user":{"login":"jwforres","id":1467629,"avatar_url":"https://avatars.githubusercontent.com/u/1467629?v=3","gravatar_id":"","url":"https://api.github.com/users/jwforres","html_url":"https://github.com/jwforres","followers_url":"https://api.github.com/users/jwforres/followers","following_url":"https://api.github.com/users/jwforres/following{/other_user}","gists_url":"https://api.github.com/users/jwforres/gists{/gist_id}","starred_url":"https://api.github.com/users/jwforres/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jwforres/subscriptions","organizations_url":"https://api.github.com/users/jwforres/orgs","repos_url":"https://api.github.com/users/jwforres/repos","events_url":"https://api.github.com/users/jwforres/events{/privacy}","received_events_url":"https://api.github.com/users/jwforres/received_events","type":"User","site_admin":false},"body":"If this is going to generate a log every 30 seconds for each chart then we may want to make a sub logger like we do for auth that can be enabled independently from the main logger.  ","created_at":"2015-10-11T18:41:08Z","updated_at":"2015-10-11T18:41:08Z","html_url":"https://github.com/openshift/origin/pull/4999#discussion_r41713935","pull_request_url":"https://api.github.com/repos/openshift/origin/pulls/4999","_links":{"self":{"href":"https://api.github.com/repos/openshift/origin/pulls/comments/41713935"},"html":{"href":"https://github.com/openshift/origin/pull/4999#discussion_r41713935"},"pull_request":{"href":"https://api.github.com/repos/openshift/origin/pulls/4999"}}},"pull_request":{"url":"https://api.github.com/repos/openshift/origin/pulls/4999","id":47062880,"html_url":"https://github.com/openshift/origin/pull/4999","diff_url":"https://github.com/openshift/origin/pull/4999.diff","patch_url":"https://github.com/openshift/origin/pull/4999.patch","issue_url":"https://api.github.com/repos/openshift/origin/issues/4999","number":4999,"state":"open","locked":false,"title":"[WIP] Show metrics on pod details page","user":{"login":"spadgett","id":1167259,"avatar_url":"https://avatars.githubusercontent.com/u/1167259?v=3","gravatar_id":"","url":"https://api.github.com/users/spadgett","html_url":"https://github.com/spadgett","followers_url":"https://api.github.com/users/spadgett/followers","following_url":"https://api.github.com/users/spadgett/following{/other_user}","gists_url":"https://api.github.com/users/spadgett/gists{/gist_id}","starred_url":"https://api.github.com/users/spadgett/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spadgett/subscriptions","organizations_url":"https://api.github.com/users/spadgett/orgs","repos_url":"https://api.github.com/users/spadgett/repos","events_url":"https://api.github.com/users/spadgett/events{/privacy}","received_events_url":"https://api.github.com/users/spadgett/received_events","type":"User","site_admin":false},"body":"Adds memory usage and CPU usage charts to the pod details page. Requires hawkular-metrics, see\r\n\r\nhttps://github.com/mwringe/origin-metrics\r\n\r\n![openshift_web_console](https://cloud.githubusercontent.com/assets/1167259/10405127/ebd161b4-6ea4-11e5-80d4-d608c6df4efc.png)\r\n\r\nTODO:\r\n\r\n- [ ] Verify accuracy of CPU usage calculation\r\n- [ ] Allow users to pick arbitrary date ranges\r\n- [ ] Consider other layouts (displaying a full spark chart beside the donut chart at wide screen widths?)\r\n- [ ] Remove my test `metricsURL` from config.js before merge.\r\n\r\n/cc @jwforres @mwringe","created_at":"2015-10-07T18:20:19Z","updated_at":"2015-10-11T18:41:08Z","closed_at":null,"merged_at":null,"merge_commit_sha":"9e9bf860307f7578a53b42145701a145178c9b77","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/openshift/origin/pulls/4999/commits","review_comments_url":"https://api.github.com/repos/openshift/origin/pulls/4999/comments","review_comment_url":"https://api.github.com/repos/openshift/origin/pulls/comments{/number}","comments_url":"https://api.github.com/repos/openshift/origin/issues/4999/comments","statuses_url":"https://api.github.com/repos/openshift/origin/statuses/564bcb1fc9e80d62312027f10ee88ec1c680bdff","head":{"label":"spadgett:ui-metrics","ref":"ui-metrics","sha":"564bcb1fc9e80d62312027f10ee88ec1c680bdff","user":{"login":"spadgett","id":1167259,"avatar_url":"https://avatars.githubusercontent.com/u/1167259?v=3","gravatar_id":"","url":"https://api.github.com/users/spadgett","html_url":"https://github.com/spadgett","followers_url":"https://api.github.com/users/spadgett/followers","following_url":"https://api.github.com/users/spadgett/following{/other_user}","gists_url":"https://api.github.com/users/spadgett/gists{/gist_id}","starred_url":"https://api.github.com/users/spadgett/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spadgett/subscriptions","organizations_url":"https://api.github.com/users/spadgett/orgs","repos_url":"https://api.github.com/users/spadgett/repos","events_url":"https://api.github.com/users/spadgett/events{/privacy}","received_events_url":"https://api.github.com/users/spadgett/received_events","type":"User","site_admin":false},"repo":{"id":34353535,"name":"origin","full_name":"spadgett/origin","owner":{"login":"spadgett","id":1167259,"avatar_url":"https://avatars.githubusercontent.com/u/1167259?v=3","gravatar_id":"","url":"https://api.github.com/users/spadgett","html_url":"https://github.com/spadgett","followers_url":"https://api.github.com/users/spadgett/followers","following_url":"https://api.github.com/users/spadgett/following{/other_user}","gists_url":"https://api.github.com/users/spadgett/gists{/gist_id}","starred_url":"https://api.github.com/users/spadgett/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spadgett/subscriptions","organizations_url":"https://api.github.com/users/spadgett/orgs","repos_url":"https://api.github.com/users/spadgett/repos","events_url":"https://api.github.com/users/spadgett/events{/privacy}","received_events_url":"https://api.github.com/users/spadgett/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/spadgett/origin","description":"OpenShift 3 - build, deploy, and manage your applications with Docker and Kubernetes","fork":true,"url":"https://api.github.com/repos/spadgett/origin","forks_url":"https://api.github.com/repos/spadgett/origin/forks","keys_url":"https://api.github.com/repos/spadgett/origin/keys{/key_id}","collaborators_url":"https://api.github.com/repos/spadgett/origin/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/spadgett/origin/teams","hooks_url":"https://api.github.com/repos/spadgett/origin/hooks","issue_events_url":"https://api.github.com/repos/spadgett/origin/issues/events{/number}","events_url":"https://api.github.com/repos/spadgett/origin/events","assignees_url":"https://api.github.com/repos/spadgett/origin/assignees{/user}","branches_url":"https://api.github.com/repos/spadgett/origin/branches{/branch}","tags_url":"https://api.github.com/repos/spadgett/origin/tags","blobs_url":"https://api.github.com/repos/spadgett/origin/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/spadgett/origin/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/spadgett/origin/git/refs{/sha}","trees_url":"https://api.github.com/repos/spadgett/origin/git/trees{/sha}","statuses_url":"https://api.github.com/repos/spadgett/origin/statuses/{sha}","languages_url":"https://api.github.com/repos/spadgett/origin/languages","stargazers_url":"https://api.github.com/repos/spadgett/origin/stargazers","contributors_url":"https://api.github.com/repos/spadgett/origin/contributors","subscribers_url":"https://api.github.com/repos/spadgett/origin/subscribers","subscription_url":"https://api.github.com/repos/spadgett/origin/subscription","commits_url":"https://api.github.com/repos/spadgett/origin/commits{/sha}","git_commits_url":"https://api.github.com/repos/spadgett/origin/git/commits{/sha}","comments_url":"https://api.github.com/repos/spadgett/origin/comments{/number}","issue_comment_url":"https://api.github.com/repos/spadgett/origin/issues/comments{/number}","contents_url":"https://api.github.com/repos/spadgett/origin/contents/{+path}","compare_url":"https://api.github.com/repos/spadgett/origin/compare/{base}...{head}","merges_url":"https://api.github.com/repos/spadgett/origin/merges","archive_url":"https://api.github.com/repos/spadgett/origin/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/spadgett/origin/downloads","issues_url":"https://api.github.com/repos/spadgett/origin/issues{/number}","pulls_url":"https://api.github.com/repos/spadgett/origin/pulls{/number}","milestones_url":"https://api.github.com/repos/spadgett/origin/milestones{/number}","notifications_url":"https://api.github.com/repos/spadgett/origin/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/spadgett/origin/labels{/name}","releases_url":"https://api.github.com/repos/spadgett/origin/releases{/id}","created_at":"2015-04-21T21:38:33Z","updated_at":"2015-05-08T19:33:23Z","pushed_at":"2015-10-11T16:52:12Z","git_url":"git://github.com/spadgett/origin.git","ssh_url":"git@github.com:spadgett/origin.git","clone_url":"https://github.com/spadgett/origin.git","svn_url":"https://github.com/spadgett/origin","homepage":"http://www.openshift.org","size":147766,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"openshift:master","ref":"master","sha":"86b63beaabe05067afc066089f94a34bad367185","user":{"login":"openshift","id":792337,"avatar_url":"https://avatars.githubusercontent.com/u/792337?v=3","gravatar_id":"","url":"https://api.github.com/users/openshift","html_url":"https://github.com/openshift","followers_url":"https://api.github.com/users/openshift/followers","following_url":"https://api.github.com/users/openshift/following{/other_user}","gists_url":"https://api.github.com/users/openshift/gists{/gist_id}","starred_url":"https://api.github.com/users/openshift/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openshift/subscriptions","organizations_url":"https://api.github.com/users/openshift/orgs","repos_url":"https://api.github.com/users/openshift/repos","events_url":"https://api.github.com/users/openshift/events{/privacy}","received_events_url":"https://api.github.com/users/openshift/received_events","type":"Organization","site_admin":false},"repo":{"id":22442668,"name":"origin","full_name":"openshift/origin","owner":{"login":"openshift","id":792337,"avatar_url":"https://avatars.githubusercontent.com/u/792337?v=3","gravatar_id":"","url":"https://api.github.com/users/openshift","html_url":"https://github.com/openshift","followers_url":"https://api.github.com/users/openshift/followers","following_url":"https://api.github.com/users/openshift/following{/other_user}","gists_url":"https://api.github.com/users/openshift/gists{/gist_id}","starred_url":"https://api.github.com/users/openshift/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openshift/subscriptions","organizations_url":"https://api.github.com/users/openshift/orgs","repos_url":"https://api.github.com/users/openshift/repos","events_url":"https://api.github.com/users/openshift/events{/privacy}","received_events_url":"https://api.github.com/users/openshift/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/openshift/origin","description":"OpenShift 3 - build, deploy, and manage your applications with Docker and Kubernetes","fork":false,"url":"https://api.github.com/repos/openshift/origin","forks_url":"https://api.github.com/repos/openshift/origin/forks","keys_url":"https://api.github.com/repos/openshift/origin/keys{/key_id}","collaborators_url":"https://api.github.com/repos/openshift/origin/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/openshift/origin/teams","hooks_url":"https://api.github.com/repos/openshift/origin/hooks","issue_events_url":"https://api.github.com/repos/openshift/origin/issues/events{/number}","events_url":"https://api.github.com/repos/openshift/origin/events","assignees_url":"https://api.github.com/repos/openshift/origin/assignees{/user}","branches_url":"https://api.github.com/repos/openshift/origin/branches{/branch}","tags_url":"https://api.github.com/repos/openshift/origin/tags","blobs_url":"https://api.github.com/repos/openshift/origin/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/openshift/origin/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/openshift/origin/git/refs{/sha}","trees_url":"https://api.github.com/repos/openshift/origin/git/trees{/sha}","statuses_url":"https://api.github.com/repos/openshift/origin/statuses/{sha}","languages_url":"https://api.github.com/repos/openshift/origin/languages","stargazers_url":"https://api.github.com/repos/openshift/origin/stargazers","contributors_url":"https://api.github.com/repos/openshift/origin/contributors","subscribers_url":"https://api.github.com/repos/openshift/origin/subscribers","subscription_url":"https://api.github.com/repos/openshift/origin/subscription","commits_url":"https://api.github.com/repos/openshift/origin/commits{/sha}","git_commits_url":"https://api.github.com/repos/openshift/origin/git/commits{/sha}","comments_url":"https://api.github.com/repos/openshift/origin/comments{/number}","issue_comment_url":"https://api.github.com/repos/openshift/origin/issues/comments{/number}","contents_url":"https://api.github.com/repos/openshift/origin/contents/{+path}","compare_url":"https://api.github.com/repos/openshift/origin/compare/{base}...{head}","merges_url":"https://api.github.com/repos/openshift/origin/merges","archive_url":"https://api.github.com/repos/openshift/origin/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/openshift/origin/downloads","issues_url":"https://api.github.com/repos/openshift/origin/issues{/number}","pulls_url":"https://api.github.com/repos/openshift/origin/pulls{/number}","milestones_url":"https://api.github.com/repos/openshift/origin/milestones{/number}","notifications_url":"https://api.github.com/repos/openshift/origin/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/openshift/origin/labels{/name}","releases_url":"https://api.github.com/repos/openshift/origin/releases{/id}","created_at":"2014-07-30T20:04:05Z","updated_at":"2015-10-10T17:44:43Z","pushed_at":"2015-10-11T17:37:10Z","git_url":"git://github.com/openshift/origin.git","ssh_url":"git@github.com:openshift/origin.git","clone_url":"https://github.com/openshift/origin.git","svn_url":"https://github.com/openshift/origin","homepage":"http://www.openshift.org","size":256234,"stargazers_count":728,"watchers_count":728,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":445,"mirror_url":null,"open_issues_count":574,"forks":445,"open_issues":574,"watchers":728,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/openshift/origin/pulls/4999"},"html":{"href":"https://github.com/openshift/origin/pull/4999"},"issue":{"href":"https://api.github.com/repos/openshift/origin/issues/4999"},"comments":{"href":"https://api.github.com/repos/openshift/origin/issues/4999/comments"},"review_comments":{"href":"https://api.github.com/repos/openshift/origin/pulls/4999/comments"},"review_comment":{"href":"https://api.github.com/repos/openshift/origin/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/openshift/origin/pulls/4999/commits"},"statuses":{"href":"https://api.github.com/repos/openshift/origin/statuses/564bcb1fc9e80d62312027f10ee88ec1c680bdff"}}}}