Search in sources :

Example 6 with Product

use of org.candlepin.model.Product in project candlepin by candlepin.

the class CandlepinPoolManager method refreshPoolsWithRegeneration.

/*
     * We need to update/regen entitlements in the same transaction we update pools
     * so we don't miss anything
     */
@Transactional
@SuppressWarnings("checkstyle:methodlength")
@Traceable
void refreshPoolsWithRegeneration(SubscriptionServiceAdapter subAdapter, @TraceableParam("owner") Owner owner, boolean lazy) {
    Date now = new Date();
    owner = this.resolveOwner(owner);
    log.info("Refreshing pools for owner: {}", owner);
    Map<String, Subscription> subscriptionMap = new HashMap<>();
    Map<String, ProductData> productMap = new HashMap<>();
    Map<String, ContentData> contentMap = new HashMap<>();
    // Resolve all our subscriptions, products and content to ensure we don't have bad or
    // duplicate inbound data
    log.debug("Fetching subscriptions from adapter...");
    List<Subscription> subscriptions = subAdapter.getSubscriptions(owner);
    log.debug("Done. Processing subscriptions...");
    for (Subscription subscription : subscriptions) {
        if (subscription == null) {
            continue;
        }
        if (subscription.getId() == null) {
            log.error("subscription does not contain a mappable ID: {}", subscription);
            throw new IllegalStateException("subscription does not contain a mappable ID: " + subscription);
        }
        Subscription existingSub = subscriptionMap.get(subscription.getId());
        if (existingSub != null && !existingSub.equals(subscription)) {
            log.warn("Multiple versions of the same subscription received during refresh; " + "discarding duplicate: {} => {}, {}", subscription.getId(), existingSub, subscription);
            continue;
        }
        subscriptionMap.put(subscription.getId(), subscription);
        List<ProductData> products = new LinkedList<>();
        products.add(subscription.getProduct());
        products.add(subscription.getDerivedProduct());
        products.addAll(subscription.getProvidedProducts());
        products.addAll(subscription.getDerivedProvidedProducts());
        for (ProductData product : products) {
            if (product == null) {
                // forward.
                continue;
            }
            if (product.getId() == null) {
                log.error("product does not contain a mappable Red Hat ID: {}", product);
                throw new IllegalStateException("product does not contain a mappable Red Hat ID: " + product);
            }
            // Product is coming from an upstream source; lock it so only upstream can make
            // further changes to it.
            product.setLocked(true);
            ProductData existingProduct = productMap.get(product.getId());
            if (existingProduct != null && !existingProduct.equals(product)) {
                log.warn("Multiple versions of the same product received during refresh; " + "discarding duplicate: {} => {}, {}", product.getId(), existingProduct, product);
            } else {
                productMap.put(product.getId(), product);
                Collection<ProductContentData> pcdCollection = product.getProductContent();
                if (pcdCollection != null) {
                    for (ProductContentData pcd : pcdCollection) {
                        if (pcd == null) {
                            log.error("product contains a null product-content mapping: {}", product);
                            throw new IllegalStateException("product contains a null product-content mapping: " + product);
                        }
                        ContentData content = pcd.getContent();
                        // population validation for us.
                        if (content == null || content.getId() == null) {
                            log.error("product contains a null or incomplete product-content mapping: {}", product);
                            throw new IllegalStateException("product contains a null or incomplete " + "product-content mapping: " + product);
                        }
                        // We need to lock the incoming content here, but doing so will affect
                        // the equality comparison for products. We'll correct them later.
                        ContentData existingContent = contentMap.get(content.getId());
                        if (existingContent != null && !existingContent.equals(content)) {
                            log.warn("Multiple versions of the same content received during refresh; " + "discarding duplicate: {} => {}, {}", content.getId(), existingContent, content);
                        } else {
                            contentMap.put(content.getId(), content);
                        }
                    }
                }
            }
        }
    }
    // Persist content changes
    log.debug("Importing {} content...", contentMap.size());
    // TODO: Find a more efficient way of doing this, preferably within this method
    for (ContentData cdata : contentMap.values()) {
        cdata.setLocked(true);
    }
    Map<String, Content> importedContent = this.contentManager.importContent(owner, contentMap, productMap.keySet()).getImportedEntities();
    log.debug("Importing {} product(s)...", productMap.size());
    ImportResult<Product> importResult = this.productManager.importProducts(owner, productMap, importedContent);
    Map<String, Product> importedProducts = importResult.getImportedEntities();
    Map<String, Product> updatedProducts = importResult.getUpdatedEntities();
    log.debug("Refreshing {} pool(s)...", subscriptionMap.size());
    Iterator<Map.Entry<String, Subscription>> subsIterator = subscriptionMap.entrySet().iterator();
    while (subsIterator.hasNext()) {
        Map.Entry<String, Subscription> entry = subsIterator.next();
        Subscription sub = entry.getValue();
        if (now.after(sub.getEndDate())) {
            log.info("Skipping expired subscription: {}", sub);
            subsIterator.remove();
            continue;
        }
        log.debug("Processing subscription: {}", sub);
        Pool pool = this.convertToMasterPoolImpl(sub, owner, importedProducts);
        this.refreshPoolsForMasterPool(pool, false, lazy, updatedProducts);
    }
    // delete pools whose subscription disappeared:
    log.debug("Deleting pools for absent subscriptions...");
    List<Pool> poolsToDelete = new ArrayList<>();
    for (Pool pool : poolCurator.getPoolsFromBadSubs(owner, subscriptionMap.keySet())) {
        if (this.isManaged(pool)) {
            poolsToDelete.add(pool);
        }
    }
    deletePools(poolsToDelete);
    // TODO: break this call into smaller pieces. There may be lots of floating pools
    log.debug("Updating floating pools...");
    List<Pool> floatingPools = poolCurator.getOwnersFloatingPools(owner);
    updateFloatingPools(floatingPools, lazy, updatedProducts);
    log.info("Refresh pools for owner: {} completed in: {}ms", owner.getKey(), System.currentTimeMillis() - now.getTime());
}
Also used : HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Product(org.candlepin.model.Product) Entry(java.util.Map.Entry) ProductContentData(org.candlepin.model.dto.ProductContentData) ContentData(org.candlepin.model.dto.ContentData) Pool(org.candlepin.model.Pool) SourceSubscription(org.candlepin.model.SourceSubscription) Subscription(org.candlepin.model.dto.Subscription) ProductContentData(org.candlepin.model.dto.ProductContentData) ProductData(org.candlepin.model.dto.ProductData) Date(java.util.Date) LinkedList(java.util.LinkedList) Content(org.candlepin.model.Content) Map(java.util.Map) HashMap(java.util.HashMap) Traceable(org.candlepin.util.Traceable) Transactional(com.google.inject.persist.Transactional)

