{"action":"created","comment":{"url":"https://api.github.com/repos/spinnaker/clouddriver/pulls/comments/216301856","pull_request_review_id":153748591,"id":216301856,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIxNjMwMTg1Ng==","diff_hunk":"@@ -16,12 +16,201 @@\n \n package com.netflix.spinnaker.clouddriver.cloudfoundry.client;\n \n-import lombok.RequiredArgsConstructor;\n+import com.fasterxml.jackson.databind.DeserializationFeature;\n+import com.fasterxml.jackson.databind.ObjectMapper;\n+import com.fasterxml.jackson.databind.PropertyNamingStrategy;\n+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\n+import com.netflix.spinnaker.clouddriver.cloudfoundry.client.api.*;\n+import com.netflix.spinnaker.clouddriver.cloudfoundry.client.model.Token;\n+import com.squareup.okhttp.OkHttpClient;\n+import com.squareup.okhttp.Response;\n+import io.github.resilience4j.retry.Retry;\n+import io.github.resilience4j.retry.RetryConfig;\n+import lombok.extern.slf4j.Slf4j;\n+import okio.Buffer;\n+import okio.BufferedSource;\n+import retrofit.RequestInterceptor;\n+import retrofit.RestAdapter;\n+import retrofit.client.OkClient;\n+import retrofit.converter.JacksonConverter;\n \n-@RequiredArgsConstructor\n+import javax.net.ssl.SSLContext;\n+import javax.net.ssl.TrustManager;\n+import javax.net.ssl.X509TrustManager;\n+import java.nio.charset.Charset;\n+import java.security.KeyManagementException;\n+import java.security.NoSuchAlgorithmException;\n+import java.security.SecureRandom;\n+import java.security.cert.X509Certificate;\n+import java.time.Duration;\n+import java.util.concurrent.atomic.AtomicLong;\n+import java.util.concurrent.atomic.AtomicReference;\n+\n+@Slf4j\n public class CloudFoundryClient {\n-  private final String account;\n   private final String apiHost;\n   private final String user;\n   private final String password;\n+  private final OkHttpClient okHttpClient;\n+\n+  private AuthenticationService uaaService;\n+  private AtomicLong tokenExpirationNs = new AtomicLong(System.nanoTime());\n+  private volatile Token token;\n+\n+  private JacksonConverter jacksonConverter;\n+\n+  private Spaces spaces;\n+  private Organizations organizations;\n+  private Domains domains;\n+  private Routes routes;\n+  private Applications applications;\n+  private ServiceInstances serviceInstances;\n+\n+  private final RequestInterceptor oauthInterceptor = new RequestInterceptor() {\n+    @Override\n+    public void intercept(RequestFacade request) {\n+      refreshTokenIfNecessary();\n+      request.addHeader(\"Authorization\", \"bearer \" + token.getAccessToken());\n+    }\n+  };\n+\n+  private static class RetryableApiException extends Exception {\n+  }\n+\n+  public CloudFoundryClient(String account, String apiHost, String user, String password) {\n+    this.apiHost = apiHost;\n+    this.user = user;\n+    this.password = password;\n+\n+    this.okHttpClient = new OkHttpClient();\n+    okHttpClient.interceptors().add(chain -> {\n+      Retry retry = Retry.of(\"cf.api.call\", RetryConfig.custom()\n+        .retryExceptions(RetryableApiException.class)\n+        .build());\n+\n+      AtomicReference<Response> lastResponse = new AtomicReference<>();\n+      try {\n+        return retry.executeCallable(() -> {\n+          Response response = chain.proceed(chain.request());\n+\n+          switch (response.code()) {\n+            case 401:\n+              BufferedSource source = response.body().source();\n+              source.request(Long.MAX_VALUE); // request the entire body\n+              Buffer buffer = source.buffer();\n+              String body = buffer.clone().readString(Charset.forName(\"UTF-8\"));\n+              if (!body.contains(\"Bad credentials\")) {\n+                refreshToken();\n+                response = chain.proceed(chain.request().newBuilder().header(\"Authorization\", \"bearer \" + token.getAccessToken()).build());\n+              }\n+              break;\n+            case 502:\n+            case 503:\n+            case 504:\n+              throw new RetryableApiException();","path":"clouddriver-cloudfoundry/src/main/java/com/netflix/spinnaker/clouddriver/cloudfoundry/client/CloudFoundryClient.java","position":98,"original_position":98,"commit_id":"1907e0c3565264f910088137364b4513b1d5f3ee","original_commit_id":"1c01994e158e23cb732a2129fc366809d4678fa9","user":{"login":"lwander","id":4874941,"node_id":"MDQ6VXNlcjQ4NzQ5NDE=","avatar_url":"https://avatars3.githubusercontent.com/u/4874941?v=4","gravatar_id":"","url":"https://api.github.com/users/lwander","html_url":"https://github.com/lwander","followers_url":"https://api.github.com/users/lwander/followers","following_url":"https://api.github.com/users/lwander/following{/other_user}","gists_url":"https://api.github.com/users/lwander/gists{/gist_id}","starred_url":"https://api.github.com/users/lwander/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lwander/subscriptions","organizations_url":"https://api.github.com/users/lwander/orgs","repos_url":"https://api.github.com/users/lwander/repos","events_url":"https://api.github.com/users/lwander/events{/privacy}","received_events_url":"https://api.github.com/users/lwander/received_events","type":"User","site_admin":false},"body":"Oh I meant to log when a non-expected error code arises. We've spent a lot of time debugging that came down to a strange error thrown by our platform that we failed to log/acknowledge. ","created_at":"2018-09-10T12:27:51Z","updated_at":"2018-09-10T12:27:51Z","html_url":"https://github.com/spinnaker/clouddriver/pull/2939#discussion_r216301856","pull_request_url":"https://api.github.com/repos/spinnaker/clouddriver/pulls/2939","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/spinnaker/clouddriver/pulls/comments/216301856"},"html":{"href":"https://github.com/spinnaker/clouddriver/pull/2939#discussion_r216301856"},"pull_request":{"href":"https://api.github.com/repos/spinnaker/clouddriver/pulls/2939"}},"in_reply_to_id":215403543},"pull_request":{"url":"https://api.github.com/repos/spinnaker/clouddriver/pulls/2939","id":213373824,"node_id":"MDExOlB1bGxSZXF1ZXN0MjEzMzczODI0","html_url":"https://github.com/spinnaker/clouddriver/pull/2939","diff_url":"https://github.com/spinnaker/clouddriver/pull/2939.diff","patch_url":"https://github.com/spinnaker/clouddriver/pull/2939.patch","issue_url":"https://api.github.com/repos/spinnaker/clouddriver/issues/2939","number":2939,"state":"closed","locked":false,"title":"feat(provider/cf): API client and caching agent","user":{"login":"jkschneider","id":1697736,"node_id":"MDQ6VXNlcjE2OTc3MzY=","avatar_url":"https://avatars2.githubusercontent.com/u/1697736?v=4","gravatar_id":"","url":"https://api.github.com/users/jkschneider","html_url":"https://github.com/jkschneider","followers_url":"https://api.github.com/users/jkschneider/followers","following_url":"https://api.github.com/users/jkschneider/following{/other_user}","gists_url":"https://api.github.com/users/jkschneider/gists{/gist_id}","starred_url":"https://api.github.com/users/jkschneider/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jkschneider/subscriptions","organizations_url":"https://api.github.com/users/jkschneider/orgs","repos_url":"https://api.github.com/users/jkschneider/repos","events_url":"https://api.github.com/users/jkschneider/events{/privacy}","received_events_url":"https://api.github.com/users/jkschneider/received_events","type":"User","site_admin":false},"body":"This completes the read side of the provider.","created_at":"2018-09-05T17:37:58Z","updated_at":"2018-09-10T12:27:51Z","closed_at":"2018-09-06T21:41:38Z","merged_at":"2018-09-06T21:41:38Z","merge_commit_sha":"ba3271fe6b684dd3fc1f7caede7b58c2e8b5d116","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/spinnaker/clouddriver/pulls/2939/commits","review_comments_url":"https://api.github.com/repos/spinnaker/clouddriver/pulls/2939/comments","review_comment_url":"https://api.github.com/repos/spinnaker/clouddriver/pulls/comments{/number}","comments_url":"https://api.github.com/repos/spinnaker/clouddriver/issues/2939/comments","statuses_url":"https://api.github.com/repos/spinnaker/clouddriver/statuses/1907e0c3565264f910088137364b4513b1d5f3ee","head":{"label":"jkschneider:cf","ref":"cf","sha":"1907e0c3565264f910088137364b4513b1d5f3ee","user":{"login":"jkschneider","id":1697736,"node_id":"MDQ6VXNlcjE2OTc3MzY=","avatar_url":"https://avatars2.githubusercontent.com/u/1697736?v=4","gravatar_id":"","url":"https://api.github.com/users/jkschneider","html_url":"https://github.com/jkschneider","followers_url":"https://api.github.com/users/jkschneider/followers","following_url":"https://api.github.com/users/jkschneider/following{/other_user}","gists_url":"https://api.github.com/users/jkschneider/gists{/gist_id}","starred_url":"https://api.github.com/users/jkschneider/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jkschneider/subscriptions","organizations_url":"https://api.github.com/users/jkschneider/orgs","repos_url":"https://api.github.com/users/jkschneider/repos","events_url":"https://api.github.com/users/jkschneider/events{/privacy}","received_events_url":"https://api.github.com/users/jkschneider/received_events","type":"User","site_admin":false},"repo":{"id":130436756,"node_id":"MDEwOlJlcG9zaXRvcnkxMzA0MzY3NTY=","name":"clouddriver","full_name":"jkschneider/clouddriver","owner":{"login":"jkschneider","id":1697736,"node_id":"MDQ6VXNlcjE2OTc3MzY=","avatar_url":"https://avatars2.githubusercontent.com/u/1697736?v=4","gravatar_id":"","url":"https://api.github.com/users/jkschneider","html_url":"https://github.com/jkschneider","followers_url":"https://api.github.com/users/jkschneider/followers","following_url":"https://api.github.com/users/jkschneider/following{/other_user}","gists_url":"https://api.github.com/users/jkschneider/gists{/gist_id}","starred_url":"https://api.github.com/users/jkschneider/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jkschneider/subscriptions","organizations_url":"https://api.github.com/users/jkschneider/orgs","repos_url":"https://api.github.com/users/jkschneider/repos","events_url":"https://api.github.com/users/jkschneider/events{/privacy}","received_events_url":"https://api.github.com/users/jkschneider/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jkschneider/clouddriver","description":"read and write operations across cloud providers","fork":true,"url":"https://api.github.com/repos/jkschneider/clouddriver","forks_url":"https://api.github.com/repos/jkschneider/clouddriver/forks","keys_url":"https://api.github.com/repos/jkschneider/clouddriver/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jkschneider/clouddriver/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jkschneider/clouddriver/teams","hooks_url":"https://api.github.com/repos/jkschneider/clouddriver/hooks","issue_events_url":"https://api.github.com/repos/jkschneider/clouddriver/issues/events{/number}","events_url":"https://api.github.com/repos/jkschneider/clouddriver/events","assignees_url":"https://api.github.com/repos/jkschneider/clouddriver/assignees{/user}","branches_url":"https://api.github.com/repos/jkschneider/clouddriver/branches{/branch}","tags_url":"https://api.github.com/repos/jkschneider/clouddriver/tags","blobs_url":"https://api.github.com/repos/jkschneider/clouddriver/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jkschneider/clouddriver/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jkschneider/clouddriver/git/refs{/sha}","trees_url":"https://api.github.com/repos/jkschneider/clouddriver/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jkschneider/clouddriver/statuses/{sha}","languages_url":"https://api.github.com/repos/jkschneider/clouddriver/languages","stargazers_url":"https://api.github.com/repos/jkschneider/clouddriver/stargazers","contributors_url":"https://api.github.com/repos/jkschneider/clouddriver/contributors","subscribers_url":"https://api.github.com/repos/jkschneider/clouddriver/subscribers","subscription_url":"https://api.github.com/repos/jkschneider/clouddriver/subscription","commits_url":"https://api.github.com/repos/jkschneider/clouddriver/commits{/sha}","git_commits_url":"https://api.github.com/repos/jkschneider/clouddriver/git/commits{/sha}","comments_url":"https://api.github.com/repos/jkschneider/clouddriver/comments{/number}","issue_comment_url":"https://api.github.com/repos/jkschneider/clouddriver/issues/comments{/number}","contents_url":"https://api.github.com/repos/jkschneider/clouddriver/contents/{+path}","compare_url":"https://api.github.com/repos/jkschneider/clouddriver/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jkschneider/clouddriver/merges","archive_url":"https://api.github.com/repos/jkschneider/clouddriver/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jkschneider/clouddriver/downloads","issues_url":"https://api.github.com/repos/jkschneider/clouddriver/issues{/number}","pulls_url":"https://api.github.com/repos/jkschneider/clouddriver/pulls{/number}","milestones_url":"https://api.github.com/repos/jkschneider/clouddriver/milestones{/number}","notifications_url":"https://api.github.com/repos/jkschneider/clouddriver/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jkschneider/clouddriver/labels{/name}","releases_url":"https://api.github.com/repos/jkschneider/clouddriver/releases{/id}","deployments_url":"https://api.github.com/repos/jkschneider/clouddriver/deployments","created_at":"2018-04-21T03:34:26Z","updated_at":"2018-07-31T13:55:45Z","pushed_at":"2018-09-06T20:10:45Z","git_url":"git://github.com/jkschneider/clouddriver.git","ssh_url":"git@github.com:jkschneider/clouddriver.git","clone_url":"https://github.com/jkschneider/clouddriver.git","svn_url":"https://github.com/jkschneider/clouddriver","homepage":null,"size":40657,"stargazers_count":0,"watchers_count":0,"language":"Groovy","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"spinnaker:master","ref":"master","sha":"58851a2eedf05e6cfae5f96a434d7aa7d81a256b","user":{"login":"spinnaker","id":7634182,"node_id":"MDEyOk9yZ2FuaXphdGlvbjc2MzQxODI=","avatar_url":"https://avatars1.githubusercontent.com/u/7634182?v=4","gravatar_id":"","url":"https://api.github.com/users/spinnaker","html_url":"https://github.com/spinnaker","followers_url":"https://api.github.com/users/spinnaker/followers","following_url":"https://api.github.com/users/spinnaker/following{/other_user}","gists_url":"https://api.github.com/users/spinnaker/gists{/gist_id}","starred_url":"https://api.github.com/users/spinnaker/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spinnaker/subscriptions","organizations_url":"https://api.github.com/users/spinnaker/orgs","repos_url":"https://api.github.com/users/spinnaker/repos","events_url":"https://api.github.com/users/spinnaker/events{/privacy}","received_events_url":"https://api.github.com/users/spinnaker/received_events","type":"Organization","site_admin":false},"repo":{"id":37556951,"node_id":"MDEwOlJlcG9zaXRvcnkzNzU1Njk1MQ==","name":"clouddriver","full_name":"spinnaker/clouddriver","owner":{"login":"spinnaker","id":7634182,"node_id":"MDEyOk9yZ2FuaXphdGlvbjc2MzQxODI=","avatar_url":"https://avatars1.githubusercontent.com/u/7634182?v=4","gravatar_id":"","url":"https://api.github.com/users/spinnaker","html_url":"https://github.com/spinnaker","followers_url":"https://api.github.com/users/spinnaker/followers","following_url":"https://api.github.com/users/spinnaker/following{/other_user}","gists_url":"https://api.github.com/users/spinnaker/gists{/gist_id}","starred_url":"https://api.github.com/users/spinnaker/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/spinnaker/subscriptions","organizations_url":"https://api.github.com/users/spinnaker/orgs","repos_url":"https://api.github.com/users/spinnaker/repos","events_url":"https://api.github.com/users/spinnaker/events{/privacy}","received_events_url":"https://api.github.com/users/spinnaker/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/spinnaker/clouddriver","description":"read and write operations across cloud providers","fork":false,"url":"https://api.github.com/repos/spinnaker/clouddriver","forks_url":"https://api.github.com/repos/spinnaker/clouddriver/forks","keys_url":"https://api.github.com/repos/spinnaker/clouddriver/keys{/key_id}","collaborators_url":"https://api.github.com/repos/spinnaker/clouddriver/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/spinnaker/clouddriver/teams","hooks_url":"https://api.github.com/repos/spinnaker/clouddriver/hooks","issue_events_url":"https://api.github.com/repos/spinnaker/clouddriver/issues/events{/number}","events_url":"https://api.github.com/repos/spinnaker/clouddriver/events","assignees_url":"https://api.github.com/repos/spinnaker/clouddriver/assignees{/user}","branches_url":"https://api.github.com/repos/spinnaker/clouddriver/branches{/branch}","tags_url":"https://api.github.com/repos/spinnaker/clouddriver/tags","blobs_url":"https://api.github.com/repos/spinnaker/clouddriver/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/spinnaker/clouddriver/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/spinnaker/clouddriver/git/refs{/sha}","trees_url":"https://api.github.com/repos/spinnaker/clouddriver/git/trees{/sha}","statuses_url":"https://api.github.com/repos/spinnaker/clouddriver/statuses/{sha}","languages_url":"https://api.github.com/repos/spinnaker/clouddriver/languages","stargazers_url":"https://api.github.com/repos/spinnaker/clouddriver/stargazers","contributors_url":"https://api.github.com/repos/spinnaker/clouddriver/contributors","subscribers_url":"https://api.github.com/repos/spinnaker/clouddriver/subscribers","subscription_url":"https://api.github.com/repos/spinnaker/clouddriver/subscription","commits_url":"https://api.github.com/repos/spinnaker/clouddriver/commits{/sha}","git_commits_url":"https://api.github.com/repos/spinnaker/clouddriver/git/commits{/sha}","comments_url":"https://api.github.com/repos/spinnaker/clouddriver/comments{/number}","issue_comment_url":"https://api.github.com/repos/spinnaker/clouddriver/issues/comments{/number}","contents_url":"https://api.github.com/repos/spinnaker/clouddriver/contents/{+path}","compare_url":"https://api.github.com/repos/spinnaker/clouddriver/compare/{base}...{head}","merges_url":"https://api.github.com/repos/spinnaker/clouddriver/merges","archive_url":"https://api.github.com/repos/spinnaker/clouddriver/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/spinnaker/clouddriver/downloads","issues_url":"https://api.github.com/repos/spinnaker/clouddriver/issues{/number}","pulls_url":"https://api.github.com/repos/spinnaker/clouddriver/pulls{/number}","milestones_url":"https://api.github.com/repos/spinnaker/clouddriver/milestones{/number}","notifications_url":"https://api.github.com/repos/spinnaker/clouddriver/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/spinnaker/clouddriver/labels{/name}","releases_url":"https://api.github.com/repos/spinnaker/clouddriver/releases{/id}","deployments_url":"https://api.github.com/repos/spinnaker/clouddriver/deployments","created_at":"2015-06-16T21:26:02Z","updated_at":"2018-09-08T05:41:49Z","pushed_at":"2018-09-08T03:44:59Z","git_url":"git://github.com/spinnaker/clouddriver.git","ssh_url":"git@github.com:spinnaker/clouddriver.git","clone_url":"https://github.com/spinnaker/clouddriver.git","svn_url":"https://github.com/spinnaker/clouddriver","homepage":null,"size":40717,"stargazers_count":168,"watchers_count":168,"language":"Groovy","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":428,"mirror_url":null,"archived":false,"open_issues_count":14,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":428,"open_issues":14,"watchers":168,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/spinnaker/clouddriver/pulls/2939"},"html":{"href":"https://github.com/spinnaker/clouddriver/pull/2939"},"issue":{"href":"https://api.github.com/repos/spinnaker/clouddriver/issues/2939"},"comments":{"href":"https://api.github.com/repos/spinnaker/clouddriver/issues/2939/comments"},"review_comments":{"href":"https://api.github.com/repos/spinnaker/clouddriver/pulls/2939/comments"},"review_comment":{"href":"https://api.github.com/repos/spinnaker/clouddriver/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/spinnaker/clouddriver/pulls/2939/commits"},"statuses":{"href":"https://api.github.com/repos/spinnaker/clouddriver/statuses/1907e0c3565264f910088137364b4513b1d5f3ee"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"lwander"}}