-
Notifications
You must be signed in to change notification settings - Fork 41
Improved pagination in GCE #62
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -393,7 +393,8 @@ public void suspendNode(String name) { | |
| private LoginCredentials getFromImageAndOverrideIfRequired(org.jclouds.compute.domain.Image image, | ||
| GoogleComputeEngineTemplateOptions options) { | ||
| LoginCredentials defaultCredentials = image.getDefaultCredentials(); | ||
| String[] keys = defaultCredentials.getPrivateKey().split(":"); | ||
| // Atthis point the private key must exist | ||
| String[] keys = defaultCredentials.getOptionalPrivateKey().get().split(":"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is fine, but I will sidebar: particularly with java8 having optional, I feel we should divest or at least stop proliferating this type. Nullable is fine. I don't want to get to a very long debate about it, just raising my opinion on the matter.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree. My intent was only to remove the warnings, but not start a major cleanup of the provider, but I'd be happy to start a global cleanup PR in the provider to address all things we want to improve. |
||
| String publicKey = keys[0]; | ||
| String privateKey = keys[1]; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -119,8 +119,7 @@ public Set<SecurityGroup> listSecurityGroupsForNode(String id) { | |
| return ImmutableSet.of(); | ||
| } | ||
|
|
||
| ImmutableSet.Builder builder = ImmutableSet.builder(); | ||
|
|
||
| ImmutableSet.Builder<SecurityGroup> builder = ImmutableSet.builder(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another thing that is fine, but I'd say in the future we should stop Set'ing everything. List is fine for things like this. |
||
|
|
||
| for (NetworkInterface nwInterface : instance.getNetworkInterfaces()) { | ||
| String networkUrl = nwInterface.getNetwork().getPath(); | ||
|
|
@@ -171,7 +170,7 @@ public boolean removeSecurityGroup(String id) { | |
|
|
||
| ListOptions options = new ListOptions.Builder().filter("network eq .*/" + id); | ||
|
|
||
| FluentIterable<Firewall> fws = api.getFirewallApiForProject(userProject.get()).list(options).concat(); | ||
| FluentIterable<Firewall> fws = api.getFirewallApiForProject(userProject.get()).list(options).toPagedIterable().concat(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean this? FluentIterable.from(api.getFirewallApiForProject(userProject.get())
.list(options)
.toPagedIterable()
.concat());The PagedIterable#concat already returns a FluentIterable, so that won't be needed (and that method in Guava is deprecated). Are you in the direction of changing the |
||
|
|
||
| for (Firewall fw : fws) { | ||
| AtomicReference<Operation> operation = Atomics.newReference(api.getFirewallApiForProject(userProject.get()) | ||
|
|
@@ -203,7 +202,8 @@ public SecurityGroup addIpPermission(IpPermission ipPermission, SecurityGroup gr | |
|
|
||
| ListOptions options = new ListOptions.Builder().filter("network eq .*/" + group.getName()); | ||
|
|
||
| if (api.getFirewallApiForProject(userProject.get()).list(options).concat().anyMatch(providesIpPermission(ipPermission))) { | ||
| FluentIterable<Firewall> fws = api.getFirewallApiForProject(userProject.get()).list(options).toPagedIterable().concat(); | ||
| if (fws.anyMatch(providesIpPermission(ipPermission))) { | ||
| // Permission already exists. | ||
| return group; | ||
| } | ||
|
|
@@ -268,7 +268,7 @@ public SecurityGroup removeIpPermission(IpPermission ipPermission, SecurityGroup | |
|
|
||
| ListOptions options = new ListOptions.Builder().filter("network eq .*/" + group.getName()); | ||
|
|
||
| FluentIterable<Firewall> fws = api.getFirewallApiForProject(userProject.get()).list(options).concat(); | ||
| FluentIterable<Firewall> fws = api.getFirewallApiForProject(userProject.get()).list(options).toPagedIterable().concat(); | ||
|
|
||
| for (Firewall fw : fws) { | ||
| if (equalsIpPermission(ipPermission).apply(fw)) { | ||
|
|
@@ -328,7 +328,7 @@ public boolean supportsExclusionCidrBlocks() { | |
|
|
||
| private SecurityGroup groupForTagsInNetwork(Network nw, final Set <String> tags) { | ||
| ListOptions opts = new Builder().filter("network eq .*/" + nw.getName()); | ||
| Set<Firewall> fws = api.getFirewallApiForProject(userProject.get()).list(opts).concat() | ||
| Set<Firewall> fws = api.getFirewallApiForProject(userProject.get()).list(opts).toPagedIterable().concat() | ||
| .filter(new Predicate<Firewall>() { | ||
| @Override | ||
| public boolean apply(final Firewall input) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,120 +16,59 @@ | |
| */ | ||
| package org.jclouds.googlecomputeengine.domain; | ||
|
|
||
| import static com.google.common.base.MoreObjects.toStringHelper; | ||
| import static com.google.common.base.Objects.equal; | ||
| import static com.google.common.base.Preconditions.checkNotNull; | ||
| import static org.jclouds.googlecomputeengine.domain.Resource.Kind; | ||
|
|
||
| import java.beans.ConstructorProperties; | ||
| import java.util.Iterator; | ||
|
|
||
| import org.jclouds.collect.IterableWithMarker; | ||
| import org.jclouds.collect.IterableWithMarkers; | ||
| import org.jclouds.collect.PagedIterable; | ||
| import org.jclouds.collect.PagedIterables; | ||
| import org.jclouds.googlecomputeengine.domain.Resource.Kind; | ||
|
|
||
| import com.google.common.base.MoreObjects; | ||
| import com.google.common.base.Objects; | ||
| import com.google.common.base.Function; | ||
| import com.google.common.base.Optional; | ||
| import com.google.common.collect.ImmutableList; | ||
| import com.google.common.collect.ImmutableSet; | ||
|
|
||
| /** | ||
| * The collection returned from any <code>listFirstPage()</code> method. | ||
| * A single page returned from a paginated collection that knows how to advance | ||
| * to the next page. | ||
| */ | ||
| public class ListPage<T> extends IterableWithMarker<T> { | ||
|
|
||
| private final Kind kind; | ||
| private final String nextPageToken; | ||
| private final Iterable<T> items; | ||
| private final PageWithMarker<T> delegate; | ||
| private final Function<PageWithMarker<T>, PagedIterable<T>> advancingFunction; | ||
|
|
||
| @ConstructorProperties({ "kind", "nextPageToken", "items" }) | ||
| protected ListPage(Kind kind, String nextPageToken, Iterable<T> items) { | ||
| this.kind = checkNotNull(kind, "kind"); | ||
| this.nextPageToken = nextPageToken; | ||
| this.items = items != null ? ImmutableList.copyOf(items) : ImmutableList.<T>of(); | ||
| public ListPage(PageWithMarker<T> delegate, Function<PageWithMarker<T>, PagedIterable<T>> advancingFunction) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. prefer to separate listpage so that it can be a simple data type. offering an additional method or otherwise mean to construct an advancing list would be more ideal than having all lists have additional internal state that includes references to jclouds context internals. For example, a reference to this list will keep a lot of a jclouds context from being gc'd, and the user may not suspect that. I'm not saying we don't already do things like this. Just suggesting that today, not sure we would always prefer to conflate the list with its advancing function for all use cases. Simple way out is to make an alternate method that returns a normal list.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we want to be able to build a PagedIterable given single page (IterableWithMarker or ListPage) we need the advancing function. Wether it is already set inside the list object or we use an alternate method to build it (such as PagedIterables#advance) we need to hold a reference to that function as long as the List object exists; otherwise it won't be able to lazily fetch new pages as they are needed. This said, I tried to hide the advancing function from users because it might be difficult to get:
That made me go for the Just to make sure I understand your concern: we could be leaking the reference to the advancing function only in the case the user does not want to advance to the next page. When getting the "entire" list provided by the PagedIterable, the advancing function is always needed. IMO, the former use case might not be the most used one, as the method that returns the ListPage is the one used when filtering resources or performing searches, but that's only a guess; we really don't know which of both methods will be used more. I see two options to move forward:
Am I getting the picture? WDYT?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will followup more completely later.. maybe :) since maybe I will answer One thing to keep in mind that in many cases there will be only one page of It may be possible in the current design to special case this. Also we had a meetup with clojure folks back in the day (specifically toni In all, I think this change is a step backwards or at best neutral, from a I am not focused on it or the several other patterns we use, and admit that In the mean time, feel free to merge this, taking into consideration what
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In fact, I first implemented it by returning a regular Anyway, I'm OK to change to that approach, but I'll also refactor the majority of the tests. Most of the expect tests are a mess, and live tests that use lists have a lot of improvement, so I'll change them and refactor all expect tests to MWS ones. It will take some more time, but we'll have a cleaner provider and the tests in the way we want them :) Thanks for the feedback!
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no problemo. I like that you are thinking about things bigger than (but |
||
| this.delegate = checkNotNull(delegate, "delegate"); | ||
| this.advancingFunction = checkNotNull(advancingFunction, "advancingFunction"); | ||
| } | ||
|
|
||
| public Kind getKind() { | ||
| return kind; | ||
| } | ||
|
|
||
| @Override | ||
| public Optional<Object> nextMarker() { | ||
| return Optional.<Object>fromNullable(nextPageToken); | ||
| public PagedIterable<T> toPagedIterable() { | ||
| return advancingFunction.apply(delegate); | ||
| } | ||
|
|
||
| @Override | ||
| public Iterator<T> iterator() { | ||
| return checkNotNull(items, "items").iterator(); | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hashCode(kind, items); | ||
| return delegate.iterator(); | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object obj) { | ||
| if (this == obj) | ||
| return true; | ||
| if (obj == null || getClass() != obj.getClass()) | ||
| return false; | ||
| ListPage<?> that = ListPage.class.cast(obj); | ||
| return equal(this.kind, that.kind) && equal(this.items, that.items); | ||
| } | ||
|
|
||
| protected MoreObjects.ToStringHelper string() { | ||
| return toStringHelper(this).omitNullValues().add("kind", kind).add("nextPageToken", nextPageToken) | ||
| .add("items", items); | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return string().toString(); | ||
| } | ||
|
|
||
| public static <T> Builder<T> builder() { | ||
| return new Builder<T>(); | ||
| } | ||
|
|
||
| public Builder<T> toBuilder() { | ||
| return new Builder<T>().fromPagedList(this); | ||
| public Optional<Object> nextMarker() { | ||
| return delegate.nextMarker(); | ||
| } | ||
|
|
||
| public static final class Builder<T> { | ||
|
|
||
| private Kind kind; | ||
| private String nextPageToken; | ||
| private ImmutableList.Builder<T> items = ImmutableList.builder(); | ||
|
|
||
| public Builder<T> kind(Kind kind) { | ||
| this.kind = kind; | ||
| return this; | ||
| } | ||
|
|
||
| public Builder<T> addItem(T item) { | ||
| this.items.add(item); | ||
| return this; | ||
| } | ||
|
|
||
| public Builder<T> items(Iterable<T> items) { | ||
| this.items.addAll(items); | ||
| return this; | ||
| } | ||
| public static class Empty<T> extends ListPage<T> { | ||
|
|
||
| public Builder<T> nextPageToken(String nextPageToken) { | ||
| this.nextPageToken = nextPageToken; | ||
| return this; | ||
| public Empty(Kind kind) { | ||
| super(PageWithMarker.<T>builder().kind(kind).build(), new Function<PageWithMarker<T>, PagedIterable<T>>() { | ||
| @Override | ||
| public PagedIterable<T> apply(PageWithMarker<T> input) { | ||
| return PagedIterables.onlyPage(IterableWithMarkers.from(ImmutableSet.<T>of())); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| public ListPage<T> build() { | ||
| return new ListPage<T>(kind, nextPageToken, items.build()); | ||
| } | ||
|
|
||
| public Builder<T> fromPagedList(ListPage<T> in) { | ||
| return this | ||
| .kind(in.getKind()) | ||
| .nextPageToken((String) in.nextMarker().orNull()) | ||
| .items(in); | ||
|
|
||
| } | ||
| } | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
git seems to know something I don't know!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes :) I removed the PATCH class as it already exists in jclouds-core. That seems to have fooled git.