Example 7 with Product

use of org.candlepin.model.Product in project candlepin by candlepin.

the class CandlepinPoolManager method refreshPoolsForMasterPool.

@Transactional
void refreshPoolsForMasterPool(Pool pool, boolean updateStackDerived, boolean lazy, Map<String, Product> changedProducts) {
    // These don't all necessarily belong to this owner
    List<Pool> subscriptionPools;
    if (pool.getSubscriptionId() != null) {
        subscriptionPools = this.poolCurator.getPoolsBySubscriptionId(pool.getSubscriptionId()).list();
    } else {
        // If we don't have a subscription ID, this *is* the master pool, but we need to use
        // the original, hopefully unmodified pool
        subscriptionPools = pool.getId() != null ? Collections.<Pool>singletonList(this.poolCurator.find(pool.getId())) : Collections.<Pool>singletonList(pool);
    }
    log.debug("Found {} pools for subscription {}", subscriptionPools.size(), pool.getSubscriptionId());
    if (log.isDebugEnabled()) {
        for (Pool p : subscriptionPools) {
            log.debug("    owner={} - {}", p.getOwner().getKey(), p);
        }
    }
    // TODO: Should this be performed by poolRules? Seems like that should be a thing.
    for (Pool subPool : subscriptionPools) {
        Product product = subPool.getProduct();
        if (product != null) {
            Product update = changedProducts.get(product.getId());
            if (update != null) {
                subPool.setProduct(update);
            }
        }
        product = subPool.getDerivedProduct();
        if (product != null) {
            Product update = changedProducts.get(product.getId());
            if (update != null) {
                subPool.setDerivedProduct(update);
            }
        }
    }
    // Cleans up pools on other owners who have migrated subs away
    removeAndDeletePoolsOnOtherOwners(subscriptionPools, pool);
    // capture the original quantity to check for updates later
    Long originalQuantity = pool.getQuantity();
    // BZ 1012386: This will regenerate master/derived for bonus scenarios if only one of the
    // pair still exists.
    createAndEnrichPools(pool, subscriptionPools);
    // don't update floating here, we'll do that later so we don't update anything twice
    Set<String> updatedMasterPools = updatePoolsForMasterPool(subscriptionPools, pool, originalQuantity, updateStackDerived, changedProducts);
    regenerateCertificatesByEntIds(updatedMasterPools, lazy);
}
Also used : Product(org.candlepin.model.Product) Pool(org.candlepin.model.Pool) Transactional(com.google.inject.persist.Transactional)

