|  |  | @@ -85,18 +85,20 @@ type GCECloud struct { | 
			
		
	
		
		
			
				
					
					|  |  |  | 	managedZones             []string // List of zones we are spanning (for Ubernetes-Lite, primarily when running on master) |  |  |  | 	managedZones             []string // List of zones we are spanning (for Ubernetes-Lite, primarily when running on master) | 
			
		
	
		
		
			
				
					
					|  |  |  | 	networkURL               string |  |  |  | 	networkURL               string | 
			
		
	
		
		
			
				
					
					|  |  |  | 	nodeTags                 []string // List of tags to use on firewall rules for load balancers |  |  |  | 	nodeTags                 []string // List of tags to use on firewall rules for load balancers | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 	nodeInstancePrefix       string   // If non-"", an advisory prefix for all nodes in the cluster | 
			
		
	
		
		
			
				
					
					|  |  |  | 	useMetadataServer        bool |  |  |  | 	useMetadataServer        bool | 
			
		
	
		
		
			
				
					
					|  |  |  | 	operationPollRateLimiter flowcontrol.RateLimiter |  |  |  | 	operationPollRateLimiter flowcontrol.RateLimiter | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | type Config struct { |  |  |  | type Config struct { | 
			
		
	
		
		
			
				
					
					|  |  |  | 	Global struct { |  |  |  | 	Global struct { | 
			
		
	
		
		
			
				
					
					|  |  |  | 		TokenURL    string   `gcfg:"token-url"` |  |  |  | 		TokenURL           string   `gcfg:"token-url"` | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		TokenBody   string   `gcfg:"token-body"` |  |  |  | 		TokenBody          string   `gcfg:"token-body"` | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		ProjectID   string   `gcfg:"project-id"` |  |  |  | 		ProjectID          string   `gcfg:"project-id"` | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		NetworkName string   `gcfg:"network-name"` |  |  |  | 		NetworkName        string   `gcfg:"network-name"` | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		NodeTags    []string `gcfg:"node-tags"` |  |  |  | 		NodeTags           []string `gcfg:"node-tags"` | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		Multizone   bool     `gcfg:"multizone"` |  |  |  | 		NodeInstancePrefix string   `gcfg:"node-instance-prefix"` | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		Multizone          bool     `gcfg:"multizone"` | 
			
		
	
		
		
			
				
					
					|  |  |  | 	} |  |  |  | 	} | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -260,6 +262,7 @@ func newGCECloud(config io.Reader) (*GCECloud, error) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 	tokenSource := google.ComputeTokenSource("") |  |  |  | 	tokenSource := google.ComputeTokenSource("") | 
			
		
	
		
		
			
				
					
					|  |  |  | 	var nodeTags []string |  |  |  | 	var nodeTags []string | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 	var nodeInstancePrefix string | 
			
		
	
		
		
			
				
					
					|  |  |  | 	if config != nil { |  |  |  | 	if config != nil { | 
			
		
	
		
		
			
				
					
					|  |  |  | 		var cfg Config |  |  |  | 		var cfg Config | 
			
		
	
		
		
			
				
					
					|  |  |  | 		if err := gcfg.ReadInto(&cfg, config); err != nil { |  |  |  | 		if err := gcfg.ReadInto(&cfg, config); err != nil { | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -281,19 +284,20 @@ func newGCECloud(config io.Reader) (*GCECloud, error) { | 
			
		
	
		
		
			
				
					
					|  |  |  | 			tokenSource = NewAltTokenSource(cfg.Global.TokenURL, cfg.Global.TokenBody) |  |  |  | 			tokenSource = NewAltTokenSource(cfg.Global.TokenURL, cfg.Global.TokenBody) | 
			
		
	
		
		
			
				
					
					|  |  |  | 		} |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  | 		nodeTags = cfg.Global.NodeTags |  |  |  | 		nodeTags = cfg.Global.NodeTags | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		nodeInstancePrefix = cfg.Global.NodeInstancePrefix | 
			
		
	
		
		
			
				
					
					|  |  |  | 		if cfg.Global.Multizone { |  |  |  | 		if cfg.Global.Multizone { | 
			
		
	
		
		
			
				
					
					|  |  |  | 			managedZones = nil // Use all zones in region |  |  |  | 			managedZones = nil // Use all zones in region | 
			
		
	
		
		
			
				
					
					|  |  |  | 		} |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  | 	} |  |  |  | 	} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 	return CreateGCECloud(projectID, region, zone, managedZones, networkURL, nodeTags, tokenSource, true /* useMetadataServer */) |  |  |  | 	return CreateGCECloud(projectID, region, zone, managedZones, networkURL, nodeTags, nodeInstancePrefix, tokenSource, true /* useMetadataServer */) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | // Creates a GCECloud object using the specified parameters. |  |  |  | // Creates a GCECloud object using the specified parameters. | 
			
		
	
		
		
			
				
					
					|  |  |  | // If no networkUrl is specified, loads networkName via rest call. |  |  |  | // If no networkUrl is specified, loads networkName via rest call. | 
			
		
	
		
		
			
				
					
					|  |  |  | // If no tokenSource is specified, uses oauth2.DefaultTokenSource. |  |  |  | // If no tokenSource is specified, uses oauth2.DefaultTokenSource. | 
			
		
	
		
		
			
				
					
					|  |  |  | // If managedZones is nil / empty all zones in the region will be managed. |  |  |  | // If managedZones is nil / empty all zones in the region will be managed. | 
			
		
	
		
		
			
				
					
					|  |  |  | func CreateGCECloud(projectID, region, zone string, managedZones []string, networkURL string, nodeTags []string, tokenSource oauth2.TokenSource, useMetadataServer bool) (*GCECloud, error) { |  |  |  | func CreateGCECloud(projectID, region, zone string, managedZones []string, networkURL string, nodeTags []string, nodeInstancePrefix string, tokenSource oauth2.TokenSource, useMetadataServer bool) (*GCECloud, error) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 	if tokenSource == nil { |  |  |  | 	if tokenSource == nil { | 
			
		
	
		
		
			
				
					
					|  |  |  | 		var err error |  |  |  | 		var err error | 
			
		
	
		
		
			
				
					
					|  |  |  | 		tokenSource, err = google.DefaultTokenSource( |  |  |  | 		tokenSource, err = google.DefaultTokenSource( | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -348,6 +352,7 @@ func CreateGCECloud(projectID, region, zone string, managedZones []string, netwo | 
			
		
	
		
		
			
				
					
					|  |  |  | 		managedZones:             managedZones, |  |  |  | 		managedZones:             managedZones, | 
			
		
	
		
		
			
				
					
					|  |  |  | 		networkURL:               networkURL, |  |  |  | 		networkURL:               networkURL, | 
			
		
	
		
		
			
				
					
					|  |  |  | 		nodeTags:                 nodeTags, |  |  |  | 		nodeTags:                 nodeTags, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		nodeInstancePrefix:       nodeInstancePrefix, | 
			
		
	
		
		
			
				
					
					|  |  |  | 		useMetadataServer:        useMetadataServer, |  |  |  | 		useMetadataServer:        useMetadataServer, | 
			
		
	
		
		
			
				
					
					|  |  |  | 		operationPollRateLimiter: operationPollRateLimiter, |  |  |  | 		operationPollRateLimiter: operationPollRateLimiter, | 
			
		
	
		
		
			
				
					
					|  |  |  | 	}, nil |  |  |  | 	}, nil | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -1017,9 +1022,20 @@ func (gce *GCECloud) firewallObject(name, region, desc string, sourceRanges nets | 
			
		
	
		
		
			
				
					
					|  |  |  | // is unspecified |  |  |  | // is unspecified | 
			
		
	
		
		
			
				
					
					|  |  |  | func (gce *GCECloud) computeHostTags(hosts []*gceInstance) ([]string, error) { |  |  |  | func (gce *GCECloud) computeHostTags(hosts []*gceInstance) ([]string, error) { | 
			
		
	
		
		
			
				
					
					|  |  |  | 	// TODO: We could store the tags in gceInstance, so we could have already fetched it |  |  |  | 	// TODO: We could store the tags in gceInstance, so we could have already fetched it | 
			
		
	
		
		
			
				
					
					|  |  |  | 	hostNamesByZone := make(map[string][]string) |  |  |  | 	hostNamesByZone := make(map[string]map[string]bool) // map of zones -> map of names -> bool (for easy lookup) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 	nodeInstancePrefix := gce.nodeInstancePrefix | 
			
		
	
		
		
			
				
					
					|  |  |  | 	for _, host := range hosts { |  |  |  | 	for _, host := range hosts { | 
			
		
	
		
		
			
				
					
					|  |  |  | 		hostNamesByZone[host.Zone] = append(hostNamesByZone[host.Zone], host.Name) |  |  |  | 		if !strings.HasPrefix(host.Name, gce.nodeInstancePrefix) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			glog.Warningf("instance '%s' does not conform to prefix '%s', ignoring filter", host, gce.nodeInstancePrefix) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			nodeInstancePrefix = "" | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		z, ok := hostNamesByZone[host.Zone] | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		if !ok { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			z = make(map[string]bool) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			hostNamesByZone[host.Zone] = z | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		z[host.Name] = true | 
			
		
	
		
		
			
				
					
					|  |  |  | 	} |  |  |  | 	} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 	tags := sets.NewString() |  |  |  | 	tags := sets.NewString() | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -1030,11 +1046,14 @@ func (gce *GCECloud) computeHostTags(hosts []*gceInstance) ([]string, error) { | 
			
		
	
		
		
			
				
					
					|  |  |  | 		for ; page == 0 || (pageToken != "" && page < maxPages); page++ { |  |  |  | 		for ; page == 0 || (pageToken != "" && page < maxPages); page++ { | 
			
		
	
		
		
			
				
					
					|  |  |  | 			listCall := gce.service.Instances.List(gce.projectID, zone) |  |  |  | 			listCall := gce.service.Instances.List(gce.projectID, zone) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 			// Add the filter for hosts |  |  |  | 			if nodeInstancePrefix != "" { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 			listCall = listCall.Filter("name eq (" + strings.Join(hostNames, "|") + ")") |  |  |  | 				// Add the filter for hosts | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				listCall = listCall.Filter("name eq " + nodeInstancePrefix + ".*") | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 			// Add the fields we want |  |  |  | 			// Add the fields we want | 
			
		
	
		
		
			
				
					
					|  |  |  | 			listCall = listCall.Fields("items(name,tags)") |  |  |  | 			// TODO(zmerlynn): Internal bug 29524655 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			// listCall = listCall.Fields("items(name,tags)") | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 			if pageToken != "" { |  |  |  | 			if pageToken != "" { | 
			
		
	
		
		
			
				
					
					|  |  |  | 				listCall = listCall.PageToken(pageToken) |  |  |  | 				listCall = listCall.PageToken(pageToken) | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -1046,6 +1065,10 @@ func (gce *GCECloud) computeHostTags(hosts []*gceInstance) ([]string, error) { | 
			
		
	
		
		
			
				
					
					|  |  |  | 			} |  |  |  | 			} | 
			
		
	
		
		
			
				
					
					|  |  |  | 			pageToken = res.NextPageToken |  |  |  | 			pageToken = res.NextPageToken | 
			
		
	
		
		
			
				
					
					|  |  |  | 			for _, instance := range res.Items { |  |  |  | 			for _, instance := range res.Items { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				if !hostNames[instance.Name] { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					continue | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 				longest_tag := "" |  |  |  | 				longest_tag := "" | 
			
		
	
		
		
			
				
					
					|  |  |  | 				for _, tag := range instance.Tags.Items { |  |  |  | 				for _, tag := range instance.Tags.Items { | 
			
		
	
		
		
			
				
					
					|  |  |  | 					if strings.HasPrefix(instance.Name, tag) && len(tag) > len(longest_tag) { |  |  |  | 					if strings.HasPrefix(instance.Name, tag) && len(tag) > len(longest_tag) { | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2429,21 +2452,20 @@ type gceDisk struct { | 
			
		
	
		
		
			
				
					
					|  |  |  | // Gets the named instances, returning cloudprovider.InstanceNotFound if any instance is not found |  |  |  | // Gets the named instances, returning cloudprovider.InstanceNotFound if any instance is not found | 
			
		
	
		
		
			
				
					
					|  |  |  | func (gce *GCECloud) getInstancesByNames(names []string) ([]*gceInstance, error) { |  |  |  | func (gce *GCECloud) getInstancesByNames(names []string) ([]*gceInstance, error) { | 
			
		
	
		
		
			
				
					
					|  |  |  | 	instances := make(map[string]*gceInstance) |  |  |  | 	instances := make(map[string]*gceInstance) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 	remaining := len(names) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 	nodeInstancePrefix := gce.nodeInstancePrefix | 
			
		
	
		
		
			
				
					
					|  |  |  | 	for _, name := range names { |  |  |  | 	for _, name := range names { | 
			
		
	
		
		
			
				
					
					|  |  |  | 		name = canonicalizeInstanceName(name) |  |  |  | 		name = canonicalizeInstanceName(name) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		if !strings.HasPrefix(name, gce.nodeInstancePrefix) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			glog.Warningf("instance '%s' does not conform to prefix '%s', removing filter", name, gce.nodeInstancePrefix) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			nodeInstancePrefix = "" | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  | 		instances[name] = nil |  |  |  | 		instances[name] = nil | 
			
		
	
		
		
			
				
					
					|  |  |  | 	} |  |  |  | 	} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 	for _, zone := range gce.managedZones { |  |  |  | 	for _, zone := range gce.managedZones { | 
			
		
	
		
		
			
				
					
					|  |  |  | 		var remaining []string |  |  |  | 		if remaining == 0 { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		for name, instance := range instances { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 			if instance == nil { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 				remaining = append(remaining, name) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 			} |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		} |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 		if len(remaining) == 0 { |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 			break |  |  |  | 			break | 
			
		
	
		
		
			
				
					
					|  |  |  | 		} |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2452,10 +2474,13 @@ func (gce *GCECloud) getInstancesByNames(names []string) ([]*gceInstance, error) | 
			
		
	
		
		
			
				
					
					|  |  |  | 		for ; page == 0 || (pageToken != "" && page < maxPages); page++ { |  |  |  | 		for ; page == 0 || (pageToken != "" && page < maxPages); page++ { | 
			
		
	
		
		
			
				
					
					|  |  |  | 			listCall := gce.service.Instances.List(gce.projectID, zone) |  |  |  | 			listCall := gce.service.Instances.List(gce.projectID, zone) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 			// Add the filter for hosts |  |  |  | 			if nodeInstancePrefix != "" { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 			listCall = listCall.Filter("name eq (" + strings.Join(remaining, "|") + ")") |  |  |  | 				// Add the filter for hosts | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				listCall = listCall.Filter("name eq " + nodeInstancePrefix + ".*") | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 			listCall = listCall.Fields("items(name,id,disks,machineType)") |  |  |  | 			// TODO(zmerlynn): Internal bug 29524655 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 			// listCall = listCall.Fields("items(name,id,disks,machineType)") | 
			
		
	
		
		
			
				
					
					|  |  |  | 			if pageToken != "" { |  |  |  | 			if pageToken != "" { | 
			
		
	
		
		
			
				
					
					|  |  |  | 				listCall.PageToken(pageToken) |  |  |  | 				listCall.PageToken(pageToken) | 
			
		
	
		
		
			
				
					
					|  |  |  | 			} |  |  |  | 			} | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2467,6 +2492,10 @@ func (gce *GCECloud) getInstancesByNames(names []string) ([]*gceInstance, error) | 
			
		
	
		
		
			
				
					
					|  |  |  | 			pageToken = res.NextPageToken |  |  |  | 			pageToken = res.NextPageToken | 
			
		
	
		
		
			
				
					
					|  |  |  | 			for _, i := range res.Items { |  |  |  | 			for _, i := range res.Items { | 
			
		
	
		
		
			
				
					
					|  |  |  | 				name := i.Name |  |  |  | 				name := i.Name | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				if _, ok := instances[name]; !ok { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 					continue | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 				instance := &gceInstance{ |  |  |  | 				instance := &gceInstance{ | 
			
		
	
		
		
			
				
					
					|  |  |  | 					Zone:  zone, |  |  |  | 					Zone:  zone, | 
			
		
	
		
		
			
				
					
					|  |  |  | 					Name:  name, |  |  |  | 					Name:  name, | 
			
		
	
	
		
		
			
				
					
					|  |  | @@ -2475,6 +2504,7 @@ func (gce *GCECloud) getInstancesByNames(names []string) ([]*gceInstance, error) | 
			
		
	
		
		
			
				
					
					|  |  |  | 					Type:  lastComponent(i.MachineType), |  |  |  | 					Type:  lastComponent(i.MachineType), | 
			
		
	
		
		
			
				
					
					|  |  |  | 				} |  |  |  | 				} | 
			
		
	
		
		
			
				
					
					|  |  |  | 				instances[name] = instance |  |  |  | 				instances[name] = instance | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 				remaining-- | 
			
		
	
		
		
			
				
					
					|  |  |  | 			} |  |  |  | 			} | 
			
		
	
		
		
			
				
					
					|  |  |  | 		} |  |  |  | 		} | 
			
		
	
		
		
			
				
					
					|  |  |  | 		if page >= maxPages { |  |  |  | 		if page >= maxPages { | 
			
		
	
	
		
		
			
				
					
					|  |  |   |