Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Periodically update and store tenant contact information #6784

Merged
merged 3 commits into from Sep 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.api.integration.organization;

import com.google.inject.Inject;
import com.yahoo.component.AbstractComponent;
import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId;

import java.net.URI;
Expand All @@ -18,7 +19,7 @@
/**
* @author jvenstad
*/
public class MockOrganization implements Organization {
public class MockOrganization extends AbstractComponent implements Organization {

private final Clock clock;
private final AtomicLong counter = new AtomicLong();
Expand Down Expand Up @@ -89,45 +90,58 @@ public List<? extends List<? extends User>> contactsFor(PropertyId propertyId) {

@Override
public URI issueCreationUri(PropertyId propertyId) {
return URI.create("www.issues.tld/" + propertyId.id());
return properties.getOrDefault(propertyId, new PropertyInfo()).issueUrl;
}

@Override
public URI contactsUri(PropertyId propertyId) {
return URI.create("www.contacts.tld/" + propertyId.id());
return properties.getOrDefault(propertyId, new PropertyInfo()).contactsUrl;
}

@Override
public URI propertyUri(PropertyId propertyId) {
return URI.create("www.properties.tld/" + propertyId.id());
return properties.getOrDefault(propertyId, new PropertyInfo()).propertyUrl;
}

public Map<IssueId, MockIssue> issues() {
return Collections.unmodifiableMap(issues);
}

public void close(IssueId issueId) {
public MockOrganization close(IssueId issueId) {
issues.get(issueId).open = false;
touch(issueId);
return this;
}

public void setDefaultAssigneeFor(PropertyId propertyId, User defaultAssignee) {
properties.get(propertyId).defaultAssignee = defaultAssignee;
public MockOrganization setContactsFor(PropertyId propertyId, List<List<User>> contacts) {
properties.get(propertyId).contacts = contacts;
return this;
}

public void setContactsFor(PropertyId propertyId, List<List<User>> contacts) {
properties.get(propertyId).contacts = contacts;
public MockOrganization setPropertyUrl(PropertyId propertyId, URI url) {
properties.get(propertyId).propertyUrl = url;
return this;
}

public MockOrganization setContactsUrl(PropertyId propertyId, URI url) {
properties.get(propertyId).contactsUrl = url;
return this;
}

public void addProperty(PropertyId propertyId) {
public MockOrganization setIssueUrl(PropertyId propertyId, URI url) {
properties.get(propertyId).issueUrl = url;
return this;
}

public MockOrganization addProperty(PropertyId propertyId) {
properties.put(propertyId, new PropertyInfo());
return this;
}

private void touch(IssueId issueId) {
issues.get(issueId).updated = clock.instant();
}


public class MockIssue {

private Issue issue;
Expand All @@ -148,11 +162,13 @@ private MockIssue(Issue issue) {

}


private class PropertyInfo {

private User defaultAssignee;
private List<List<User>> contacts = Collections.emptyList();
private URI issueUrl;
private URI contactsUrl;
private URI propertyUrl;

}

Expand Down
Expand Up @@ -12,8 +12,8 @@
import com.yahoo.vespa.hosted.controller.api.identifiers.Property;
import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId;
import com.yahoo.vespa.hosted.controller.api.integration.BuildService;
import com.yahoo.vespa.hosted.controller.api.integration.RunDataStore;
import com.yahoo.vespa.hosted.controller.api.integration.MetricsService;
import com.yahoo.vespa.hosted.controller.api.integration.RunDataStore;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactory;
import com.yahoo.vespa.hosted.controller.api.integration.chef.Chef;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServer;
Expand Down Expand Up @@ -79,7 +79,6 @@ public class Controller extends AbstractComponent {
private final ConfigServer configServer;
private final MetricsService metricsService;
private final Chef chef;
private final Organization organization;
private final AthenzClientFactory athenzClientFactory;

/**
Expand Down Expand Up @@ -117,7 +116,6 @@ public Controller(CuratorDb curator, RotationsConfig rotationsConfig,
this.curator = Objects.requireNonNull(curator, "Curator cannot be null");
this.gitHub = Objects.requireNonNull(gitHub, "GitHub cannot be null");
this.entityService = Objects.requireNonNull(entityService, "EntityService cannot be null");
this.organization = Objects.requireNonNull(organization, "Organization cannot be null");
this.globalRoutingService = Objects.requireNonNull(globalRoutingService, "GlobalRoutingService cannot be null");
this.zoneRegistry = Objects.requireNonNull(zoneRegistry, "ZoneRegistry cannot be null");
this.configServer = Objects.requireNonNull(configServer, "ConfigServer cannot be null");
Expand All @@ -136,7 +134,7 @@ public Controller(CuratorDb curator, RotationsConfig rotationsConfig,
Objects.requireNonNull(routingGenerator, "RoutingGenerator cannot be null"),
Objects.requireNonNull(buildService, "BuildService cannot be null"),
clock);
tenantController = new TenantController(this, curator, athenzClientFactory);
tenantController = new TenantController(this, curator, athenzClientFactory, organization);

// Record the version of this controller
curator().writeControllerVersion(this.hostname(), Vtag.currentVersion);
Expand Down Expand Up @@ -289,10 +287,6 @@ public Chef chefClient() {
return chef;
}

public Organization organization() {
return organization;
}

public CuratorDb curator() {
return curator;
}
Expand Down
@@ -0,0 +1,76 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller;

import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.vespa.curator.Lock;
import com.yahoo.vespa.hosted.controller.api.identifiers.Property;
import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId;
import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant;
import com.yahoo.vespa.hosted.controller.tenant.Contact;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;

/**
* A tenant that has been locked for modification. Provides methods for modifying a tenant's fields.
*
* @author mpolden
*/
public class LockedTenant {

private final Lock lock;
private final TenantName name;
private final AthenzDomain domain;
private final Property property;
private final Optional<PropertyId> propertyId;
private final Optional<Contact> contact;

/**
* Should never be constructed directly.
*
* Use {@link TenantController#lockIfPresent(TenantName, Consumer)} or
* {@link TenantController#lockOrThrow(TenantName, Consumer)}
*/
LockedTenant(AthenzTenant tenant, Lock lock) {
this(lock, tenant.name(), tenant.domain(), tenant.property(), tenant.propertyId(), tenant.contact());
}

private LockedTenant(Lock lock, TenantName name, AthenzDomain domain, Property property,
Optional<PropertyId> propertyId, Optional<Contact> contact) {
this.lock = Objects.requireNonNull(lock, "lock must be non-null");
this.name = Objects.requireNonNull(name, "name must be non-null");
this.domain = Objects.requireNonNull(domain, "domain must be non-null");
this.property = Objects.requireNonNull(property, "property must be non-null");
this.propertyId = Objects.requireNonNull(propertyId, "propertyId must be non-null");
this.contact = Objects.requireNonNull(contact, "contact must be non-null");
}

/** Returns a read-only copy of this */
public AthenzTenant get() {
return new AthenzTenant(name, domain, property, propertyId, contact);
}

public LockedTenant with(AthenzDomain domain) {
return new LockedTenant(lock, name, domain, property, propertyId, contact);
}

public LockedTenant with(Property property) {
return new LockedTenant(lock, name, domain, property, propertyId, contact);
}

public LockedTenant with(PropertyId propertyId) {
return new LockedTenant(lock, name, domain, property, Optional.of(propertyId), contact);
}

public LockedTenant with(Contact contact) {
return new LockedTenant(lock, name, domain, property, propertyId, Optional.of(contact));
}

@Override
public String toString() {
return "tenant '" + name + "'";
}

}