Example 8 with Product

use of org.candlepin.model.Product in project candlepin by candlepin.

the class CandlepinPoolManager method convertToMasterPool.

/*
     * if you are using this method, you might want to override the quantity
     * with PoolRules.calculateQuantity
     */
@Override
public Pool convertToMasterPool(Subscription sub) {
    if (sub == null) {
        throw new IllegalArgumentException("subscription is null");
    }
    // Resolve the subscription's owner...
    if (sub.getOwner() == null || (sub.getOwner().getId() == null && sub.getOwner().getKey() == null)) {
        throw new IllegalStateException("Subscription references an invalid owner: " + sub.getOwner());
    }
    Owner owner = sub.getOwner().getId() != null ? this.ownerCurator.find(sub.getOwner().getId()) : this.ownerCurator.lookupByKey(sub.getOwner().getKey());
    if (owner == null) {
        throw new IllegalStateException("Subscription references an owner which cannot be resolved: " + sub.getOwner());
    }
    // Gather the product IDs referenced by this subscription...
    Set<ProductData> productData = new HashSet<>();
    Set<String> productIds = new HashSet<>();
    Map<String, Product> productMap = new HashMap<>();
    productData.add(sub.getProduct());
    productData.add(sub.getDerivedProduct());
    if (sub.getProvidedProducts() != null) {
        productData.addAll(sub.getProvidedProducts());
    }
    if (sub.getDerivedProvidedProducts() != null) {
        productData.addAll(sub.getDerivedProvidedProducts());
    }
    for (ProductData pdata : productData) {
        if (pdata != null) {
            if (pdata.getId() == null) {
                throw new IllegalStateException("Subscription references an incomplete product: " + pdata);
            }
            productIds.add(pdata.getId());
        }
    }
    // Build the product map from the product IDs we pulled off the subscription...
    for (Product product : this.ownerProductCurator.getProductsByIds(owner, productIds)) {
        productMap.put(product.getId(), product);
    }
    return this.convertToMasterPoolImpl(sub, owner, productMap);
}
Also used : ProductData(org.candlepin.model.dto.ProductData) Owner(org.candlepin.model.Owner) HashMap(java.util.HashMap) Product(org.candlepin.model.Product) HashSet(java.util.HashSet)

Example 9 with Product

use of org.candlepin.model.Product in project candlepin by candlepin.

the class ContentManager method updateContent.

/**
 * Updates the specified content instance, creating a new version of the content as necessary.
 * The content instance returned by this method is not guaranteed to be the same instance passed
 * in. As such, once this method has been called, callers should only use the instance output by
 * this method.
 *
 * @param owner
 *  The owner for which to update the content
 *
 * @param regenerateEntitlementCerts
 *  Whether or not changes made to the content should trigger the regeneration of entitlement
 *  certificates for affected consumers
 *
 * @throws IllegalStateException
 *  if the given content update references a content that does not exist for the specified owner
 *
 * @throws IllegalArgumentException
 *  if either the provided content entity or owner are null
 *
 * @return
 *  the updated content entity, or a new content entity
 */
