1 /* 2 * Copyright 2016 Google LLC 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.cloud.dns; 18 19 import static com.google.cloud.RetryHelper.runWithRetries; 20 import static com.google.common.base.Preconditions.checkArgument; 21 22 import com.google.api.gax.paging.Page; 23 import com.google.api.services.dns.model.Change; 24 import com.google.api.services.dns.model.ManagedZone; 25 import com.google.api.services.dns.model.Project; 26 import com.google.api.services.dns.model.ResourceRecordSet; 27 import com.google.cloud.BaseService; 28 import com.google.cloud.PageImpl; 29 import com.google.cloud.RetryHelper; 30 import com.google.cloud.dns.spi.v1.DnsRpc; 31 import com.google.common.base.Function; 32 import com.google.common.collect.ImmutableList; 33 import com.google.common.collect.ImmutableMap; 34 import com.google.common.collect.Iterables; 35 import com.google.common.collect.Maps; 36 import java.util.Map; 37 import java.util.concurrent.Callable; 38 39 /** A default implementation of Dns. */ 40 final class DnsImpl extends BaseService<DnsOptions> implements Dns { 41 42 private final DnsRpc dnsRpc; 43 44 static class ZonePageFetcher implements PageImpl.NextPageFetcher<Zone> { 45 46 private static final long serialVersionUID = 2158209410430566961L; 47 private final Map<DnsRpc.Option, ?> requestOptions; 48 private final DnsOptions serviceOptions; 49 ZonePageFetcher(DnsOptions serviceOptions, String cursor, Map<DnsRpc.Option, ?> optionMap)50 ZonePageFetcher(DnsOptions serviceOptions, String cursor, Map<DnsRpc.Option, ?> optionMap) { 51 this.requestOptions = 52 PageImpl.nextRequestOptions(DnsRpc.Option.PAGE_TOKEN, cursor, optionMap); 53 this.serviceOptions = serviceOptions; 54 } 55 56 @Override getNextPage()57 public Page<Zone> getNextPage() { 58 return listZones(serviceOptions, requestOptions); 59 } 60 } 61 62 static class ChangeRequestPageFetcher implements PageImpl.NextPageFetcher<ChangeRequest> { 63 64 private static final long serialVersionUID = 4473265130673029139L; 65 private final String zoneName; 66 private final Map<DnsRpc.Option, ?> requestOptions; 67 private final DnsOptions serviceOptions; 68 ChangeRequestPageFetcher( String zoneName, DnsOptions serviceOptions, String cursor, Map<DnsRpc.Option, ?> optionMap)69 ChangeRequestPageFetcher( 70 String zoneName, 71 DnsOptions serviceOptions, 72 String cursor, 73 Map<DnsRpc.Option, ?> optionMap) { 74 this.zoneName = zoneName; 75 this.requestOptions = 76 PageImpl.nextRequestOptions(DnsRpc.Option.PAGE_TOKEN, cursor, optionMap); 77 this.serviceOptions = serviceOptions; 78 } 79 80 @Override getNextPage()81 public Page<ChangeRequest> getNextPage() { 82 return listChangeRequests(zoneName, serviceOptions, requestOptions); 83 } 84 } 85 86 static class RecordSetPageFetcher implements PageImpl.NextPageFetcher<RecordSet> { 87 88 private static final long serialVersionUID = -6039369212511530846L; 89 private final Map<DnsRpc.Option, ?> requestOptions; 90 private final DnsOptions serviceOptions; 91 private final String zoneName; 92 RecordSetPageFetcher( String zoneName, DnsOptions serviceOptions, String cursor, Map<DnsRpc.Option, ?> optionMap)93 RecordSetPageFetcher( 94 String zoneName, 95 DnsOptions serviceOptions, 96 String cursor, 97 Map<DnsRpc.Option, ?> optionMap) { 98 this.zoneName = zoneName; 99 this.requestOptions = 100 PageImpl.nextRequestOptions(DnsRpc.Option.PAGE_TOKEN, cursor, optionMap); 101 this.serviceOptions = serviceOptions; 102 } 103 104 @Override getNextPage()105 public Page<RecordSet> getNextPage() { 106 return listRecordSets(zoneName, serviceOptions, requestOptions); 107 } 108 } 109 DnsImpl(DnsOptions options)110 DnsImpl(DnsOptions options) { 111 super(options); 112 dnsRpc = options.getDnsRpcV1(); 113 } 114 zoneFromPb(final DnsOptions options)115 static Function<ManagedZone, Zone> zoneFromPb(final DnsOptions options) { 116 return new Function<ManagedZone, Zone>() { 117 @Override 118 public Zone apply(ManagedZone zonePb) { 119 return Zone.fromPb(options.getService(), zonePb); 120 } 121 }; 122 } 123 124 @Override 125 public Page<Zone> listZones(ZoneListOption... options) { 126 return listZones(getOptions(), optionMap(options)); 127 } 128 129 private static Page<Zone> listZones( 130 final DnsOptions serviceOptions, final Map<DnsRpc.Option, ?> optionsMap) { 131 try { 132 // get a list of managed zones 133 final DnsRpc rpc = serviceOptions.getDnsRpcV1(); 134 DnsRpc.ListResult<ManagedZone> result = 135 runWithRetries( 136 new Callable<DnsRpc.ListResult<ManagedZone>>() { 137 @Override 138 public DnsRpc.ListResult<ManagedZone> call() { 139 return rpc.listZones(optionsMap); 140 } 141 }, 142 serviceOptions.getRetrySettings(), 143 EXCEPTION_HANDLER, 144 serviceOptions.getClock()); 145 String cursor = result.pageToken(); 146 // transform that list into zone objects 147 Iterable<Zone> zones = 148 result.results() == null 149 ? ImmutableList.<Zone>of() 150 : Iterables.transform(result.results(), zoneFromPb(serviceOptions)); 151 return new PageImpl<>(new ZonePageFetcher(serviceOptions, cursor, optionsMap), cursor, zones); 152 } catch (RetryHelper.RetryHelperException e) { 153 throw DnsException.translateAndThrow(e); 154 } 155 } 156 157 @Override 158 public Page<ChangeRequest> listChangeRequests( 159 String zoneName, ChangeRequestListOption... options) { 160 return listChangeRequests(zoneName, getOptions(), optionMap(options)); 161 } 162 163 private static Page<ChangeRequest> listChangeRequests( 164 final String zoneName, 165 final DnsOptions serviceOptions, 166 final Map<DnsRpc.Option, ?> optionsMap) { 167 try { 168 // get a list of changes 169 final DnsRpc rpc = serviceOptions.getDnsRpcV1(); 170 DnsRpc.ListResult<Change> result = 171 runWithRetries( 172 new Callable<DnsRpc.ListResult<Change>>() { 173 @Override 174 public DnsRpc.ListResult<Change> call() { 175 return rpc.listChangeRequests(zoneName, optionsMap); 176 } 177 }, 178 serviceOptions.getRetrySettings(), 179 EXCEPTION_HANDLER, 180 serviceOptions.getClock()); 181 String cursor = result.pageToken(); 182 // transform that list into change request objects 183 Iterable<ChangeRequest> changes = 184 result.results() == null 185 ? ImmutableList.<ChangeRequest>of() 186 : Iterables.transform( 187 result.results(), 188 ChangeRequest.fromPbFunction(serviceOptions.getService(), zoneName)); 189 return new PageImpl<>( 190 new ChangeRequestPageFetcher(zoneName, serviceOptions, cursor, optionsMap), 191 cursor, 192 changes); 193 } catch (RetryHelper.RetryHelperException e) { 194 throw DnsException.translateAndThrow(e); 195 } 196 } 197 198 @Override 199 public Page<RecordSet> listRecordSets(String zoneName, RecordSetListOption... options) { 200 return listRecordSets(zoneName, getOptions(), optionMap(options)); 201 } 202 203 private static Page<RecordSet> listRecordSets( 204 final String zoneName, 205 final DnsOptions serviceOptions, 206 final Map<DnsRpc.Option, ?> optionsMap) { 207 try { 208 // get a list of record sets 209 final DnsRpc rpc = serviceOptions.getDnsRpcV1(); 210 DnsRpc.ListResult<ResourceRecordSet> result = 211 runWithRetries( 212 new Callable<DnsRpc.ListResult<ResourceRecordSet>>() { 213 @Override 214 public DnsRpc.ListResult<ResourceRecordSet> call() { 215 return rpc.listRecordSets(zoneName, optionsMap); 216 } 217 }, 218 serviceOptions.getRetrySettings(), 219 EXCEPTION_HANDLER, 220 serviceOptions.getClock()); 221 String cursor = result.pageToken(); 222 // transform that list into record sets 223 Iterable<RecordSet> recordSets = 224 result.results() == null 225 ? ImmutableList.<RecordSet>of() 226 : Iterables.transform(result.results(), RecordSet.FROM_PB_FUNCTION); 227 return new PageImpl<>( 228 new RecordSetPageFetcher(zoneName, serviceOptions, cursor, optionsMap), 229 cursor, 230 recordSets); 231 } catch (RetryHelper.RetryHelperException e) { 232 throw DnsException.translateAndThrow(e); 233 } 234 } 235 236 @Override 237 public Zone create(final ZoneInfo zoneInfo, Dns.ZoneOption... options) { 238 final Map<DnsRpc.Option, ?> optionsMap = optionMap(options); 239 try { 240 ManagedZone answer = 241 runWithRetries( 242 new Callable<ManagedZone>() { 243 @Override 244 public ManagedZone call() { 245 return dnsRpc.create(zoneInfo.toPb(), optionsMap); 246 } 247 }, 248 getOptions().getRetrySettings(), 249 EXCEPTION_HANDLER, 250 getOptions().getClock()); 251 return answer == null ? null : Zone.fromPb(this, answer); 252 } catch (RetryHelper.RetryHelperException ex) { 253 throw DnsException.translateAndThrow(ex); 254 } 255 } 256 257 @Override 258 public Zone getZone(final String zoneName, Dns.ZoneOption... options) { 259 final Map<DnsRpc.Option, ?> optionsMap = optionMap(options); 260 try { 261 ManagedZone answer = 262 runWithRetries( 263 new Callable<ManagedZone>() { 264 @Override 265 public ManagedZone call() { 266 return dnsRpc.getZone(zoneName, optionsMap); 267 } 268 }, 269 getOptions().getRetrySettings(), 270 EXCEPTION_HANDLER, 271 getOptions().getClock()); 272 return answer == null ? null : Zone.fromPb(this, answer); 273 } catch (RetryHelper.RetryHelperException ex) { 274 throw DnsException.translateAndThrow(ex); 275 } 276 } 277 278 @Override 279 public boolean delete(final String zoneName) { 280 try { 281 return runWithRetries( 282 new Callable<Boolean>() { 283 @Override 284 public Boolean call() { 285 return dnsRpc.deleteZone(zoneName); 286 } 287 }, 288 getOptions().getRetrySettings(), 289 EXCEPTION_HANDLER, 290 getOptions().getClock()); 291 } catch (RetryHelper.RetryHelperException ex) { 292 throw DnsException.translateAndThrow(ex); 293 } 294 } 295 296 @Override 297 public ProjectInfo getProject(Dns.ProjectOption... fields) { 298 final Map<DnsRpc.Option, ?> optionsMap = optionMap(fields); 299 try { 300 Project answer = 301 runWithRetries( 302 new Callable<Project>() { 303 @Override 304 public Project call() { 305 return dnsRpc.getProject(optionsMap); 306 } 307 }, 308 getOptions().getRetrySettings(), 309 EXCEPTION_HANDLER, 310 getOptions().getClock()); 311 return answer == null ? null : ProjectInfo.fromPb(answer); // should never be null 312 } catch (RetryHelper.RetryHelperException ex) { 313 throw DnsException.translateAndThrow(ex); 314 } 315 } 316 317 @Override 318 public ChangeRequest applyChangeRequest( 319 final String zoneName, 320 final ChangeRequestInfo changeRequest, 321 ChangeRequestOption... options) { 322 final Map<DnsRpc.Option, ?> optionsMap = optionMap(options); 323 try { 324 Change answer = 325 runWithRetries( 326 new Callable<Change>() { 327 @Override 328 public Change call() { 329 return dnsRpc.applyChangeRequest(zoneName, changeRequest.toPb(), optionsMap); 330 } 331 }, 332 getOptions().getRetrySettings(), 333 EXCEPTION_HANDLER, 334 getOptions().getClock()); 335 return answer == null ? null : ChangeRequest.fromPb(this, zoneName, answer); // not null 336 } catch (RetryHelper.RetryHelperException ex) { 337 throw DnsException.translateAndThrow(ex); 338 } 339 } 340 341 @Override 342 public ChangeRequest getChangeRequest( 343 final String zoneName, final String changeRequestId, Dns.ChangeRequestOption... options) { 344 final Map<DnsRpc.Option, ?> optionsMap = optionMap(options); 345 try { 346 Change answer = 347 runWithRetries( 348 new Callable<Change>() { 349 @Override 350 public Change call() { 351 return dnsRpc.getChangeRequest(zoneName, changeRequestId, optionsMap); 352 } 353 }, 354 getOptions().getRetrySettings(), 355 EXCEPTION_HANDLER, 356 getOptions().getClock()); 357 return answer == null ? null : ChangeRequest.fromPb(this, zoneName, answer); 358 } catch (RetryHelper.RetryHelperException ex) { 359 throw DnsException.translateAndThrow(ex); 360 } 361 } 362 363 @Override 364 public DnsBatch batch() { 365 return new DnsBatch(this.getOptions()); 366 } 367 368 static Map<DnsRpc.Option, ?> optionMap(Option... options) { 369 Map<DnsRpc.Option, Object> temp = Maps.newEnumMap(DnsRpc.Option.class); 370 for (Option option : options) { 371 Object prev = temp.put(option.getRpcOption(), option.getValue()); 372 checkArgument(prev == null, "Duplicate option %s", option); 373 } 374 return ImmutableMap.copyOf(temp); 375 } 376 } 377