1
0
mirror of https://github.com/quay/quay.git synced 2025-07-30 07:43:13 +03:00

billing: Assign SKU to org (PROJQUAY-5363) (#1989)

* add migration for orgrhskus table

* add endpoints for managing and listing skus bound to an org

* create checks in billing flow to look for org-bound skus

* refactor RH marketplace api objects to be more usable in tests

* update cypress test db data and exclude it from pre-commit hook formatting
This commit is contained in:
Marcus Kok
2023-08-25 14:52:54 -04:00
committed by GitHub
parent 5f63b3a7bb
commit e44783fe19
19 changed files with 715 additions and 327 deletions

6
.gitignore vendored
View File

@ -111,4 +111,8 @@ web/yarn-error.log*
# Cypress files # Cypress files
web/cypress/videos web/cypress/videos
web/cypress/screenshots web/cypress/screenshots
web/cypress/downloads web/cypress/downloads
# marketplace
local-dev/stack/quay-marketplace-api.crt
local-dev/stack/quay-marketplace-api.key

View File

@ -23,8 +23,11 @@ repos:
entry: web/node_modules/.bin/eslint --fix entry: web/node_modules/.bin/eslint --fix
language: system language: system
files: ^web/ files: ^web/
exclude: ^web/cypress/test/
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0 rev: v4.3.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
exclude: ^web/cypress/test/quay-db-data.txt
- id: end-of-file-fixer - id: end-of-file-fixer
exclude: ^web/cypress/test/quay-db-data.txt

11
app.py
View File

@ -53,7 +53,7 @@ from util.greenlet_tracing import enable_tracing
from util.ipresolver import IPResolver from util.ipresolver import IPResolver
from util.label_validator import LabelValidator from util.label_validator import LabelValidator
from util.log import filter_logs from util.log import filter_logs
from util.marketplace import RHMarketplaceAPI, RHUserAPI from util.marketplace import MarketplaceSubscriptionApi, MarketplaceUserApi
from util.metrics.prometheus import PrometheusPlugin from util.metrics.prometheus import PrometheusPlugin
from util.names import urn_generator from util.names import urn_generator
from util.repomirror.api import RepoMirrorAPI from util.repomirror.api import RepoMirrorAPI
@ -236,12 +236,6 @@ Principal(app, use_sessions=False)
tf = app.config["DB_TRANSACTION_FACTORY"] tf = app.config["DB_TRANSACTION_FACTORY"]
rh_user_api = None
rh_marketplace_api = None
if features.ENTITLEMENT_RECONCILIATION or features.RH_MARKETPLACE:
rh_user_api = RHUserAPI(app.config)
rh_marketplace_api = RHMarketplaceAPI(app.config)
model_cache = get_model_cache(app.config) model_cache = get_model_cache(app.config)
avatar = Avatar(app) avatar = Avatar(app)
login_manager = LoginManager(app) login_manager = LoginManager(app)
@ -310,6 +304,9 @@ repo_mirror_api = RepoMirrorAPI(
tuf_metadata_api = TUFMetadataAPI(app, app.config) tuf_metadata_api = TUFMetadataAPI(app, app.config)
marketplace_users = MarketplaceUserApi(app)
marketplace_subscriptions = MarketplaceSubscriptionApi(app)
# Check for a key in config. If none found, generate a new signing key for Docker V2 manifests. # Check for a key in config. If none found, generate a new signing key for Docker V2 manifests.
_v2_key_path = os.path.join(OVERRIDE_CONFIG_DIRECTORY, DOCKER_V2_SIGNINGKEY_FILENAME) _v2_key_path = os.path.join(OVERRIDE_CONFIG_DIRECTORY, DOCKER_V2_SIGNINGKEY_FILENAME)
if os.path.exists(_v2_key_path): if os.path.exists(_v2_key_path):

View File

@ -745,6 +745,7 @@ class User(BaseModel):
UserOrganizationQuota, UserOrganizationQuota,
QuotaLimits, QuotaLimits,
RedHatSubscriptions, RedHatSubscriptions,
OrganizationRhSkus,
} }
| appr_classes | appr_classes
| v22_classes | v22_classes
@ -1981,13 +1982,29 @@ class ProxyCacheConfig(BaseModel):
class RedHatSubscriptions(BaseModel): class RedHatSubscriptions(BaseModel):
""" """
Represents internal Red Hat subscriptions for customers Represents store for users' RH account numbers
""" """
user_id = ForeignKeyField(User, backref="subscription") user_id = ForeignKeyField(User, backref="account_number")
account_number = IntegerField(unique=True) account_number = IntegerField(unique=True)
class OrganizationRhSkus(BaseModel):
"""
Represents sku to org binding for
RH subscriptions
"""
subscription_id = IntegerField(index=True, unique=True)
user_id = ForeignKeyField(User, backref="org_bound_subscription")
org_id = ForeignKeyField(User, backref="subscription")
indexes = (
(("subscription_id", "org_id"), True),
(("subscription_id", "org_id", "user_id"), True),
)
# Defines a map from full-length index names to the legacy names used in our code # Defines a map from full-length index names to the legacy names used in our code
# to meet length restrictions. # to meet length restrictions.
LEGACY_INDEX_MAP = { LEGACY_INDEX_MAP = {

View File

@ -0,0 +1,54 @@
"""Create table for org to sku relations
Revision ID: b82361fba1cd
Revises: 46980ea2dde5
Create Date: 2023-06-07 14:22:09.384808
"""
# revision identifiers, used by Alembic.
revision = "b82361fba1cd"
down_revision = "8a70b8777089"
from typing import Text
import sqlalchemy as sa
def upgrade(op, tables, tester):
op.create_table(
"organizationrhskus",
sa.Column("id", sa.Integer, nullable=False, autoincrement=True),
sa.Column("subscription_id", sa.Integer, nullable=False),
sa.Column("org_id", sa.Integer, nullable=False),
sa.Column("user_id", sa.Integer, nullable=False),
sa.PrimaryKeyConstraint("id", name=op.f("pk_organizationrhskus")),
sa.ForeignKeyConstraint(
["user_id"], ["user.id"], name=op.f("fk_organizationrhskus_userid")
),
sa.ForeignKeyConstraint(["org_id"], ["user.id"], name=op.f("fk_organizationrhskus_orgid")),
)
op.create_index(
"organizationrhskus_subscription_id", "organizationrhskus", ["subscription_id"], unique=True
)
op.create_index(
"organizationrhskus_subscription_id_org_id",
"organizationrhskus",
["subscription_id", "org_id"],
unique=True,
)
op.create_index(
"organizationrhskus_subscription_id_org_id_user_id",
"organizationrhskus",
["subscription_id", "org_id", "user_id"],
unique=True,
)
def downgrade(op, tables, tester):
op.drop_index("organizationrhskus_subscription_id", table_name="organizationrhskus")
op.drop_index("organizationrhskus_subscription_id_org_id", table_name="organizationrhskus")
op.drop_index(
"organizationrhskus_subscription_id_org_id_user_id", table_name="organizationrhskus"
)
op.drop_table("organizationrhskus")

View File

@ -137,6 +137,10 @@ class UnsupportedQuotaSize(DataModelException):
pass pass
class OrgSubscriptionBindingAlreadyExists(DataModelException):
pass
class TooManyLoginAttemptsException(Exception): class TooManyLoginAttemptsException(Exception):
def __init__(self, message, retry_after): def __init__(self, message, retry_after):
super(TooManyLoginAttemptsException, self).__init__(message) super(TooManyLoginAttemptsException, self).__init__(message)
@ -178,6 +182,7 @@ from data.model import (
notification, notification,
oauth, oauth,
organization, organization,
organization_skus,
permission, permission,
proxy_cache, proxy_cache,
release, release,

View File

@ -0,0 +1,56 @@
import logging
import peewee
from data import model
from data.database import OrganizationRhSkus
logger = logging.getLogger(__name__)
def get_org_subscriptions(org_id):
try:
query = OrganizationRhSkus.select().where(OrganizationRhSkus.org_id == org_id)
return query
except OrganizationRhSkus.DoesNotExist:
return None
def bind_subscription_to_org(subscription_id, org_id, user_id):
try:
return OrganizationRhSkus.create(
subscription_id=subscription_id, org_id=org_id, user_id=user_id
)
except model.DataModelException as ex:
logger.error("Problem binding subscription to org %s: %s", org_id, ex)
except peewee.IntegrityError:
raise model.OrgSubscriptionBindingAlreadyExists()
def subscription_bound_to_org(subscription_id):
# lookup row in table matching subscription_id, if there is no row return false, otherwise return true
# this function is used to check if a subscription is bound to an org or
try:
binding = OrganizationRhSkus.get(OrganizationRhSkus.subscription_id == subscription_id)
return True, binding.org_id
except OrganizationRhSkus.DoesNotExist:
return False, None
def remove_subscription_from_org(org_id, subscription_id):
query = OrganizationRhSkus.delete().where(
OrganizationRhSkus.org_id == org_id,
OrganizationRhSkus.subscription_id == subscription_id,
)
query.execute()
def remove_all_owner_subscriptions_from_org(user_id, org_id):
try:
query = OrganizationRhSkus.delete().where(
OrganizationRhSkus.user_id == user_id,
OrganizationRhSkus.org_id == org_id,
)
query.execute()
except model.DataModelException as ex:
raise model.DataModelException(ex)

View File

@ -9,12 +9,13 @@ import stripe
from flask import request from flask import request
import features import features
from app import app, billing, rh_marketplace_api, rh_user_api from app import app, billing, marketplace_subscriptions, marketplace_users
from auth import scopes from auth import scopes
from auth.auth_context import get_authenticated_user from auth.auth_context import get_authenticated_user
from auth.permissions import AdministerOrganizationPermission from auth.permissions import AdministerOrganizationPermission
from data import model from data import model
from data.billing import PLANS, get_plan from data.billing import PLANS, get_plan, get_plan_using_rh_sku
from data.model import InvalidOrganizationException, organization_skus
from endpoints.api import ( from endpoints.api import (
ApiResource, ApiResource,
abort, abort,
@ -47,11 +48,22 @@ def check_internal_api_for_subscription(namespace_user):
Returns subscription from RH marketplace. Returns subscription from RH marketplace.
None returned if no subscription is found. None returned if no subscription is found.
""" """
user_account_number = rh_user_api.get_account_number(namespace_user) plans = []
if user_account_number: if namespace_user.organization:
user_subscriptions = rh_marketplace_api.find_stripe_subscription(user_account_number) query = organization_skus.get_org_subscriptions(namespace_user.id)
return user_subscriptions org_subscriptions = list(query.dicts()) if query is not None else []
return [] for subscription in org_subscriptions:
subscription_id = subscription["subscription_id"]
sku = marketplace_subscriptions.get_subscription_sku(subscription_id)
plans.append(get_plan_using_rh_sku(sku))
pass
else:
user_account_number = marketplace_users.get_account_number(namespace_user)
if user_account_number:
plans = marketplace_subscriptions.get_list_of_subscriptions(
user_account_number, filter_out_org_bindings=True, convert_to_stripe_plans=True
)
return plans
def get_namespace_plan(namespace): def get_namespace_plan(namespace):
@ -87,9 +99,11 @@ def lookup_allowed_private_repos(namespace):
if features.RH_MARKETPLACE: if features.RH_MARKETPLACE:
namespace_user = model.user.get_namespace_user(namespace) namespace_user = model.user.get_namespace_user(namespace)
marketplace_subscriptions = check_internal_api_for_subscription(namespace_user)
for subscription in marketplace_subscriptions: subscriptions = check_internal_api_for_subscription(namespace_user)
repos_allowed += subscription["privateRepos"] for subscription in subscriptions:
if subscription is not None:
repos_allowed += subscription["privateRepos"]
# Find the number of private repositories used by the namespace and compare it to the # Find the number of private repositories used by the namespace and compare it to the
# plan subscribed. # plan subscribed.
@ -916,3 +930,127 @@ class OrganizationInvoiceField(ApiResource):
return "Okay", 201 return "Okay", 201
abort(403) abort(403)
@resource("/v1/organization/<orgname>/marketplace")
@path_param("orgname", "The name of the organization")
@show_if(features.BILLING)
class OrganizationRhSku(ApiResource):
"""
Resource for managing an organization's RH SKU
"""
@require_scope(scopes.ORG_ADMIN)
@nickname("listOrgSkus")
def get(self, orgname):
"""
Get sku assigned to org
"""
permission = AdministerOrganizationPermission(orgname)
if permission.can():
organization = model.organization.get_organization(orgname)
query = model.organization_skus.get_org_subscriptions(organization.id)
if query:
subscriptions = list(query.dicts())
for subscription in subscriptions:
subscription["sku"] = marketplace_subscriptions.get_subscription_sku(
subscription["subscription_id"]
)
return subscriptions
else:
return []
abort(401)
@require_scope(scopes.ORG_ADMIN)
@nickname("bindSkuToOrg")
def post(self, orgname):
"""
Assigns a sku to an org
"""
permission = AdministerOrganizationPermission(orgname)
request_data = request.get_json()
subscription_id = request_data["subscription_id"]
if permission.can():
organization = model.organization.get_organization(orgname)
user = get_authenticated_user()
account_number = marketplace_users.get_account_number(user)
subscriptions = marketplace_subscriptions.get_list_of_subscriptions(account_number)
if subscriptions is None:
abort(401, message="no valid subscriptions present")
user_subscription_ids = [int(subscription["id"]) for subscription in subscriptions]
if int(subscription_id) in user_subscription_ids:
try:
model.organization_skus.bind_subscription_to_org(
user_id=user.id, subscription_id=subscription_id, org_id=organization.id
)
return "Okay", 201
except model.OrgSubscriptionBindingAlreadyExists:
abort(400, message="subscription is already bound to an org")
else:
abort(401, message=f"subscription does not belong to {user.username}")
abort(401)
@resource("/v1/organization/<orgname>/marketplace/<subscription_id>")
@path_param("orgname", "The name of the organization")
@path_param("subscription_id", "Marketplace subscription id")
@related_user_resource(UserPlan)
@show_if(features.BILLING)
class OrganizationRhSkuSubscriptionField(ApiResource):
"""
Resource for removing RH skus from an organization
"""
@require_scope(scopes.ORG_ADMIN)
def delete(self, orgname, subscription_id):
"""
Remove sku from an org
"""
permission = AdministerOrganizationPermission(orgname)
if permission.can():
try:
organization = model.organization.get_organization(orgname)
except InvalidOrganizationException:
return ("Organization not valid", 400)
model.organization_skus.remove_subscription_from_org(organization.id, subscription_id)
return ("Deleted", 204)
abort(401)
@resource("/v1/user/marketplace")
@show_if(features.RH_MARKETPLACE)
class UserSkuList(ApiResource):
"""
Resource for listing a user's RH skus
bound to an org
"""
@require_user_admin()
@nickname("getUserMarketplaceSubscriptions")
def get(self):
"""
List the invoices for the current user.
"""
user = get_authenticated_user()
account_number = marketplace_users.get_account_number(user)
if not account_number:
raise NotFound()
user_subscriptions = marketplace_subscriptions.get_list_of_subscriptions(account_number)
for subscription in user_subscriptions:
bound_to_org, organization = organization_skus.subscription_bound_to_org(
subscription["id"]
)
# fill in information for whether a subscription is bound to an org
if bound_to_org:
subscription["assigned_to_org"] = organization.username
else:
subscription["assigned_to_org"] = None
return user_subscriptions

View File

@ -10,7 +10,7 @@ from flask import request
import features import features
from app import all_queues, app, authentication, avatar from app import all_queues, app, authentication, avatar
from app import billing as stripe from app import billing as stripe
from app import ip_resolver, namespace_gc_queue, usermanager from app import ip_resolver, marketplace_subscriptions, namespace_gc_queue, usermanager
from auth import scopes from auth import scopes
from auth.auth_context import get_authenticated_user from auth.auth_context import get_authenticated_user
from auth.permissions import ( from auth.permissions import (
@ -21,8 +21,9 @@ from auth.permissions import (
ViewTeamPermission, ViewTeamPermission,
) )
from data import model from data import model
from data.billing import get_plan from data.billing import get_plan, get_plan_using_rh_sku
from data.database import ProxyCacheConfig from data.database import ProxyCacheConfig
from data.model import organization_skus
from endpoints.api import ( from endpoints.api import (
ApiResource, ApiResource,
allow_if_superuser, allow_if_superuser,
@ -42,6 +43,7 @@ from endpoints.api import (
from endpoints.api.user import PrivateRepositories, User from endpoints.api.user import PrivateRepositories, User
from endpoints.exception import NotFound, Unauthorized from endpoints.exception import NotFound, Unauthorized
from proxy import Proxy, UpstreamRegistryError from proxy import Proxy, UpstreamRegistryError
from util.marketplace import MarketplaceSubscriptionApi
from util.names import parse_robot_username from util.names import parse_robot_username
from util.request import get_request_ip from util.request import get_request_ip
@ -364,16 +366,27 @@ class OrgPrivateRepositories(ApiResource):
organization = model.organization.get_organization(orgname) organization = model.organization.get_organization(orgname)
private_repos = model.user.get_private_repo_count(organization.username) private_repos = model.user.get_private_repo_count(organization.username)
data = {"privateAllowed": False} data = {"privateAllowed": False}
repos_allowed = 0
if organization.stripe_id: if organization.stripe_id:
cus = stripe.Customer.retrieve(organization.stripe_id) cus = stripe.Customer.retrieve(organization.stripe_id)
if cus.subscription: if cus.subscription:
repos_allowed = 0
plan = get_plan(cus.subscription.plan.id) plan = get_plan(cus.subscription.plan.id)
if plan: if plan:
repos_allowed = plan["privateRepos"] repos_allowed = plan["privateRepos"]
data["privateAllowed"] = private_repos < repos_allowed if features.RH_MARKETPLACE:
query = organization_skus.get_org_subscriptions(organization.id)
rh_subscriptions = list(query.dicts()) if query is not None else []
for subscription in rh_subscriptions:
subscription_sku = marketplace_subscriptions.get_subscription_sku(
subscription["subscription_id"]
)
equivalent_stripe_plan = get_plan_using_rh_sku(subscription_sku)
if equivalent_stripe_plan:
repos_allowed += equivalent_stripe_plan["privateRepos"]
data["privateAllowed"] = private_repos < repos_allowed
if AdministerOrganizationPermission(orgname).can(): if AdministerOrganizationPermission(orgname).can():
data["privateCount"] = private_repos data["privateCount"] = private_repos

View File

@ -482,6 +482,9 @@ class TeamMember(ApiResource):
return "", 204 return "", 204
model.team.remove_user_from_team(orgname, teamname, membername, invoking_user) model.team.remove_user_from_team(orgname, teamname, membername, invoking_user)
if features.RH_MARKETPLACE:
org_id = model.organization.get_organization(orgname).id
model.organization_skus.remove_all_owner_subscriptions_from_org(member.id, org_id)
log_action("org_remove_team_member", orgname, {"member": membername, "team": teamname}) log_action("org_remove_team_member", orgname, {"member": membername, "team": teamname})
return "", 204 return "", 204

View File

@ -1,3 +1,5 @@
# isort reordering imports breaks these tests, so tell it to skip
# isort: skip_file
from typing import List, Optional, Dict, Tuple, Any, Type from typing import List, Optional, Dict, Tuple, Any, Type
from mock import patch from mock import patch
@ -6024,6 +6026,38 @@ SECURITY_TESTS: List[
"devtable", "devtable",
404, 404,
), ),
(
OrganizationRhSkuSubscriptionField,
"DELETE",
{"orgname": "buynlarge", "subscription_id": 12345},
None,
None,
401,
),
(
OrganizationRhSku,
"GET",
{"orgname": "buynlarge"},
None,
None,
401,
),
(
OrganizationRhSku,
"POST",
{"orgname": "buynlarge"},
{"subscription_id": 12345},
None,
401,
),
(
UserSkuList,
"GET",
{"orgname": "buynlarge"},
None,
None,
401,
),
] ]

View File

@ -16,10 +16,10 @@ from app import all_queues, app, authentication, avatar
from app import billing as stripe from app import billing as stripe
from app import ( from app import (
ip_resolver, ip_resolver,
marketplace_subscriptions,
marketplace_users,
namespace_gc_queue, namespace_gc_queue,
oauth_login, oauth_login,
rh_marketplace_api,
rh_user_api,
url_scheme_and_hostname, url_scheme_and_hostname,
) )
from auth import scopes from auth import scopes
@ -638,12 +638,12 @@ class PrivateRepositories(ApiResource):
repos_allowed = plan["privateRepos"] repos_allowed = plan["privateRepos"]
if features.RH_MARKETPLACE: if features.RH_MARKETPLACE:
# subscriptions in marketplace will get added to private repo count # subscriptions in marketplace will get added to private repo count
user_account_number = rh_user_api.get_account_number(user) user_account_number = marketplace_users.get_account_number(user)
if user_account_number: if user_account_number:
marketplace_subscriptions = rh_marketplace_api.find_stripe_subscription( subscriptions = marketplace_subscriptions.get_list_of_subscriptions(
user_account_number user_account_number, filter_out_org_bindings=True, convert_to_stripe_plans=True
) )
for user_subscription in marketplace_subscriptions: for user_subscription in subscriptions:
repos_allowed += user_subscription["privateRepos"] repos_allowed += user_subscription["privateRepos"]
return {"privateCount": private_repos, "privateAllowed": (private_repos < repos_allowed)} return {"privateCount": private_repos, "privateAllowed": (private_repos < repos_allowed)}

View File

@ -1,3 +1,9 @@
# isort: skip_file
from typing import Dict, Any
import logging
import json
import hashlib
import random
import argparse import argparse
import calendar import calendar
import hashlib import hashlib
@ -1339,6 +1345,7 @@ WHITELISTED_EMPTY_MODELS = [
"Image", "Image",
"ProxyCacheConfig", "ProxyCacheConfig",
"RedHatSubscriptions", "RedHatSubscriptions",
"OrganizationRhSkus",
"QuotaRegistrySize", "QuotaRegistrySize",
] ]

View File

@ -37,6 +37,8 @@ from endpoints.api.billing import (
ListPlans, ListPlans,
OrganizationCard, OrganizationCard,
OrganizationPlan, OrganizationPlan,
OrganizationRhSku,
OrganizationRhSkuSubscriptionField,
UserCard, UserCard,
UserPlan, UserPlan,
) )
@ -5065,5 +5067,62 @@ class TestSuperUserManagement(ApiTestCase):
self.assertEqual(len(json["messages"]), 1) self.assertEqual(len(json["messages"]), 1)
class TestOrganizationRhSku(ApiTestCase):
def test_bind_sku_to_org(self):
self.login(ADMIN_ACCESS_USER)
self.postResponse(
resource_name=OrganizationRhSku,
params=dict(orgname=ORGANIZATION),
data={"subscription_id": 12345},
expected_code=201,
)
json = self.getJsonResponse(
resource_name=OrganizationRhSku,
params=dict(orgname=ORGANIZATION),
)
self.assertEqual(len(json), 1)
def test_bind_sku_duplicate(self):
user = model.user.get_user(ADMIN_ACCESS_USER)
org = model.organization.get_organization(ORGANIZATION)
model.organization_skus.bind_subscription_to_org(12345, org.id, user.id)
self.login(ADMIN_ACCESS_USER)
self.postResponse(
resource_name=OrganizationRhSku,
params=dict(orgname=ORGANIZATION),
data={"subscription_id": 12345},
expected_code=400,
)
def test_bind_sku_unauthorized(self):
# bind a sku that user does not own
self.login(ADMIN_ACCESS_USER)
self.postResponse(
resource_name=OrganizationRhSku,
params=dict(orgname=ORGANIZATION),
data={"subscription_id": 11111},
expected_code=401,
)
def test_remove_sku_from_org(self):
self.login(ADMIN_ACCESS_USER)
self.postResponse(
resource_name=OrganizationRhSku,
params=dict(orgname=ORGANIZATION),
data={"subscription_id": 12345},
expected_code=201,
)
self.deleteResponse(
resource_name=OrganizationRhSkuSubscriptionField,
params=dict(orgname=ORGANIZATION, subscription_id=12345),
expected_code=204,
)
json = self.getJsonResponse(
resource_name=OrganizationRhSku,
params=dict(orgname=ORGANIZATION),
)
self.assertEqual(len(json), 0)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -112,3 +112,5 @@ class TestConfig(DefaultConfig):
FEATURE_PROXY_CACHE = True FEATURE_PROXY_CACHE = True
PERMANENTLY_DELETE_TAGS = True PERMANENTLY_DELETE_TAGS = True
RESET_CHILD_MANIFEST_EXPIRATION = True RESET_CHILD_MANIFEST_EXPIRATION = True
FEATURE_RH_MARKETPLACE = True

View File

@ -5,8 +5,8 @@ from datetime import datetime
import requests import requests
from data.billing import RH_SKUS, get_plan, get_plan_using_rh_sku from data.billing import RH_SKUS, get_plan_using_rh_sku
from data.model import entitlements from data.model import entitlements, organization_skus
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -16,7 +16,7 @@ MARKETPLACE_FILE = "/conf/stack/quay-marketplace-api.crt"
MARKETPLACE_SECRET = "/conf/stack/quay-marketplace-api.key" MARKETPLACE_SECRET = "/conf/stack/quay-marketplace-api.key"
class RHUserAPI: class RedHatUserApi(object):
def __init__(self, app_config): def __init__(self, app_config):
self.cert = (MARKETPLACE_FILE, MARKETPLACE_SECRET) self.cert = (MARKETPLACE_FILE, MARKETPLACE_SECRET)
self.user_endpoint = app_config.get("ENTITLEMENT_RECONCILIATION_USER_ENDPOINT") self.user_endpoint = app_config.get("ENTITLEMENT_RECONCILIATION_USER_ENDPOINT")
@ -52,7 +52,6 @@ class RHUserAPI:
} }
request_url = f"{self.user_endpoint}/v2/findUsers" request_url = f"{self.user_endpoint}/v2/findUsers"
r = requests.request( r = requests.request(
method="post", method="post",
url=request_url, url=request_url,
@ -69,7 +68,7 @@ class RHUserAPI:
return account_number return account_number
class RHMarketplaceAPI: class RedHatSubscriptionApi(object):
def __init__(self, app_config): def __init__(self, app_config):
self.cert = (MARKETPLACE_FILE, MARKETPLACE_SECRET) self.cert = (MARKETPLACE_FILE, MARKETPLACE_SECRET)
self.marketplace_endpoint = app_config.get( self.marketplace_endpoint = app_config.get(
@ -108,6 +107,7 @@ class RHMarketplaceAPI:
now_ms = time.time() * 1000 now_ms = time.time() * 1000
# Is subscription still valid? # Is subscription still valid?
if now_ms < end_date: if now_ms < end_date:
logger.debug("subscription found for %s", str(skuId))
return subscription return subscription
return None return None
@ -165,15 +165,170 @@ class RHMarketplaceAPI:
) )
return r.status_code return r.status_code
def find_stripe_subscription(self, account_number): def get_subscription_sku(self, subscription_id):
""" """
Returns the stripe plan/s relating to marketplace subscriptions Return the sku for a specific subscription
for a given account number
""" """
stripe_plans = [] request_url = f"{self.marketplace_endpoint}/subscription/v5/products/subscription_id={subscription_id}"
request_headers = {"Content-Type": "application/json"}
try:
r = requests.request(
method="get",
url=request_url,
cert=self.cert,
verify=True,
timeout=REQUEST_TIMEOUT,
headers=request_headers,
)
info = json.loads(r.content)
SubscriptionSKU = info[0]["sku"]
return SubscriptionSKU
except requests.exceptions.SSLError:
raise requests.exceptions.SSLError
def get_list_of_subscriptions(
self, account_number, filter_out_org_bindings=False, convert_to_stripe_plans=False
):
"""
Returns a list of all active subscriptions a user has
in RH marketplace
"""
subscription_list = []
for sku in RH_SKUS: for sku in RH_SKUS:
user_subscription = self.lookup_subscription(account_number, sku) user_subscription = self.lookup_subscription(account_number, sku)
if user_subscription is not None: if user_subscription is not None:
stripe_plans.append(get_plan_using_rh_sku(sku)) bound_to_org = organization_skus.subscription_bound_to_org(user_subscription["id"])
return stripe_plans if filter_out_org_bindings and bound_to_org[0]:
continue
if convert_to_stripe_plans:
subscription_list.append(get_plan_using_rh_sku(sku))
else:
# add in sku field for convenience
user_subscription["sku"] = sku
subscription_list.append(user_subscription)
return subscription_list
TEST_USER = {
"account_number": 12345,
"email": "test_user@test.com",
"username": "test_user",
"password": "password",
}
FREE_USER = {
"account_number": 23456,
"email": "free_user@test.com",
"username": "free_user",
"password": "password",
}
DEV_ACCOUNT_NUMBER = 76543
class FakeUserApi(object):
"""
Fake class used for tests
"""
def lookup_customer_id(self, email):
if email == TEST_USER["email"]:
return TEST_USER["account_number"]
if email == FREE_USER["email"]:
return FREE_USER["account_number"]
return None
def get_account_number(self, user):
if user.username == "devtable":
return DEV_ACCOUNT_NUMBER
return self.lookup_customer_id(user.email)
class FakeSubscriptionApi(object):
"""
Fake class used for tests
"""
def __init__(self):
self.subscription_extended = False
self.subscription_created = False
def lookup_subscription(self, customer_id, sku_id):
return None
def create_entitlement(self, customer_id, sku_id):
self.subscription_created = True
def extend_subscription(self, subscription_id, end_date):
self.subscription_extended = True
def get_subscription_sku(self, subscription_id):
if id == 12345:
return "FakeSku"
else:
return None
def get_list_of_subscriptions(
self, account_number, filter_out_org_bindings=False, convert_to_stripe_plans=False
):
if account_number == DEV_ACCOUNT_NUMBER:
return [
{
"id": 12345,
"sku": "FakeSku",
"privateRepos": 0,
}
]
return []
class MarketplaceUserApi(object):
def __init__(self, app=None):
self.app = app
if app is not None:
self.state = self.init_app(app)
else:
self.state = None
def init_app(self, app):
marketplace_enabled = app.config.get("FEATURE_RH_MARKETPLACE", False)
marketplace_user_api = FakeUserApi()
if marketplace_enabled and not app.config.get("TESTING"):
marketplace_user_api = RedHatUserApi(app.config)
app.extensions = getattr(app, "extensions", {})
app.extensions["marketplace_user_api"] = marketplace_user_api
return marketplace_user_api
def __getattr__(self, name):
return getattr(self.state, name, None)
class MarketplaceSubscriptionApi(object):
def __init__(self, app=None):
self.app = app
if app is not None:
self.state = self.init_app(app)
else:
self.state = None
def init_app(self, app):
marketplace_enabled = app.config.get("FEATURE_RH_MARKETPLACE", False)
marketplace_subscription_api = FakeSubscriptionApi()
if marketplace_enabled and not app.config.get("TESTING"):
marketplace_subscription_api = RedHatSubscriptionApi(app.config)
app.extensions = getattr(app, "extensions", {})
app.extensions["marketplace_subscription_api"] = marketplace_subscription_api
return marketplace_subscription_api
def __getattr__(self, name):
return getattr(self.state, name, None)

View File

@ -23,7 +23,6 @@ ALTER TABLE IF EXISTS ONLY public.userprompt DROP CONSTRAINT IF EXISTS fk_userpr
ALTER TABLE IF EXISTS ONLY public.userorganizationquota DROP CONSTRAINT IF EXISTS fk_userorganizationquota_organization; ALTER TABLE IF EXISTS ONLY public.userorganizationquota DROP CONSTRAINT IF EXISTS fk_userorganizationquota_organization;
ALTER TABLE IF EXISTS ONLY public.uploadedblob DROP CONSTRAINT IF EXISTS fk_uploadedblob_repository_id_repository; ALTER TABLE IF EXISTS ONLY public.uploadedblob DROP CONSTRAINT IF EXISTS fk_uploadedblob_repository_id_repository;
ALTER TABLE IF EXISTS ONLY public.uploadedblob DROP CONSTRAINT IF EXISTS fk_uploadedblob_blob_id_imagestorage; ALTER TABLE IF EXISTS ONLY public.uploadedblob DROP CONSTRAINT IF EXISTS fk_uploadedblob_blob_id_imagestorage;
ALTER TABLE IF EXISTS ONLY public.torrentinfo DROP CONSTRAINT IF EXISTS fk_torrentinfo_storage_id_imagestorage;
ALTER TABLE IF EXISTS ONLY public.teamsync DROP CONSTRAINT IF EXISTS fk_teamsync_team_id_team; ALTER TABLE IF EXISTS ONLY public.teamsync DROP CONSTRAINT IF EXISTS fk_teamsync_team_id_team;
ALTER TABLE IF EXISTS ONLY public.teamsync DROP CONSTRAINT IF EXISTS fk_teamsync_service_id_loginservice; ALTER TABLE IF EXISTS ONLY public.teamsync DROP CONSTRAINT IF EXISTS fk_teamsync_service_id_loginservice;
ALTER TABLE IF EXISTS ONLY public.teammemberinvite DROP CONSTRAINT IF EXISTS fk_teammemberinvite_user_id_user; ALTER TABLE IF EXISTS ONLY public.teammemberinvite DROP CONSTRAINT IF EXISTS fk_teammemberinvite_user_id_user;
@ -33,20 +32,6 @@ ALTER TABLE IF EXISTS ONLY public.teammember DROP CONSTRAINT IF EXISTS fk_teamme
ALTER TABLE IF EXISTS ONLY public.teammember DROP CONSTRAINT IF EXISTS fk_teammember_team_id_team; ALTER TABLE IF EXISTS ONLY public.teammember DROP CONSTRAINT IF EXISTS fk_teammember_team_id_team;
ALTER TABLE IF EXISTS ONLY public.team DROP CONSTRAINT IF EXISTS fk_team_role_id_teamrole; ALTER TABLE IF EXISTS ONLY public.team DROP CONSTRAINT IF EXISTS fk_team_role_id_teamrole;
ALTER TABLE IF EXISTS ONLY public.team DROP CONSTRAINT IF EXISTS fk_team_organization_id_user; ALTER TABLE IF EXISTS ONLY public.team DROP CONSTRAINT IF EXISTS fk_team_organization_id_user;
ALTER TABLE IF EXISTS ONLY public.tagtorepositorytag DROP CONSTRAINT IF EXISTS fk_tagtorepositorytag_tag_id_tag;
ALTER TABLE IF EXISTS ONLY public.tagtorepositorytag DROP CONSTRAINT IF EXISTS fk_tagtorepositorytag_repository_tag_id_repositorytag;
ALTER TABLE IF EXISTS ONLY public.tagtorepositorytag DROP CONSTRAINT IF EXISTS fk_tagtorepositorytag_repository_id_repository;
ALTER TABLE IF EXISTS ONLY public.tagmanifesttomanifest DROP CONSTRAINT IF EXISTS fk_tagmanifesttomanifest_tag_manifest_id_tagmanifest;
ALTER TABLE IF EXISTS ONLY public.tagmanifesttomanifest DROP CONSTRAINT IF EXISTS fk_tagmanifesttomanifest_manifest_id_manifest;
ALTER TABLE IF EXISTS ONLY public.tagmanifestlabelmap DROP CONSTRAINT IF EXISTS fk_tagmanifestlabelmap_tag_manifest_label_id_tagmanifestlabel;
ALTER TABLE IF EXISTS ONLY public.tagmanifestlabelmap DROP CONSTRAINT IF EXISTS fk_tagmanifestlabelmap_tag_manifest_id_tagmanifest;
ALTER TABLE IF EXISTS ONLY public.tagmanifestlabelmap DROP CONSTRAINT IF EXISTS fk_tagmanifestlabelmap_manifest_label_id_manifestlabel;
ALTER TABLE IF EXISTS ONLY public.tagmanifestlabelmap DROP CONSTRAINT IF EXISTS fk_tagmanifestlabelmap_manifest_id_manifest;
ALTER TABLE IF EXISTS ONLY public.tagmanifestlabelmap DROP CONSTRAINT IF EXISTS fk_tagmanifestlabelmap_label_id_label;
ALTER TABLE IF EXISTS ONLY public.tagmanifestlabel DROP CONSTRAINT IF EXISTS fk_tagmanifestlabel_repository_id_repository;
ALTER TABLE IF EXISTS ONLY public.tagmanifestlabel DROP CONSTRAINT IF EXISTS fk_tagmanifestlabel_label_id_label;
ALTER TABLE IF EXISTS ONLY public.tagmanifestlabel DROP CONSTRAINT IF EXISTS fk_tagmanifestlabel_annotated_id_tagmanifest;
ALTER TABLE IF EXISTS ONLY public.tagmanifest DROP CONSTRAINT IF EXISTS fk_tagmanifest_tag_id_repositorytag;
ALTER TABLE IF EXISTS ONLY public.tag DROP CONSTRAINT IF EXISTS fk_tag_tag_kind_id_tagkind; ALTER TABLE IF EXISTS ONLY public.tag DROP CONSTRAINT IF EXISTS fk_tag_tag_kind_id_tagkind;
ALTER TABLE IF EXISTS ONLY public.tag DROP CONSTRAINT IF EXISTS fk_tag_repository_id_repository; ALTER TABLE IF EXISTS ONLY public.tag DROP CONSTRAINT IF EXISTS fk_tag_repository_id_repository;
ALTER TABLE IF EXISTS ONLY public.tag DROP CONSTRAINT IF EXISTS fk_tag_manifest_id_manifest; ALTER TABLE IF EXISTS ONLY public.tag DROP CONSTRAINT IF EXISTS fk_tag_manifest_id_manifest;
@ -56,8 +41,6 @@ ALTER TABLE IF EXISTS ONLY public.star DROP CONSTRAINT IF EXISTS fk_star_reposit
ALTER TABLE IF EXISTS ONLY public.servicekey DROP CONSTRAINT IF EXISTS fk_servicekey_approval_id_servicekeyapproval; ALTER TABLE IF EXISTS ONLY public.servicekey DROP CONSTRAINT IF EXISTS fk_servicekey_approval_id_servicekeyapproval;
ALTER TABLE IF EXISTS ONLY public.robotaccounttoken DROP CONSTRAINT IF EXISTS fk_robotaccounttoken_robot_account_id_user; ALTER TABLE IF EXISTS ONLY public.robotaccounttoken DROP CONSTRAINT IF EXISTS fk_robotaccounttoken_robot_account_id_user;
ALTER TABLE IF EXISTS ONLY public.robotaccountmetadata DROP CONSTRAINT IF EXISTS fk_robotaccountmetadata_robot_account_id_user; ALTER TABLE IF EXISTS ONLY public.robotaccountmetadata DROP CONSTRAINT IF EXISTS fk_robotaccountmetadata_robot_account_id_user;
ALTER TABLE IF EXISTS ONLY public.repositorytag DROP CONSTRAINT IF EXISTS fk_repositorytag_repository_id_repository;
ALTER TABLE IF EXISTS ONLY public.repositorytag DROP CONSTRAINT IF EXISTS fk_repositorytag_image_id_image;
ALTER TABLE IF EXISTS ONLY public.repositorysize DROP CONSTRAINT IF EXISTS fk_repositorysize_repository_id_repository; ALTER TABLE IF EXISTS ONLY public.repositorysize DROP CONSTRAINT IF EXISTS fk_repositorysize_repository_id_repository;
ALTER TABLE IF EXISTS ONLY public.repositorysearchscore DROP CONSTRAINT IF EXISTS fk_repositorysearchscore_repository_id_repository; ALTER TABLE IF EXISTS ONLY public.repositorysearchscore DROP CONSTRAINT IF EXISTS fk_repositorysearchscore_repository_id_repository;
ALTER TABLE IF EXISTS ONLY public.repositorypermission DROP CONSTRAINT IF EXISTS fk_repositorypermission_user_id_user; ALTER TABLE IF EXISTS ONLY public.repositorypermission DROP CONSTRAINT IF EXISTS fk_repositorypermission_user_id_user;
@ -101,6 +84,8 @@ ALTER TABLE IF EXISTS ONLY public.permissionprototype DROP CONSTRAINT IF EXISTS
ALTER TABLE IF EXISTS ONLY public.permissionprototype DROP CONSTRAINT IF EXISTS fk_permissionprototype_delegate_user_id_user; ALTER TABLE IF EXISTS ONLY public.permissionprototype DROP CONSTRAINT IF EXISTS fk_permissionprototype_delegate_user_id_user;
ALTER TABLE IF EXISTS ONLY public.permissionprototype DROP CONSTRAINT IF EXISTS fk_permissionprototype_delegate_team_id_team; ALTER TABLE IF EXISTS ONLY public.permissionprototype DROP CONSTRAINT IF EXISTS fk_permissionprototype_delegate_team_id_team;
ALTER TABLE IF EXISTS ONLY public.permissionprototype DROP CONSTRAINT IF EXISTS fk_permissionprototype_activating_user_id_user; ALTER TABLE IF EXISTS ONLY public.permissionprototype DROP CONSTRAINT IF EXISTS fk_permissionprototype_activating_user_id_user;
ALTER TABLE IF EXISTS ONLY public.organizationrhskus DROP CONSTRAINT IF EXISTS fk_organizationrhskus_userid;
ALTER TABLE IF EXISTS ONLY public.organizationrhskus DROP CONSTRAINT IF EXISTS fk_organizationrhskus_orgid;
ALTER TABLE IF EXISTS ONLY public.oauthauthorizationcode DROP CONSTRAINT IF EXISTS fk_oauthauthorizationcode_application_id_oauthapplication; ALTER TABLE IF EXISTS ONLY public.oauthauthorizationcode DROP CONSTRAINT IF EXISTS fk_oauthauthorizationcode_application_id_oauthapplication;
ALTER TABLE IF EXISTS ONLY public.oauthapplication DROP CONSTRAINT IF EXISTS fk_oauthapplication_organization_id_user; ALTER TABLE IF EXISTS ONLY public.oauthapplication DROP CONSTRAINT IF EXISTS fk_oauthapplication_organization_id_user;
ALTER TABLE IF EXISTS ONLY public.oauthaccesstoken DROP CONSTRAINT IF EXISTS fk_oauthaccesstoken_authorized_user_id_user; ALTER TABLE IF EXISTS ONLY public.oauthaccesstoken DROP CONSTRAINT IF EXISTS fk_oauthaccesstoken_authorized_user_id_user;
@ -111,9 +96,6 @@ ALTER TABLE IF EXISTS ONLY public.namespacegeorestriction DROP CONSTRAINT IF EXI
ALTER TABLE IF EXISTS ONLY public.messages DROP CONSTRAINT IF EXISTS fk_messages_media_type_id_mediatype; ALTER TABLE IF EXISTS ONLY public.messages DROP CONSTRAINT IF EXISTS fk_messages_media_type_id_mediatype;
ALTER TABLE IF EXISTS ONLY public.manifestsecuritystatus DROP CONSTRAINT IF EXISTS fk_manifestsecuritystatus_repository_id_repository; ALTER TABLE IF EXISTS ONLY public.manifestsecuritystatus DROP CONSTRAINT IF EXISTS fk_manifestsecuritystatus_repository_id_repository;
ALTER TABLE IF EXISTS ONLY public.manifestsecuritystatus DROP CONSTRAINT IF EXISTS fk_manifestsecuritystatus_manifest_id_manifest; ALTER TABLE IF EXISTS ONLY public.manifestsecuritystatus DROP CONSTRAINT IF EXISTS fk_manifestsecuritystatus_manifest_id_manifest;
ALTER TABLE IF EXISTS ONLY public.manifestlegacyimage DROP CONSTRAINT IF EXISTS fk_manifestlegacyimage_repository_id_repository;
ALTER TABLE IF EXISTS ONLY public.manifestlegacyimage DROP CONSTRAINT IF EXISTS fk_manifestlegacyimage_manifest_id_manifest;
ALTER TABLE IF EXISTS ONLY public.manifestlegacyimage DROP CONSTRAINT IF EXISTS fk_manifestlegacyimage_image_id_image;
ALTER TABLE IF EXISTS ONLY public.manifestlabel DROP CONSTRAINT IF EXISTS fk_manifestlabel_repository_id_repository; ALTER TABLE IF EXISTS ONLY public.manifestlabel DROP CONSTRAINT IF EXISTS fk_manifestlabel_repository_id_repository;
ALTER TABLE IF EXISTS ONLY public.manifestlabel DROP CONSTRAINT IF EXISTS fk_manifestlabel_manifest_id_manifest; ALTER TABLE IF EXISTS ONLY public.manifestlabel DROP CONSTRAINT IF EXISTS fk_manifestlabel_manifest_id_manifest;
ALTER TABLE IF EXISTS ONLY public.manifestlabel DROP CONSTRAINT IF EXISTS fk_manifestlabel_label_id_label; ALTER TABLE IF EXISTS ONLY public.manifestlabel DROP CONSTRAINT IF EXISTS fk_manifestlabel_label_id_label;
@ -133,14 +115,9 @@ ALTER TABLE IF EXISTS ONLY public.imagestoragesignature DROP CONSTRAINT IF EXIST
ALTER TABLE IF EXISTS ONLY public.imagestoragesignature DROP CONSTRAINT IF EXISTS fk_imagestoragesignature_kind_id_imagestoragesignaturekind; ALTER TABLE IF EXISTS ONLY public.imagestoragesignature DROP CONSTRAINT IF EXISTS fk_imagestoragesignature_kind_id_imagestoragesignaturekind;
ALTER TABLE IF EXISTS ONLY public.imagestorageplacement DROP CONSTRAINT IF EXISTS fk_imagestorageplacement_storage_id_imagestorage; ALTER TABLE IF EXISTS ONLY public.imagestorageplacement DROP CONSTRAINT IF EXISTS fk_imagestorageplacement_storage_id_imagestorage;
ALTER TABLE IF EXISTS ONLY public.imagestorageplacement DROP CONSTRAINT IF EXISTS fk_imagestorageplacement_location_id_imagestoragelocation; ALTER TABLE IF EXISTS ONLY public.imagestorageplacement DROP CONSTRAINT IF EXISTS fk_imagestorageplacement_location_id_imagestoragelocation;
ALTER TABLE IF EXISTS ONLY public.image DROP CONSTRAINT IF EXISTS fk_image_storage_id_imagestorage;
ALTER TABLE IF EXISTS ONLY public.image DROP CONSTRAINT IF EXISTS fk_image_repository_id_repository;
ALTER TABLE IF EXISTS ONLY public.federatedlogin DROP CONSTRAINT IF EXISTS fk_federatedlogin_user_id_user; ALTER TABLE IF EXISTS ONLY public.federatedlogin DROP CONSTRAINT IF EXISTS fk_federatedlogin_user_id_user;
ALTER TABLE IF EXISTS ONLY public.federatedlogin DROP CONSTRAINT IF EXISTS fk_federatedlogin_service_id_loginservice; ALTER TABLE IF EXISTS ONLY public.federatedlogin DROP CONSTRAINT IF EXISTS fk_federatedlogin_service_id_loginservice;
ALTER TABLE IF EXISTS ONLY public.emailconfirmation DROP CONSTRAINT IF EXISTS fk_emailconfirmation_user_id_user; ALTER TABLE IF EXISTS ONLY public.emailconfirmation DROP CONSTRAINT IF EXISTS fk_emailconfirmation_user_id_user;
ALTER TABLE IF EXISTS ONLY public.derivedstorageforimage DROP CONSTRAINT IF EXISTS fk_derivedstorageforimage_transformation_constraint;
ALTER TABLE IF EXISTS ONLY public.derivedstorageforimage DROP CONSTRAINT IF EXISTS fk_derivedstorageforimage_source_image_id_image;
ALTER TABLE IF EXISTS ONLY public.derivedstorageforimage DROP CONSTRAINT IF EXISTS fk_derivedstorageforimage_derivative_id_imagestorage;
ALTER TABLE IF EXISTS ONLY public.deletedrepository DROP CONSTRAINT IF EXISTS fk_deletedrepository_repository_id_repository; ALTER TABLE IF EXISTS ONLY public.deletedrepository DROP CONSTRAINT IF EXISTS fk_deletedrepository_repository_id_repository;
ALTER TABLE IF EXISTS ONLY public.deletednamespace DROP CONSTRAINT IF EXISTS fk_deletednamespace_namespace_id_user; ALTER TABLE IF EXISTS ONLY public.deletednamespace DROP CONSTRAINT IF EXISTS fk_deletednamespace_namespace_id_user;
ALTER TABLE IF EXISTS ONLY public.blobupload DROP CONSTRAINT IF EXISTS fk_blobupload_repository_id_repository; ALTER TABLE IF EXISTS ONLY public.blobupload DROP CONSTRAINT IF EXISTS fk_blobupload_repository_id_repository;
@ -325,6 +302,9 @@ DROP INDEX IF EXISTS public.permissionprototype_org_id;
DROP INDEX IF EXISTS public.permissionprototype_delegate_user_id; DROP INDEX IF EXISTS public.permissionprototype_delegate_user_id;
DROP INDEX IF EXISTS public.permissionprototype_delegate_team_id; DROP INDEX IF EXISTS public.permissionprototype_delegate_team_id;
DROP INDEX IF EXISTS public.permissionprototype_activating_user_id; DROP INDEX IF EXISTS public.permissionprototype_activating_user_id;
DROP INDEX IF EXISTS public.organizationrhskus_subscription_id_org_id_user_id;
DROP INDEX IF EXISTS public.organizationrhskus_subscription_id_org_id;
DROP INDEX IF EXISTS public.organizationrhskus_subscription_id;
DROP INDEX IF EXISTS public.oauthauthorizationcode_code_name; DROP INDEX IF EXISTS public.oauthauthorizationcode_code_name;
DROP INDEX IF EXISTS public.oauthauthorizationcode_application_id; DROP INDEX IF EXISTS public.oauthauthorizationcode_application_id;
DROP INDEX IF EXISTS public.oauthapplication_organization_id; DROP INDEX IF EXISTS public.oauthapplication_organization_id;
@ -538,6 +518,7 @@ ALTER TABLE IF EXISTS ONLY public.quayrelease DROP CONSTRAINT IF EXISTS pk_quayr
ALTER TABLE IF EXISTS ONLY public.quayregion DROP CONSTRAINT IF EXISTS pk_quayregion; ALTER TABLE IF EXISTS ONLY public.quayregion DROP CONSTRAINT IF EXISTS pk_quayregion;
ALTER TABLE IF EXISTS ONLY public.proxycacheconfig DROP CONSTRAINT IF EXISTS pk_proxy_cache_config; ALTER TABLE IF EXISTS ONLY public.proxycacheconfig DROP CONSTRAINT IF EXISTS pk_proxy_cache_config;
ALTER TABLE IF EXISTS ONLY public.permissionprototype DROP CONSTRAINT IF EXISTS pk_permissionprototype; ALTER TABLE IF EXISTS ONLY public.permissionprototype DROP CONSTRAINT IF EXISTS pk_permissionprototype;
ALTER TABLE IF EXISTS ONLY public.organizationrhskus DROP CONSTRAINT IF EXISTS pk_organizationrhskus;
ALTER TABLE IF EXISTS ONLY public.oauthauthorizationcode DROP CONSTRAINT IF EXISTS pk_oauthauthorizationcode; ALTER TABLE IF EXISTS ONLY public.oauthauthorizationcode DROP CONSTRAINT IF EXISTS pk_oauthauthorizationcode;
ALTER TABLE IF EXISTS ONLY public.oauthapplication DROP CONSTRAINT IF EXISTS pk_oauthapplication; ALTER TABLE IF EXISTS ONLY public.oauthapplication DROP CONSTRAINT IF EXISTS pk_oauthapplication;
ALTER TABLE IF EXISTS ONLY public.oauthaccesstoken DROP CONSTRAINT IF EXISTS pk_oauthaccesstoken; ALTER TABLE IF EXISTS ONLY public.oauthaccesstoken DROP CONSTRAINT IF EXISTS pk_oauthaccesstoken;
@ -640,6 +621,7 @@ ALTER TABLE IF EXISTS public.quayrelease ALTER COLUMN id DROP DEFAULT;
ALTER TABLE IF EXISTS public.quayregion ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS public.quayregion ALTER COLUMN id DROP DEFAULT;
ALTER TABLE IF EXISTS public.proxycacheconfig ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS public.proxycacheconfig ALTER COLUMN id DROP DEFAULT;
ALTER TABLE IF EXISTS public.permissionprototype ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS public.permissionprototype ALTER COLUMN id DROP DEFAULT;
ALTER TABLE IF EXISTS public.organizationrhskus ALTER COLUMN id DROP DEFAULT;
ALTER TABLE IF EXISTS public.oauthauthorizationcode ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS public.oauthauthorizationcode ALTER COLUMN id DROP DEFAULT;
ALTER TABLE IF EXISTS public.oauthapplication ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS public.oauthapplication ALTER COLUMN id DROP DEFAULT;
ALTER TABLE IF EXISTS public.oauthaccesstoken ALTER COLUMN id DROP DEFAULT; ALTER TABLE IF EXISTS public.oauthaccesstoken ALTER COLUMN id DROP DEFAULT;
@ -792,6 +774,8 @@ DROP SEQUENCE IF EXISTS public.proxycacheconfig_id_seq;
DROP TABLE IF EXISTS public.proxycacheconfig; DROP TABLE IF EXISTS public.proxycacheconfig;
DROP SEQUENCE IF EXISTS public.permissionprototype_id_seq; DROP SEQUENCE IF EXISTS public.permissionprototype_id_seq;
DROP TABLE IF EXISTS public.permissionprototype; DROP TABLE IF EXISTS public.permissionprototype;
DROP SEQUENCE IF EXISTS public.organizationrhskus_id_seq;
DROP TABLE IF EXISTS public.organizationrhskus;
DROP SEQUENCE IF EXISTS public.oauthauthorizationcode_id_seq; DROP SEQUENCE IF EXISTS public.oauthauthorizationcode_id_seq;
DROP TABLE IF EXISTS public.oauthauthorizationcode; DROP TABLE IF EXISTS public.oauthauthorizationcode;
DROP SEQUENCE IF EXISTS public.oauthapplication_id_seq; DROP SEQUENCE IF EXISTS public.oauthapplication_id_seq;
@ -2786,6 +2770,42 @@ ALTER TABLE public.oauthauthorizationcode_id_seq OWNER TO quay;
ALTER SEQUENCE public.oauthauthorizationcode_id_seq OWNED BY public.oauthauthorizationcode.id; ALTER SEQUENCE public.oauthauthorizationcode_id_seq OWNED BY public.oauthauthorizationcode.id;
--
-- Name: organizationrhskus; Type: TABLE; Schema: public; Owner: quay
--
CREATE TABLE public.organizationrhskus (
id integer NOT NULL,
subscription_id integer NOT NULL,
org_id integer NOT NULL,
user_id integer NOT NULL
);
ALTER TABLE public.organizationrhskus OWNER TO quay;
--
-- Name: organizationrhskus_id_seq; Type: SEQUENCE; Schema: public; Owner: quay
--
CREATE SEQUENCE public.organizationrhskus_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE public.organizationrhskus_id_seq OWNER TO quay;
--
-- Name: organizationrhskus_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: quay
--
ALTER SEQUENCE public.organizationrhskus_id_seq OWNED BY public.organizationrhskus.id;
-- --
-- Name: permissionprototype; Type: TABLE; Schema: public; Owner: quay -- Name: permissionprototype; Type: TABLE; Schema: public; Owner: quay
-- --
@ -5057,6 +5077,13 @@ ALTER TABLE ONLY public.oauthapplication ALTER COLUMN id SET DEFAULT nextval('pu
ALTER TABLE ONLY public.oauthauthorizationcode ALTER COLUMN id SET DEFAULT nextval('public.oauthauthorizationcode_id_seq'::regclass); ALTER TABLE ONLY public.oauthauthorizationcode ALTER COLUMN id SET DEFAULT nextval('public.oauthauthorizationcode_id_seq'::regclass);
--
-- Name: organizationrhskus id; Type: DEFAULT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.organizationrhskus ALTER COLUMN id SET DEFAULT nextval('public.organizationrhskus_id_seq'::regclass);
-- --
-- Name: permissionprototype id; Type: DEFAULT; Schema: public; Owner: quay -- Name: permissionprototype id; Type: DEFAULT; Schema: public; Owner: quay
-- --
@ -5437,7 +5464,7 @@ COPY public.accesstokenkind (id, name) FROM stdin;
-- --
COPY public.alembic_version (version_num) FROM stdin; COPY public.alembic_version (version_num) FROM stdin;
46980ea2dde5 b82361fba1cd
\. \.
@ -6271,6 +6298,14 @@ COPY public.oauthauthorizationcode (id, application_id, scope, data, code_creden
\. \.
--
-- Data for Name: organizationrhskus; Type: TABLE DATA; Schema: public; Owner: quay
--
COPY public.organizationrhskus (id, subscription_id, org_id, user_id) FROM stdin;
\.
-- --
-- Data for Name: permissionprototype; Type: TABLE DATA; Schema: public; Owner: quay -- Data for Name: permissionprototype; Type: TABLE DATA; Schema: public; Owner: quay
-- --
@ -6316,8 +6351,8 @@ COPY public.quayservice (id, name) FROM stdin;
-- --
COPY public.queueitem (id, queue_name, body, available_after, available, processing_expires, retries_remaining, state_id) FROM stdin; COPY public.queueitem (id, queue_name, body, available_after, available, processing_expires, retries_remaining, state_id) FROM stdin;
1 namespacegc/2/ {"marker_id": 1, "original_username": "quay"} 2023-06-28 18:17:46.058418 t 2023-06-28 21:12:45.983351 5 f4026e00-acc1-4634-b560-7b9dfe4ea2c0
2 namespacegc/3/ {"marker_id": 2, "original_username": "clair"} 2023-06-28 18:17:51.112431 t 2023-06-28 21:12:51.080949 5 1f7f0b49-2e34-4a00-8414-2559e4bbe63f 2 namespacegc/3/ {"marker_id": 2, "original_username": "clair"} 2023-06-28 18:17:51.112431 t 2023-06-28 21:12:51.080949 5 1f7f0b49-2e34-4a00-8414-2559e4bbe63f
1 namespacegc/2/ {"marker_id": 1, "original_username": "quay"} 2023-08-25 15:22:54.808849 t 2023-08-25 18:17:54.783824 5 89f5e206-0b48-4227-8e7f-15e9a549fa2e
\. \.
@ -7951,6 +7986,13 @@ SELECT pg_catalog.setval('public.oauthapplication_id_seq', 1, false);
SELECT pg_catalog.setval('public.oauthauthorizationcode_id_seq', 1, false); SELECT pg_catalog.setval('public.oauthauthorizationcode_id_seq', 1, false);
--
-- Name: organizationrhskus_id_seq; Type: SEQUENCE SET; Schema: public; Owner: quay
--
SELECT pg_catalog.setval('public.organizationrhskus_id_seq', 1, false);
-- --
-- Name: permissionprototype_id_seq; Type: SEQUENCE SET; Schema: public; Owner: quay -- Name: permissionprototype_id_seq; Type: SEQUENCE SET; Schema: public; Owner: quay
-- --
@ -8716,6 +8758,14 @@ ALTER TABLE ONLY public.oauthauthorizationcode
ADD CONSTRAINT pk_oauthauthorizationcode PRIMARY KEY (id); ADD CONSTRAINT pk_oauthauthorizationcode PRIMARY KEY (id);
--
-- Name: organizationrhskus pk_organizationrhskus; Type: CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.organizationrhskus
ADD CONSTRAINT pk_organizationrhskus PRIMARY KEY (id);
-- --
-- Name: permissionprototype pk_permissionprototype; Type: CONSTRAINT; Schema: public; Owner: quay -- Name: permissionprototype pk_permissionprototype; Type: CONSTRAINT; Schema: public; Owner: quay
-- --
@ -10258,6 +10308,27 @@ CREATE INDEX oauthauthorizationcode_application_id ON public.oauthauthorizationc
CREATE UNIQUE INDEX oauthauthorizationcode_code_name ON public.oauthauthorizationcode USING btree (code_name); CREATE UNIQUE INDEX oauthauthorizationcode_code_name ON public.oauthauthorizationcode USING btree (code_name);
--
-- Name: organizationrhskus_subscription_id; Type: INDEX; Schema: public; Owner: quay
--
CREATE UNIQUE INDEX organizationrhskus_subscription_id ON public.organizationrhskus USING btree (subscription_id);
--
-- Name: organizationrhskus_subscription_id_org_id; Type: INDEX; Schema: public; Owner: quay
--
CREATE UNIQUE INDEX organizationrhskus_subscription_id_org_id ON public.organizationrhskus USING btree (subscription_id, org_id);
--
-- Name: organizationrhskus_subscription_id_org_id_user_id; Type: INDEX; Schema: public; Owner: quay
--
CREATE UNIQUE INDEX organizationrhskus_subscription_id_org_id_user_id ON public.organizationrhskus USING btree (subscription_id, org_id, user_id);
-- --
-- Name: permissionprototype_activating_user_id; Type: INDEX; Schema: public; Owner: quay -- Name: permissionprototype_activating_user_id; Type: INDEX; Schema: public; Owner: quay
-- --
@ -11568,30 +11639,6 @@ ALTER TABLE ONLY public.deletedrepository
ADD CONSTRAINT fk_deletedrepository_repository_id_repository FOREIGN KEY (repository_id) REFERENCES public.repository(id); ADD CONSTRAINT fk_deletedrepository_repository_id_repository FOREIGN KEY (repository_id) REFERENCES public.repository(id);
--
-- Name: derivedstorageforimage fk_derivedstorageforimage_derivative_id_imagestorage; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.derivedstorageforimage
ADD CONSTRAINT fk_derivedstorageforimage_derivative_id_imagestorage FOREIGN KEY (derivative_id) REFERENCES public.imagestorage(id);
--
-- Name: derivedstorageforimage fk_derivedstorageforimage_source_image_id_image; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.derivedstorageforimage
ADD CONSTRAINT fk_derivedstorageforimage_source_image_id_image FOREIGN KEY (source_image_id) REFERENCES public.image(id);
--
-- Name: derivedstorageforimage fk_derivedstorageforimage_transformation_constraint; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.derivedstorageforimage
ADD CONSTRAINT fk_derivedstorageforimage_transformation_constraint FOREIGN KEY (transformation_id) REFERENCES public.imagestoragetransformation(id);
-- --
-- Name: emailconfirmation fk_emailconfirmation_user_id_user; Type: FK CONSTRAINT; Schema: public; Owner: quay -- Name: emailconfirmation fk_emailconfirmation_user_id_user; Type: FK CONSTRAINT; Schema: public; Owner: quay
-- --
@ -11616,22 +11663,6 @@ ALTER TABLE ONLY public.federatedlogin
ADD CONSTRAINT fk_federatedlogin_user_id_user FOREIGN KEY (user_id) REFERENCES public."user"(id); ADD CONSTRAINT fk_federatedlogin_user_id_user FOREIGN KEY (user_id) REFERENCES public."user"(id);
--
-- Name: image fk_image_repository_id_repository; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.image
ADD CONSTRAINT fk_image_repository_id_repository FOREIGN KEY (repository_id) REFERENCES public.repository(id);
--
-- Name: image fk_image_storage_id_imagestorage; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.image
ADD CONSTRAINT fk_image_storage_id_imagestorage FOREIGN KEY (storage_id) REFERENCES public.imagestorage(id);
-- --
-- Name: imagestorageplacement fk_imagestorageplacement_location_id_imagestoragelocation; Type: FK CONSTRAINT; Schema: public; Owner: quay -- Name: imagestorageplacement fk_imagestorageplacement_location_id_imagestoragelocation; Type: FK CONSTRAINT; Schema: public; Owner: quay
-- --
@ -11784,30 +11815,6 @@ ALTER TABLE ONLY public.manifestlabel
ADD CONSTRAINT fk_manifestlabel_repository_id_repository FOREIGN KEY (repository_id) REFERENCES public.repository(id); ADD CONSTRAINT fk_manifestlabel_repository_id_repository FOREIGN KEY (repository_id) REFERENCES public.repository(id);
--
-- Name: manifestlegacyimage fk_manifestlegacyimage_image_id_image; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.manifestlegacyimage
ADD CONSTRAINT fk_manifestlegacyimage_image_id_image FOREIGN KEY (image_id) REFERENCES public.image(id);
--
-- Name: manifestlegacyimage fk_manifestlegacyimage_manifest_id_manifest; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.manifestlegacyimage
ADD CONSTRAINT fk_manifestlegacyimage_manifest_id_manifest FOREIGN KEY (manifest_id) REFERENCES public.manifest(id);
--
-- Name: manifestlegacyimage fk_manifestlegacyimage_repository_id_repository; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.manifestlegacyimage
ADD CONSTRAINT fk_manifestlegacyimage_repository_id_repository FOREIGN KEY (repository_id) REFERENCES public.repository(id);
-- --
-- Name: manifestsecuritystatus fk_manifestsecuritystatus_manifest_id_manifest; Type: FK CONSTRAINT; Schema: public; Owner: quay -- Name: manifestsecuritystatus fk_manifestsecuritystatus_manifest_id_manifest; Type: FK CONSTRAINT; Schema: public; Owner: quay
-- --
@ -11888,6 +11895,22 @@ ALTER TABLE ONLY public.oauthauthorizationcode
ADD CONSTRAINT fk_oauthauthorizationcode_application_id_oauthapplication FOREIGN KEY (application_id) REFERENCES public.oauthapplication(id); ADD CONSTRAINT fk_oauthauthorizationcode_application_id_oauthapplication FOREIGN KEY (application_id) REFERENCES public.oauthapplication(id);
--
-- Name: organizationrhskus fk_organizationrhskus_orgid; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.organizationrhskus
ADD CONSTRAINT fk_organizationrhskus_orgid FOREIGN KEY (org_id) REFERENCES public."user"(id);
--
-- Name: organizationrhskus fk_organizationrhskus_userid; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.organizationrhskus
ADD CONSTRAINT fk_organizationrhskus_userid FOREIGN KEY (user_id) REFERENCES public."user"(id);
-- --
-- Name: permissionprototype fk_permissionprototype_activating_user_id_user; Type: FK CONSTRAINT; Schema: public; Owner: quay -- Name: permissionprototype fk_permissionprototype_activating_user_id_user; Type: FK CONSTRAINT; Schema: public; Owner: quay
-- --
@ -12232,22 +12255,6 @@ ALTER TABLE ONLY public.repositorysize
ADD CONSTRAINT fk_repositorysize_repository_id_repository FOREIGN KEY (repository_id) REFERENCES public.repository(id); ADD CONSTRAINT fk_repositorysize_repository_id_repository FOREIGN KEY (repository_id) REFERENCES public.repository(id);
--
-- Name: repositorytag fk_repositorytag_image_id_image; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.repositorytag
ADD CONSTRAINT fk_repositorytag_image_id_image FOREIGN KEY (image_id) REFERENCES public.image(id);
--
-- Name: repositorytag fk_repositorytag_repository_id_repository; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.repositorytag
ADD CONSTRAINT fk_repositorytag_repository_id_repository FOREIGN KEY (repository_id) REFERENCES public.repository(id);
-- --
-- Name: robotaccountmetadata fk_robotaccountmetadata_robot_account_id_user; Type: FK CONSTRAINT; Schema: public; Owner: quay -- Name: robotaccountmetadata fk_robotaccountmetadata_robot_account_id_user; Type: FK CONSTRAINT; Schema: public; Owner: quay
-- --
@ -12320,118 +12327,6 @@ ALTER TABLE ONLY public.tag
ADD CONSTRAINT fk_tag_tag_kind_id_tagkind FOREIGN KEY (tag_kind_id) REFERENCES public.tagkind(id); ADD CONSTRAINT fk_tag_tag_kind_id_tagkind FOREIGN KEY (tag_kind_id) REFERENCES public.tagkind(id);
--
-- Name: tagmanifest fk_tagmanifest_tag_id_repositorytag; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.tagmanifest
ADD CONSTRAINT fk_tagmanifest_tag_id_repositorytag FOREIGN KEY (tag_id) REFERENCES public.repositorytag(id);
--
-- Name: tagmanifestlabel fk_tagmanifestlabel_annotated_id_tagmanifest; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.tagmanifestlabel
ADD CONSTRAINT fk_tagmanifestlabel_annotated_id_tagmanifest FOREIGN KEY (annotated_id) REFERENCES public.tagmanifest(id);
--
-- Name: tagmanifestlabel fk_tagmanifestlabel_label_id_label; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.tagmanifestlabel
ADD CONSTRAINT fk_tagmanifestlabel_label_id_label FOREIGN KEY (label_id) REFERENCES public.label(id);
--
-- Name: tagmanifestlabel fk_tagmanifestlabel_repository_id_repository; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.tagmanifestlabel
ADD CONSTRAINT fk_tagmanifestlabel_repository_id_repository FOREIGN KEY (repository_id) REFERENCES public.repository(id);
--
-- Name: tagmanifestlabelmap fk_tagmanifestlabelmap_label_id_label; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.tagmanifestlabelmap
ADD CONSTRAINT fk_tagmanifestlabelmap_label_id_label FOREIGN KEY (label_id) REFERENCES public.label(id);
--
-- Name: tagmanifestlabelmap fk_tagmanifestlabelmap_manifest_id_manifest; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.tagmanifestlabelmap
ADD CONSTRAINT fk_tagmanifestlabelmap_manifest_id_manifest FOREIGN KEY (manifest_id) REFERENCES public.manifest(id);
--
-- Name: tagmanifestlabelmap fk_tagmanifestlabelmap_manifest_label_id_manifestlabel; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.tagmanifestlabelmap
ADD CONSTRAINT fk_tagmanifestlabelmap_manifest_label_id_manifestlabel FOREIGN KEY (manifest_label_id) REFERENCES public.manifestlabel(id);
--
-- Name: tagmanifestlabelmap fk_tagmanifestlabelmap_tag_manifest_id_tagmanifest; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.tagmanifestlabelmap
ADD CONSTRAINT fk_tagmanifestlabelmap_tag_manifest_id_tagmanifest FOREIGN KEY (tag_manifest_id) REFERENCES public.tagmanifest(id);
--
-- Name: tagmanifestlabelmap fk_tagmanifestlabelmap_tag_manifest_label_id_tagmanifestlabel; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.tagmanifestlabelmap
ADD CONSTRAINT fk_tagmanifestlabelmap_tag_manifest_label_id_tagmanifestlabel FOREIGN KEY (tag_manifest_label_id) REFERENCES public.tagmanifestlabel(id);
--
-- Name: tagmanifesttomanifest fk_tagmanifesttomanifest_manifest_id_manifest; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.tagmanifesttomanifest
ADD CONSTRAINT fk_tagmanifesttomanifest_manifest_id_manifest FOREIGN KEY (manifest_id) REFERENCES public.manifest(id);
--
-- Name: tagmanifesttomanifest fk_tagmanifesttomanifest_tag_manifest_id_tagmanifest; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.tagmanifesttomanifest
ADD CONSTRAINT fk_tagmanifesttomanifest_tag_manifest_id_tagmanifest FOREIGN KEY (tag_manifest_id) REFERENCES public.tagmanifest(id);
--
-- Name: tagtorepositorytag fk_tagtorepositorytag_repository_id_repository; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.tagtorepositorytag
ADD CONSTRAINT fk_tagtorepositorytag_repository_id_repository FOREIGN KEY (repository_id) REFERENCES public.repository(id);
--
-- Name: tagtorepositorytag fk_tagtorepositorytag_repository_tag_id_repositorytag; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.tagtorepositorytag
ADD CONSTRAINT fk_tagtorepositorytag_repository_tag_id_repositorytag FOREIGN KEY (repository_tag_id) REFERENCES public.repositorytag(id);
--
-- Name: tagtorepositorytag fk_tagtorepositorytag_tag_id_tag; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.tagtorepositorytag
ADD CONSTRAINT fk_tagtorepositorytag_tag_id_tag FOREIGN KEY (tag_id) REFERENCES public.tag(id);
-- --
-- Name: team fk_team_organization_id_user; Type: FK CONSTRAINT; Schema: public; Owner: quay -- Name: team fk_team_organization_id_user; Type: FK CONSTRAINT; Schema: public; Owner: quay
-- --
@ -12504,14 +12399,6 @@ ALTER TABLE ONLY public.teamsync
ADD CONSTRAINT fk_teamsync_team_id_team FOREIGN KEY (team_id) REFERENCES public.team(id); ADD CONSTRAINT fk_teamsync_team_id_team FOREIGN KEY (team_id) REFERENCES public.team(id);
--
-- Name: torrentinfo fk_torrentinfo_storage_id_imagestorage; Type: FK CONSTRAINT; Schema: public; Owner: quay
--
ALTER TABLE ONLY public.torrentinfo
ADD CONSTRAINT fk_torrentinfo_storage_id_imagestorage FOREIGN KEY (storage_id) REFERENCES public.imagestorage(id);
-- --
-- Name: uploadedblob fk_uploadedblob_blob_id_imagestorage; Type: FK CONSTRAINT; Schema: public; Owner: quay -- Name: uploadedblob fk_uploadedblob_blob_id_imagestorage; Type: FK CONSTRAINT; Schema: public; Owner: quay
-- --

View File

@ -5,8 +5,7 @@ import time
import features import features
from app import app from app import app
from app import billing as stripe from app import billing as stripe
from app import rh_marketplace_api as internal_marketplace_api from app import marketplace_subscriptions, marketplace_users
from app import rh_user_api as internal_user_api
from data import model from data import model
from data.billing import RH_SKUS, get_plan from data.billing import RH_SKUS, get_plan
from data.model import entitlements from data.model import entitlements
@ -108,7 +107,7 @@ class ReconciliationWorker(Worker):
# try to acquire lock # try to acquire lock
if skip_lock_for_testing: if skip_lock_for_testing:
self._perform_reconciliation( self._perform_reconciliation(
user_api=internal_user_api, marketplace_api=internal_marketplace_api user_api=marketplace_users, marketplace_api=marketplace_subscriptions
) )
else: else:
try: try:
@ -117,7 +116,7 @@ class ReconciliationWorker(Worker):
lock_ttl=RECONCILIATION_TIMEOUT + LOCK_TIMEOUT_PADDING, lock_ttl=RECONCILIATION_TIMEOUT + LOCK_TIMEOUT_PADDING,
): ):
self._perform_reconciliation( self._perform_reconciliation(
user_api=internal_user_api, marketplace_api=internal_marketplace_api user_api=marketplace_users, marketplace_api=marketplace_subscriptions
) )
except LockNotAcquiredException: except LockNotAcquiredException:
logger.debug("Could not acquire global lock for entitlement reconciliation") logger.debug("Could not acquire global lock for entitlement reconciliation")

View File

@ -1,79 +1,34 @@
import random import random
import string import string
from datetime import datetime
from test.fixtures import * from test.fixtures import *
from unittest.mock import MagicMock, patch from unittest.mock import patch
from dateutil.relativedelta import relativedelta
from data import model from data import model
from util.marketplace import FakeSubscriptionApi, FakeUserApi
from workers.reconciliationworker import ReconciliationWorker from workers.reconciliationworker import ReconciliationWorker
TEST_USER = { user_api = FakeUserApi()
"account_number": 12345, marketplace_api = FakeSubscriptionApi()
"email": "test_user@test.com",
"username": "test_user",
"password": "password",
}
FREE_USER = {
"account_number": 23456,
"email": "free_user@test.com",
"username": "free_user",
"password": "password",
}
class FakeUserApi:
def lookup_customer_id(self, email):
if email == TEST_USER["email"]:
return TEST_USER["account_number"]
if email == FREE_USER["email"]:
return FREE_USER["account_number"]
return None
class FakeMarketplaceApi:
def __init__(self):
self.subscription_extended = False
self.subscription_created = False
def lookup_subscription(self, customerId, sku_id):
return None
def create_entitlement(self, customerId, skuId):
pass
internal_user_api = FakeUserApi()
internal_marketplace_api = FakeMarketplaceApi()
worker = ReconciliationWorker() worker = ReconciliationWorker()
def test_create_for_stripe_user(initialized_db): def test_create_for_stripe_user(initialized_db):
test_user = model.user.create_user( test_user = model.user.create_user("test_user", "password", "test_user@test.com")
TEST_USER["username"], TEST_USER["password"], TEST_USER["email"]
)
test_user.stripe_id = "cus_" + "".join(random.choices(string.ascii_lowercase, k=14)) test_user.stripe_id = "cus_" + "".join(random.choices(string.ascii_lowercase, k=14))
test_user.save() test_user.save()
with patch.object(internal_marketplace_api, "create_entitlement") as mock: with patch.object(marketplace_api, "create_entitlement") as mock:
worker._perform_reconciliation( worker._perform_reconciliation(user_api=user_api, marketplace_api=marketplace_api)
user_api=internal_user_api, marketplace_api=internal_marketplace_api
)
mock.assert_called() mock.assert_called()
def test_skip_free_user(initialized_db): def test_skip_free_user(initialized_db):
free_user = model.user.create_user( free_user = model.user.create_user("free_user", "password", "free_user@test.com")
FREE_USER["username"], FREE_USER["password"], FREE_USER["email"]
)
free_user.save() free_user.save()
with patch.object(internal_marketplace_api, "create_entitlement") as mock: with patch.object(marketplace_api, "create_entitlement") as mock:
worker._perform_reconciliation( worker._perform_reconciliation(user_api=user_api, marketplace_api=marketplace_api)
user_api=internal_user_api, marketplace_api=internal_marketplace_api
)
mock.assert_not_called() mock.assert_not_called()