@Transactional
public Content updateContent(ContentDTO update, Owner owner, boolean regenerateEntitlementCerts) {
    if (update == null) {
        throw new IllegalArgumentException("update is null");
    }
    if (update.getId() == null) {
        throw new IllegalArgumentException("update is incomplete");
    }
    if (owner == null) {
        throw new IllegalArgumentException("owner is null");
    }
    // Resolve the entity to ensure we're working with the merged entity, and to ensure it's
    // already been created.
    // TODO: FIXME:
    // There's a bug here where if changes are applied to an entity's collections, and then
    // this method is called, the check below will cause the changes to be persisted.
    // This needs to be re-written to use DTOs as the primary source of entity creation, rather
    // than a bolted-on utility method.
    // If we never edit the entity directly, however, this is safe.
    Content entity = this.ownerContentCurator.getContentById(owner, update.getId());
    if (entity == null) {
        // If we're doing an exclusive update, this should be an error condition
        throw new IllegalStateException("Content has not yet been created");
    }
    // TODO: Remove this shim and stop using DTOs in this class
    if (!this.isChangedBy(entity, update)) {
        return entity;
    }
    log.debug("Applying content update for org: {}, {}", entity, owner);
    Content updated = this.applyContentChanges((Content) entity.clone(), update);
    List<Content> alternateVersions = this.ownerContentCurator.getContentByVersions(owner, Collections.<String, Integer>singletonMap(updated.getId(), updated.getEntityVersion())).list();
    log.debug("Checking {} alternate content versions", alternateVersions.size());
    for (Content alt : alternateVersions) {
        if (alt.equals(updated)) {
            log.debug("Converging product with existing: {} => {}", updated, alt);
            // Make sure every product using the old version/entity are updated to use the new one
            List<Product> affectedProducts = this.productCurator.getProductsByContent(owner, Arrays.asList(updated.getId())).list();
            this.ownerContentCurator.updateOwnerContentReferences(owner, Collections.<String, String>singletonMap(entity.getUuid(), alt.getUuid()));
            log.debug("Updating {} affected products", affectedProducts.size());
            ContentDTO cdto = this.modelTranslator.translate(alt, ContentDTO.class);
            // TODO: Should we bulk this up like we do in importContent? Probably.
            for (Product product : affectedProducts) {
                log.debug("Updating affected product: {}", product);
                ProductDTO pdto = this.modelTranslator.translate(product, ProductDTO.class);
                ProductContentDTO pcdto = pdto.getProductContent(cdto.getId());
                if (pcdto != null) {
                    pdto.addContent(cdto, pcdto.isEnabled());
                    // Impl note: This should also take care of our entitlement cert regeneration
                    this.productManager.updateProduct(pdto, owner, regenerateEntitlementCerts);
                }
            }
            return alt;
        }
    }
    // Temporarily (?) disabled. If we ever move to clustered caching (rather than per-instance
    // caching, this branch should be re-enabled.
    /*
        // No alternate versions with which to converge. Check if we can do an in-place update instead
        if (this.ownerContentCurator.getOwnerCount(updated) < 2) {
            log.debug("Applying in-place update to content: {}", updated);

            updated = this.contentCurator.merge(this.applyContentChanges(entity, update, owner));

            if (regenerateEntitlementCerts) {
                // Every owner with a pool using any of the affected products needs an update.
                List<Product> affectedProducts = this.productCurator
                    .getProductsByContent(Arrays.asList(updated.getUuid()))
                    .list();

                this.entitlementCertGenerator.regenerateCertificatesOf(
                    Arrays.asList(owner), affectedProducts, true
                );
            }

            return updated;
        }
        */
    log.debug("Forking content and applying update: {}", updated);
    // Get products that currently use this content...
    List<Product> affectedProducts = this.productCurator.getProductsByContent(owner, Arrays.asList(updated.getId())).list();
    // Clear the UUID so Hibernate doesn't think our copy is a detached entity
    updated.setUuid(null);
    updated = this.contentCurator.create(updated);
    this.ownerContentCurator.updateOwnerContentReferences(owner, Collections.<String, String>singletonMap(entity.getUuid(), updated.getUuid()));
    // Impl note:
    // This block is a consequence of products and contents not being strongly related.
    log.debug("Updating {} affected products", affectedProducts.size());
    ContentDTO cdto = this.modelTranslator.translate(updated, ContentDTO.class);
    // TODO: Should we bulk this up like we do in importContent? Probably.
    for (Product product : affectedProducts) {
        log.debug("Updating affected product: {}", product);
        ProductDTO pdto = this.modelTranslator.translate(product, ProductDTO.class);
        ProductContentDTO pcdto = pdto.getProductContent(cdto.getId());
        if (pcdto != null) {
            pdto.addContent(cdto, pcdto.isEnabled());
            // Impl note: This should also take care of our entitlement cert regeneration
            this.productManager.updateProduct(pdto, owner, regenerateEntitlementCerts);
        }
    }
    return updated;
}
Also used : ProductContentDTO(org.candlepin.dto.api.v1.ProductDTO.ProductContentDTO) ContentDTO(org.candlepin.dto.api.v1.ContentDTO) ProductContent(org.candlepin.model.ProductContent) Content(org.candlepin.model.Content) OwnerContent(org.candlepin.model.OwnerContent) Product(org.candlepin.model.Product) ProductContentDTO(org.candlepin.dto.api.v1.ProductDTO.ProductContentDTO) ProductDTO(org.candlepin.dto.api.v1.ProductDTO) Transactional(com.google.inject.persist.Transactional)

