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:
6
.gitignore
vendored
6
.gitignore
vendored
@ -111,4 +111,8 @@ web/yarn-error.log*
|
||||
# Cypress files
|
||||
web/cypress/videos
|
||||
web/cypress/screenshots
|
||||
web/cypress/downloads
|
||||
web/cypress/downloads
|
||||
|
||||
# marketplace
|
||||
local-dev/stack/quay-marketplace-api.crt
|
||||
local-dev/stack/quay-marketplace-api.key
|
||||
|
@ -23,8 +23,11 @@ repos:
|
||||
entry: web/node_modules/.bin/eslint --fix
|
||||
language: system
|
||||
files: ^web/
|
||||
exclude: ^web/cypress/test/
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.3.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
exclude: ^web/cypress/test/quay-db-data.txt
|
||||
- id: end-of-file-fixer
|
||||
exclude: ^web/cypress/test/quay-db-data.txt
|
||||
|
11
app.py
11
app.py
@ -53,7 +53,7 @@ from util.greenlet_tracing import enable_tracing
|
||||
from util.ipresolver import IPResolver
|
||||
from util.label_validator import LabelValidator
|
||||
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.names import urn_generator
|
||||
from util.repomirror.api import RepoMirrorAPI
|
||||
@ -236,12 +236,6 @@ Principal(app, use_sessions=False)
|
||||
|
||||
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)
|
||||
avatar = Avatar(app)
|
||||
login_manager = LoginManager(app)
|
||||
@ -310,6 +304,9 @@ repo_mirror_api = RepoMirrorAPI(
|
||||
|
||||
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.
|
||||
_v2_key_path = os.path.join(OVERRIDE_CONFIG_DIRECTORY, DOCKER_V2_SIGNINGKEY_FILENAME)
|
||||
if os.path.exists(_v2_key_path):
|
||||
|
@ -745,6 +745,7 @@ class User(BaseModel):
|
||||
UserOrganizationQuota,
|
||||
QuotaLimits,
|
||||
RedHatSubscriptions,
|
||||
OrganizationRhSkus,
|
||||
}
|
||||
| appr_classes
|
||||
| v22_classes
|
||||
@ -1981,13 +1982,29 @@ class ProxyCacheConfig(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)
|
||||
|
||||
|
||||
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
|
||||
# to meet length restrictions.
|
||||
LEGACY_INDEX_MAP = {
|
||||
|
@ -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")
|
@ -137,6 +137,10 @@ class UnsupportedQuotaSize(DataModelException):
|
||||
pass
|
||||
|
||||
|
||||
class OrgSubscriptionBindingAlreadyExists(DataModelException):
|
||||
pass
|
||||
|
||||
|
||||
class TooManyLoginAttemptsException(Exception):
|
||||
def __init__(self, message, retry_after):
|
||||
super(TooManyLoginAttemptsException, self).__init__(message)
|
||||
@ -178,6 +182,7 @@ from data.model import (
|
||||
notification,
|
||||
oauth,
|
||||
organization,
|
||||
organization_skus,
|
||||
permission,
|
||||
proxy_cache,
|
||||
release,
|
||||
|
56
data/model/organization_skus.py
Normal file
56
data/model/organization_skus.py
Normal 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)
|
@ -9,12 +9,13 @@ import stripe
|
||||
from flask import request
|
||||
|
||||
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.auth_context import get_authenticated_user
|
||||
from auth.permissions import AdministerOrganizationPermission
|
||||
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 (
|
||||
ApiResource,
|
||||
abort,
|
||||
@ -47,11 +48,22 @@ def check_internal_api_for_subscription(namespace_user):
|
||||
Returns subscription from RH marketplace.
|
||||
None returned if no subscription is found.
|
||||
"""
|
||||
user_account_number = rh_user_api.get_account_number(namespace_user)
|
||||
if user_account_number:
|
||||
user_subscriptions = rh_marketplace_api.find_stripe_subscription(user_account_number)
|
||||
return user_subscriptions
|
||||
return []
|
||||
plans = []
|
||||
if namespace_user.organization:
|
||||
query = organization_skus.get_org_subscriptions(namespace_user.id)
|
||||
org_subscriptions = list(query.dicts()) if query is not None else []
|
||||
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):
|
||||
@ -87,9 +99,11 @@ def lookup_allowed_private_repos(namespace):
|
||||
|
||||
if features.RH_MARKETPLACE:
|
||||
namespace_user = model.user.get_namespace_user(namespace)
|
||||
marketplace_subscriptions = check_internal_api_for_subscription(namespace_user)
|
||||
for subscription in marketplace_subscriptions:
|
||||
repos_allowed += subscription["privateRepos"]
|
||||
|
||||
subscriptions = check_internal_api_for_subscription(namespace_user)
|
||||
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
|
||||
# plan subscribed.
|
||||
@ -916,3 +930,127 @@ class OrganizationInvoiceField(ApiResource):
|
||||
return "Okay", 201
|
||||
|
||||
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
|
||||
|
@ -10,7 +10,7 @@ from flask import request
|
||||
import features
|
||||
from app import all_queues, app, authentication, avatar
|
||||
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.auth_context import get_authenticated_user
|
||||
from auth.permissions import (
|
||||
@ -21,8 +21,9 @@ from auth.permissions import (
|
||||
ViewTeamPermission,
|
||||
)
|
||||
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.model import organization_skus
|
||||
from endpoints.api import (
|
||||
ApiResource,
|
||||
allow_if_superuser,
|
||||
@ -42,6 +43,7 @@ from endpoints.api import (
|
||||
from endpoints.api.user import PrivateRepositories, User
|
||||
from endpoints.exception import NotFound, Unauthorized
|
||||
from proxy import Proxy, UpstreamRegistryError
|
||||
from util.marketplace import MarketplaceSubscriptionApi
|
||||
from util.names import parse_robot_username
|
||||
from util.request import get_request_ip
|
||||
|
||||
@ -364,16 +366,27 @@ class OrgPrivateRepositories(ApiResource):
|
||||
organization = model.organization.get_organization(orgname)
|
||||
private_repos = model.user.get_private_repo_count(organization.username)
|
||||
data = {"privateAllowed": False}
|
||||
repos_allowed = 0
|
||||
|
||||
if organization.stripe_id:
|
||||
cus = stripe.Customer.retrieve(organization.stripe_id)
|
||||
if cus.subscription:
|
||||
repos_allowed = 0
|
||||
plan = get_plan(cus.subscription.plan.id)
|
||||
if plan:
|
||||
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():
|
||||
data["privateCount"] = private_repos
|
||||
|
@ -482,6 +482,9 @@ class TeamMember(ApiResource):
|
||||
return "", 204
|
||||
|
||||
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})
|
||||
return "", 204
|
||||
|
||||
|
@ -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 mock import patch
|
||||
|
||||
@ -6024,6 +6026,38 @@ SECURITY_TESTS: List[
|
||||
"devtable",
|
||||
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,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -16,10 +16,10 @@ from app import all_queues, app, authentication, avatar
|
||||
from app import billing as stripe
|
||||
from app import (
|
||||
ip_resolver,
|
||||
marketplace_subscriptions,
|
||||
marketplace_users,
|
||||
namespace_gc_queue,
|
||||
oauth_login,
|
||||
rh_marketplace_api,
|
||||
rh_user_api,
|
||||
url_scheme_and_hostname,
|
||||
)
|
||||
from auth import scopes
|
||||
@ -638,12 +638,12 @@ class PrivateRepositories(ApiResource):
|
||||
repos_allowed = plan["privateRepos"]
|
||||
if features.RH_MARKETPLACE:
|
||||
# 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:
|
||||
marketplace_subscriptions = rh_marketplace_api.find_stripe_subscription(
|
||||
user_account_number
|
||||
subscriptions = marketplace_subscriptions.get_list_of_subscriptions(
|
||||
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"]
|
||||
|
||||
return {"privateCount": private_repos, "privateAllowed": (private_repos < repos_allowed)}
|
||||
|
@ -1,3 +1,9 @@
|
||||
# isort: skip_file
|
||||
from typing import Dict, Any
|
||||
import logging
|
||||
import json
|
||||
import hashlib
|
||||
import random
|
||||
import argparse
|
||||
import calendar
|
||||
import hashlib
|
||||
@ -1339,6 +1345,7 @@ WHITELISTED_EMPTY_MODELS = [
|
||||
"Image",
|
||||
"ProxyCacheConfig",
|
||||
"RedHatSubscriptions",
|
||||
"OrganizationRhSkus",
|
||||
"QuotaRegistrySize",
|
||||
]
|
||||
|
||||
|
@ -37,6 +37,8 @@ from endpoints.api.billing import (
|
||||
ListPlans,
|
||||
OrganizationCard,
|
||||
OrganizationPlan,
|
||||
OrganizationRhSku,
|
||||
OrganizationRhSkuSubscriptionField,
|
||||
UserCard,
|
||||
UserPlan,
|
||||
)
|
||||
@ -5065,5 +5067,62 @@ class TestSuperUserManagement(ApiTestCase):
|
||||
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__":
|
||||
unittest.main()
|
||||
|
@ -112,3 +112,5 @@ class TestConfig(DefaultConfig):
|
||||
FEATURE_PROXY_CACHE = True
|
||||
PERMANENTLY_DELETE_TAGS = True
|
||||
RESET_CHILD_MANIFEST_EXPIRATION = True
|
||||
|
||||
FEATURE_RH_MARKETPLACE = True
|
||||
|
@ -5,8 +5,8 @@ from datetime import datetime
|
||||
|
||||
import requests
|
||||
|
||||
from data.billing import RH_SKUS, get_plan, get_plan_using_rh_sku
|
||||
from data.model import entitlements
|
||||
from data.billing import RH_SKUS, get_plan_using_rh_sku
|
||||
from data.model import entitlements, organization_skus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -16,7 +16,7 @@ MARKETPLACE_FILE = "/conf/stack/quay-marketplace-api.crt"
|
||||
MARKETPLACE_SECRET = "/conf/stack/quay-marketplace-api.key"
|
||||
|
||||
|
||||
class RHUserAPI:
|
||||
class RedHatUserApi(object):
|
||||
def __init__(self, app_config):
|
||||
self.cert = (MARKETPLACE_FILE, MARKETPLACE_SECRET)
|
||||
self.user_endpoint = app_config.get("ENTITLEMENT_RECONCILIATION_USER_ENDPOINT")
|
||||
@ -52,7 +52,6 @@ class RHUserAPI:
|
||||
}
|
||||
|
||||
request_url = f"{self.user_endpoint}/v2/findUsers"
|
||||
|
||||
r = requests.request(
|
||||
method="post",
|
||||
url=request_url,
|
||||
@ -69,7 +68,7 @@ class RHUserAPI:
|
||||
return account_number
|
||||
|
||||
|
||||
class RHMarketplaceAPI:
|
||||
class RedHatSubscriptionApi(object):
|
||||
def __init__(self, app_config):
|
||||
self.cert = (MARKETPLACE_FILE, MARKETPLACE_SECRET)
|
||||
self.marketplace_endpoint = app_config.get(
|
||||
@ -108,6 +107,7 @@ class RHMarketplaceAPI:
|
||||
now_ms = time.time() * 1000
|
||||
# Is subscription still valid?
|
||||
if now_ms < end_date:
|
||||
logger.debug("subscription found for %s", str(skuId))
|
||||
return subscription
|
||||
return None
|
||||
|
||||
@ -165,15 +165,170 @@ class RHMarketplaceAPI:
|
||||
)
|
||||
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
|
||||
for a given account number
|
||||
Return the sku for a specific subscription
|
||||
"""
|
||||
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:
|
||||
user_subscription = self.lookup_subscription(account_number, sku)
|
||||
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)
|
||||
|
@ -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.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.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_service_id_loginservice;
|
||||
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.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.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_repository_id_repository;
|
||||
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.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.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.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;
|
||||
@ -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_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.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.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;
|
||||
@ -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.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.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_manifest_id_manifest;
|
||||
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.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.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_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.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.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;
|
||||
@ -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_team_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_application_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.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.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.oauthapplication DROP CONSTRAINT IF EXISTS pk_oauthapplication;
|
||||
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.proxycacheconfig 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.oauthapplication 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 SEQUENCE IF EXISTS public.permissionprototype_id_seq;
|
||||
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 TABLE IF EXISTS public.oauthauthorizationcode;
|
||||
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;
|
||||
|
||||
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
@ -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);
|
||||
|
||||
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
@ -5437,7 +5464,7 @@ COPY public.accesstokenkind (id, name) 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
|
||||
--
|
||||
@ -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;
|
||||
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
|
||||
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);
|
||||
|
||||
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
@ -8716,6 +8758,14 @@ ALTER TABLE ONLY public.oauthauthorizationcode
|
||||
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
|
||||
--
|
||||
@ -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);
|
||||
|
||||
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
@ -11568,30 +11639,6 @@ ALTER TABLE ONLY public.deletedrepository
|
||||
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
|
||||
--
|
||||
@ -11616,22 +11663,6 @@ ALTER TABLE ONLY public.federatedlogin
|
||||
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
|
||||
--
|
||||
@ -11784,30 +11815,6 @@ ALTER TABLE ONLY public.manifestlabel
|
||||
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
|
||||
--
|
||||
@ -11888,6 +11895,22 @@ ALTER TABLE ONLY public.oauthauthorizationcode
|
||||
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
|
||||
--
|
||||
@ -12232,22 +12255,6 @@ ALTER TABLE ONLY public.repositorysize
|
||||
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
|
||||
--
|
||||
@ -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);
|
||||
|
||||
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
@ -12504,14 +12399,6 @@ ALTER TABLE ONLY public.teamsync
|
||||
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
|
||||
--
|
||||
|
@ -5,8 +5,7 @@ import time
|
||||
import features
|
||||
from app import app
|
||||
from app import billing as stripe
|
||||
from app import rh_marketplace_api as internal_marketplace_api
|
||||
from app import rh_user_api as internal_user_api
|
||||
from app import marketplace_subscriptions, marketplace_users
|
||||
from data import model
|
||||
from data.billing import RH_SKUS, get_plan
|
||||
from data.model import entitlements
|
||||
@ -108,7 +107,7 @@ class ReconciliationWorker(Worker):
|
||||
# try to acquire lock
|
||||
if skip_lock_for_testing:
|
||||
self._perform_reconciliation(
|
||||
user_api=internal_user_api, marketplace_api=internal_marketplace_api
|
||||
user_api=marketplace_users, marketplace_api=marketplace_subscriptions
|
||||
)
|
||||
else:
|
||||
try:
|
||||
@ -117,7 +116,7 @@ class ReconciliationWorker(Worker):
|
||||
lock_ttl=RECONCILIATION_TIMEOUT + LOCK_TIMEOUT_PADDING,
|
||||
):
|
||||
self._perform_reconciliation(
|
||||
user_api=internal_user_api, marketplace_api=internal_marketplace_api
|
||||
user_api=marketplace_users, marketplace_api=marketplace_subscriptions
|
||||
)
|
||||
except LockNotAcquiredException:
|
||||
logger.debug("Could not acquire global lock for entitlement reconciliation")
|
||||
|
@ -1,79 +1,34 @@
|
||||
import random
|
||||
import string
|
||||
from datetime import datetime
|
||||
from test.fixtures import *
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
from data import model
|
||||
from util.marketplace import FakeSubscriptionApi, FakeUserApi
|
||||
from workers.reconciliationworker import ReconciliationWorker
|
||||
|
||||
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",
|
||||
}
|
||||
|
||||
|
||||
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()
|
||||
user_api = FakeUserApi()
|
||||
marketplace_api = FakeSubscriptionApi()
|
||||
worker = ReconciliationWorker()
|
||||
|
||||
|
||||
def test_create_for_stripe_user(initialized_db):
|
||||
|
||||
test_user = model.user.create_user(
|
||||
TEST_USER["username"], TEST_USER["password"], TEST_USER["email"]
|
||||
)
|
||||
test_user = model.user.create_user("test_user", "password", "test_user@test.com")
|
||||
test_user.stripe_id = "cus_" + "".join(random.choices(string.ascii_lowercase, k=14))
|
||||
test_user.save()
|
||||
with patch.object(internal_marketplace_api, "create_entitlement") as mock:
|
||||
worker._perform_reconciliation(
|
||||
user_api=internal_user_api, marketplace_api=internal_marketplace_api
|
||||
)
|
||||
with patch.object(marketplace_api, "create_entitlement") as mock:
|
||||
worker._perform_reconciliation(user_api=user_api, marketplace_api=marketplace_api)
|
||||
|
||||
mock.assert_called()
|
||||
|
||||
|
||||
def test_skip_free_user(initialized_db):
|
||||
|
||||
free_user = model.user.create_user(
|
||||
FREE_USER["username"], FREE_USER["password"], FREE_USER["email"]
|
||||
)
|
||||
free_user = model.user.create_user("free_user", "password", "free_user@test.com")
|
||||
free_user.save()
|
||||
|
||||
with patch.object(internal_marketplace_api, "create_entitlement") as mock:
|
||||
worker._perform_reconciliation(
|
||||
user_api=internal_user_api, marketplace_api=internal_marketplace_api
|
||||
)
|
||||
with patch.object(marketplace_api, "create_entitlement") as mock:
|
||||
worker._perform_reconciliation(user_api=user_api, marketplace_api=marketplace_api)
|
||||
|
||||
mock.assert_not_called()
|
||||
|
Reference in New Issue
Block a user