Example 10 with Product

use of org.candlepin.model.Product in project candlepin by candlepin.

the class ProductManager method removeProduct.

/**
 * Removes the specified product from the given owner. If the product is in use by multiple
 * owners, the product will not actually be deleted, but, instead, will simply by removed from
 * the given owner's visibility.
 *
 * @param owner
 *  The owner for which to remove the product
 *
 * @param entity
 *  The product entity to remove
 *
 * @throws IllegalStateException
 *  if this method is called with an entity does not exist in the backing database for the given
 *  owner, or if the product is currently in use by one or more subscriptions/pools
 *
 * @throws IllegalArgumentException
 *  if entity or owner is null
 */
public void removeProduct(Owner owner, Product entity) {
    if (owner == null) {
        throw new IllegalArgumentException("owner is null");
    }
    if (entity == null) {
        throw new IllegalArgumentException("entity is null");
    }
    // This has to fetch a new instance, or we'll be unable to compare the objects
    Product existing = this.ownerProductCurator.getProductById(owner, entity.getId());
    if (existing == null) {
        // If we're doing an exclusive update, this should be an error condition
        throw new IllegalStateException("Product has not yet been created");
    }
    this.removeProductsByUuids(owner, Arrays.asList(existing.getUuid()));
}
Also used : Product(org.candlepin.model.Product) OwnerProduct(org.candlepin.model.OwnerProduct)

Aggregations

Product (org.candlepin.model.Product)407 Test (org.junit.Test)281 Pool (org.candlepin.model.Pool)216 Owner (org.candlepin.model.Owner)153 Consumer (org.candlepin.model.Consumer)112 ConsumerInstalledProduct (org.candlepin.model.ConsumerInstalledProduct)108 HashSet (java.util.HashSet)84 Date (java.util.Date)74 ArrayList (java.util.ArrayList)69 Entitlement (org.candlepin.model.Entitlement)67 LinkedList (java.util.LinkedList)66 HashMap (java.util.HashMap)65 Subscription (org.candlepin.model.dto.Subscription)47 Content (org.candlepin.model.Content)40 ValidationResult (org.candlepin.policy.ValidationResult)38 SourceSubscription (org.candlepin.model.SourceSubscription)36 Matchers.anyString (org.mockito.Matchers.anyString)31 List (java.util.List)29 PoolQuantity (org.candlepin.model.PoolQuantity)29 DateRange (org.candlepin.policy.js.compliance.DateRange)27