Merge pull request #717 from coreos-inc/v2-phase4

V2 phase4
This commit is contained in:
Jake Moshenko 2015-10-24 15:18:15 -04:00
commit 0d137ab11b
25 changed files with 198 additions and 39 deletions

View file

@ -3,15 +3,16 @@ import logging
import uuid import uuid
import time import time
import toposort import toposort
import resumablehashlib
from random import SystemRandom from random import SystemRandom
from datetime import datetime from datetime import datetime
from peewee import * from peewee import *
from data.read_slave import ReadSlaveModel from data.read_slave import ReadSlaveModel
from data.fields import ResumableSHAField, JSONField
from sqlalchemy.engine.url import make_url from sqlalchemy.engine.url import make_url
from collections import defaultdict from collections import defaultdict
from data.read_slave import ReadSlaveModel
from util.names import urn_generator from util.names import urn_generator
@ -348,7 +349,7 @@ class Repository(BaseModel):
# These models don't need to use transitive deletes, because the referenced objects # These models don't need to use transitive deletes, because the referenced objects
# are cleaned up directly # are cleaned up directly
skip_transitive_deletes = {RepositoryTag, RepositoryBuild, RepositoryBuildTrigger} skip_transitive_deletes = {RepositoryTag, RepositoryBuild, RepositoryBuildTrigger, BlobUpload}
# We need to sort the ops so that models get cleaned in order of their dependencies # We need to sort the ops so that models get cleaned in order of their dependencies
ops = reversed(list(self.dependencies(delete_nullable))) ops = reversed(list(self.dependencies(delete_nullable)))
@ -490,6 +491,7 @@ class ImageStorage(BaseModel):
image_size = BigIntegerField(null=True) image_size = BigIntegerField(null=True)
uncompressed_size = BigIntegerField(null=True) uncompressed_size = BigIntegerField(null=True)
uploading = BooleanField(default=True, null=True) uploading = BooleanField(default=True, null=True)
cas_path = BooleanField(default=True)
class ImageStorageTransformation(BaseModel): class ImageStorageTransformation(BaseModel):
@ -761,6 +763,23 @@ class RepositoryAuthorizedEmail(BaseModel):
) )
class BlobUpload(BaseModel):
repository = ForeignKeyField(Repository, index=True)
uuid = CharField(index=True, unique=True)
byte_count = IntegerField(default=0)
sha_state = ResumableSHAField(null=True, default=resumablehashlib.sha256)
location = ForeignKeyField(ImageStorageLocation)
storage_metadata = JSONField(null=True, default={})
class Meta:
database = db
read_slaves = (read_slave,)
indexes = (
# create a unique index on email and repository
(('repository', 'uuid'), True),
)
class QuayService(BaseModel): class QuayService(BaseModel):
name = CharField(index=True, unique=True) name = CharField(index=True, unique=True)
@ -788,7 +807,6 @@ class QuayRelease(BaseModel):
) )
all_models = [User, Repository, Image, AccessToken, Role, RepositoryPermission, Visibility, all_models = [User, Repository, Image, AccessToken, Role, RepositoryPermission, Visibility,
RepositoryTag, EmailConfirmation, FederatedLogin, LoginService, QueueItem, RepositoryTag, EmailConfirmation, FederatedLogin, LoginService, QueueItem,
RepositoryBuild, Team, TeamMember, TeamRole, LogEntryKind, LogEntry, RepositoryBuild, Team, TeamMember, TeamRole, LogEntryKind, LogEntry,
@ -799,4 +817,4 @@ all_models = [User, Repository, Image, AccessToken, Role, RepositoryPermission,
RepositoryAuthorizedEmail, ImageStorageTransformation, DerivedImageStorage, RepositoryAuthorizedEmail, ImageStorageTransformation, DerivedImageStorage,
TeamMemberInvite, ImageStorageSignature, ImageStorageSignatureKind, TeamMemberInvite, ImageStorageSignature, ImageStorageSignatureKind,
AccessTokenKind, Star, RepositoryActionCount, TagManifest, UserRegion, AccessTokenKind, Star, RepositoryActionCount, TagManifest, UserRegion,
QuayService, QuayRegion, QuayRelease] QuayService, QuayRegion, QuayRelease, BlobUpload]

38
data/fields.py Normal file
View file

@ -0,0 +1,38 @@
import base64
import resumablehashlib
import json
from peewee import TextField
class ResumableSHAField(TextField):
def db_value(self, value):
sha_state = value.state()
# One of the fields is a byte string, let's base64 encode it to make sure
# we can store and fetch it regardless of default collocation.
sha_state[3] = base64.b64encode(sha_state[3])
return json.dumps(sha_state)
def python_value(self, value):
to_resume = resumablehashlib.sha256()
if value is None:
return to_resume
sha_state = json.loads(value)
# We need to base64 decode the data bytestring.
sha_state[3] = base64.b64decode(sha_state[3])
to_resume.set_state(sha_state)
return to_resume
class JSONField(TextField):
def db_value(self, value):
return json.dumps(value)
def python_value(self, value):
if value is None or value == "":
return {}
return json.loads(value)

View file

@ -108,6 +108,16 @@ test_migrate $MYSQL_CONFIG_OVERRIDE
set -e set -e
down_mysql down_mysql
# Test via Postgres.
echo '> Starting Postgres'
up_postgres
echo '> Testing Migration (postgres)'
set +e
test_migrate $PGSQL_CONFIG_OVERRIDE
set -e
down_postgres
# Test via MariaDB. # Test via MariaDB.
echo '> Starting MariaDB' echo '> Starting MariaDB'
up_mariadb up_mariadb
@ -127,13 +137,3 @@ set +e
test_migrate $PERCONA_CONFIG_OVERRIDE test_migrate $PERCONA_CONFIG_OVERRIDE
set -e set -e
down_percona down_percona
# Test via Postgres.
echo '> Starting Postgres'
up_postgres
echo '> Testing Migration (postgres)'
set +e
test_migrate $PGSQL_CONFIG_OVERRIDE
set -e
down_postgres

View file

@ -0,0 +1,31 @@
"""Remove the deprecated imagestorage columns.
Revision ID: 127905a52fdd
Revises: 2e0380215d01
Create Date: 2015-09-17 15:48:56.667823
"""
# revision identifiers, used by Alembic.
revision = '127905a52fdd'
down_revision = '2e0380215d01'
from alembic import op
import sqlalchemy as sa
def upgrade(tables):
### commands auto generated by Alembic - please adjust! ###
op.drop_column('imagestorage', 'comment')
op.drop_column('imagestorage', 'aggregate_size')
op.drop_column('imagestorage', 'command')
op.drop_column('imagestorage', 'created')
### end Alembic commands ###
def downgrade(tables):
### commands auto generated by Alembic - please adjust! ###
op.add_column('imagestorage', sa.Column('created', sa.DateTime(), nullable=True))
op.add_column('imagestorage', sa.Column('command', sa.Text(), nullable=True))
op.add_column('imagestorage', sa.Column('aggregate_size', sa.BigInteger(), nullable=True))
op.add_column('imagestorage', sa.Column('comment', sa.Text(), nullable=True))
### end Alembic commands ###

View file

@ -12,7 +12,6 @@ down_revision = '5ad999136045'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###

View file

@ -12,7 +12,6 @@ down_revision = 'f42b0ea7a4d'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###

View file

@ -12,7 +12,6 @@ down_revision = '313d297811c4'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):

View file

@ -12,7 +12,6 @@ down_revision = '2fb36d4be80d'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
from util.migrate.backfill_user_uuids import backfill_user_uuids from util.migrate.backfill_user_uuids import backfill_user_uuids
def upgrade(tables): def upgrade(tables):

View file

@ -12,7 +12,6 @@ down_revision = '14fe12ade3df'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
from sqlalchemy.exc import InternalError from sqlalchemy.exc import InternalError
def upgrade(tables): def upgrade(tables):
@ -29,7 +28,7 @@ def upgrade(tables):
def downgrade(tables): def downgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###
try: try:
op.add_column('logentry', sa.Column('access_token_id', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) op.add_column('logentry', sa.Column('access_token_id', sa.Integer(), nullable=True))
op.create_foreign_key(u'fk_logentry_access_token_id_accesstoken', 'logentry', 'accesstoken', ['access_token_id'], ['id']) op.create_foreign_key(u'fk_logentry_access_token_id_accesstoken', 'logentry', 'accesstoken', ['access_token_id'], ['id'])
op.create_index('logentry_access_token_id', 'logentry', ['access_token_id'], unique=False) op.create_index('logentry_access_token_id', 'logentry', ['access_token_id'], unique=False)
except InternalError: except InternalError:

View file

@ -12,7 +12,6 @@ down_revision = '5a07499ce53f'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###

View file

@ -12,7 +12,6 @@ down_revision = '707d5191eda'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
op.create_table('star', op.create_table('star',

View file

@ -12,7 +12,6 @@ down_revision = '5b84373e5db'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
op.drop_index('queueitem_queue_name', table_name='queueitem') op.drop_index('queueitem_queue_name', table_name='queueitem')

View file

@ -12,12 +12,11 @@ down_revision = '214350b6a8b1'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###
op.alter_column('repositorybuild', 'resource_key', op.alter_column('repositorybuild', 'resource_key',
existing_type=mysql.VARCHAR(length=255), existing_type=sa.String(length=255),
nullable=True) nullable=True)
### end Alembic commands ### ### end Alembic commands ###
@ -25,6 +24,6 @@ def upgrade(tables):
def downgrade(tables): def downgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###
op.alter_column('repositorybuild', 'resource_key', op.alter_column('repositorybuild', 'resource_key',
existing_type=mysql.VARCHAR(length=255), existing_type=sa.String(length=255),
nullable=False) nullable=False)
### end Alembic commands ### ### end Alembic commands ###

View file

@ -12,7 +12,6 @@ down_revision = '204abf14783d'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###

View file

@ -12,7 +12,6 @@ down_revision = '4b7ef0c7bdb2'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###

View file

@ -0,0 +1,43 @@
"""Backport v2 db changes.
Revision ID: 33bd39ef5ed6
Revises: 127905a52fdd
Create Date: 2015-10-23 12:34:22.776542
"""
# revision identifiers, used by Alembic.
revision = '33bd39ef5ed6'
down_revision = '127905a52fdd'
from alembic import op
import sqlalchemy as sa
def upgrade(tables):
### commands auto generated by Alembic - please adjust! ###
op.create_table('blobupload',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('repository_id', sa.Integer(), nullable=False),
sa.Column('uuid', sa.String(length=255), nullable=False),
sa.Column('byte_count', sa.Integer(), nullable=False),
sa.Column('sha_state', sa.Text(), nullable=True),
sa.Column('location_id', sa.Integer(), nullable=False),
sa.Column('storage_metadata', sa.Text(), nullable=True),
sa.ForeignKeyConstraint(['location_id'], ['imagestoragelocation.id'], name=op.f('fk_blobupload_location_id_imagestoragelocation')),
sa.ForeignKeyConstraint(['repository_id'], ['repository.id'], name=op.f('fk_blobupload_repository_id_repository')),
sa.PrimaryKeyConstraint('id', name=op.f('pk_blobupload'))
)
op.create_index('blobupload_location_id', 'blobupload', ['location_id'], unique=False)
op.create_index('blobupload_repository_id', 'blobupload', ['repository_id'], unique=False)
op.create_index('blobupload_repository_id_uuid', 'blobupload', ['repository_id', 'uuid'], unique=True)
op.create_index('blobupload_uuid', 'blobupload', ['uuid'], unique=True)
op.add_column(u'imagestorage', sa.Column('cas_path', sa.Boolean(), nullable=False, server_default="0"))
### end Alembic commands ###
def downgrade(tables):
### commands auto generated by Alembic - please adjust! ###
op.drop_column(u'imagestorage', 'cas_path')
op.drop_table('blobupload')
### end Alembic commands ###

View file

@ -0,0 +1,47 @@
"""Switch manifest text to a longtext.
Revision ID: 35f538da62
Revises: 33bd39ef5ed6
Create Date: 2015-10-23 15:31:27.353995
"""
# revision identifiers, used by Alembic.
revision = '35f538da62'
down_revision = '33bd39ef5ed6'
from alembic import op
import sqlalchemy as sa
from sqlalchemy.types import TypeDecorator, Text
from sqlalchemy.dialects.mysql import LONGTEXT
import uuid
class EngineLongText(TypeDecorator):
"""Platform-independent LongText type.
Uses MySQL's LONGTEXT type, otherwise uses
Text, because other engines are not as limited
as MySQL.
"""
impl = Text
def load_dialect_impl(self, dialect):
if dialect.name == 'mysql':
return dialect.type_descriptor(LONGTEXT())
else:
return dialect.type_descriptor(Text())
def upgrade(tables):
### commands auto generated by Alembic - please adjust! ###
op.drop_column(u'tagmanifest', 'json_data')
op.add_column(u'tagmanifest', sa.Column('json_data', EngineLongText(), nullable=False))
### end Alembic commands ###
def downgrade(tables):
### commands auto generated by Alembic - please adjust! ###
op.drop_column(u'tagmanifest', 'json_data')
op.add_column(u'tagmanifest', sa.Column('json_data', sa.Text(), nullable=False))
### end Alembic commands ###

View file

@ -12,12 +12,11 @@ down_revision = '31288f79df53'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###
op.alter_column('repositorybuildtrigger', 'auth_token', op.alter_column('repositorybuildtrigger', 'auth_token',
existing_type=mysql.VARCHAR(length=255), existing_type=sa.String(length=255),
nullable=True) nullable=True)
### end Alembic commands ### ### end Alembic commands ###
@ -25,6 +24,6 @@ def upgrade(tables):
def downgrade(tables): def downgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###
op.alter_column('repositorybuildtrigger', 'auth_token', op.alter_column('repositorybuildtrigger', 'auth_token',
existing_type=mysql.VARCHAR(length=255), existing_type=sa.String(length=255),
nullable=False) nullable=False)
### end Alembic commands ### ### end Alembic commands ###

View file

@ -12,7 +12,6 @@ down_revision = '1594a74a74ca'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
op.bulk_insert(tables.externalnotificationmethod, op.bulk_insert(tables.externalnotificationmethod,

View file

@ -12,7 +12,6 @@ down_revision = '43e943c0639f'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###

View file

@ -12,7 +12,6 @@ down_revision = '228d1af6af1c'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###

View file

@ -12,7 +12,6 @@ down_revision = '4ef04c61fcf9'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###
@ -26,7 +25,7 @@ def upgrade(tables):
def downgrade(tables): def downgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###
op.add_column('repositorybuild', sa.Column('queue_item_id', mysql.INTEGER(display_width=11), autoincrement=False, nullable=True)) op.add_column('repositorybuild', sa.Column('queue_item_id', sa.Integer(), autoincrement=False, nullable=True))
op.create_foreign_key(u'fk_repositorybuild_queue_item_id_queueitem', 'repositorybuild', 'queueitem', ['queue_item_id'], ['id']) op.create_foreign_key(u'fk_repositorybuild_queue_item_id_queueitem', 'repositorybuild', 'queueitem', ['queue_item_id'], ['id'])
op.create_index('repositorybuild_queue_item_id', 'repositorybuild', ['queue_item_id'], unique=False) op.create_index('repositorybuild_queue_item_id', 'repositorybuild', ['queue_item_id'], unique=False)
op.drop_index('repositorybuild_queue_id', table_name='repositorybuild') op.drop_index('repositorybuild_queue_id', table_name='repositorybuild')

View file

@ -12,7 +12,6 @@ down_revision = '47670cbeced'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
op.bulk_insert(tables.imagestoragelocation, op.bulk_insert(tables.imagestoragelocation,

View file

@ -12,7 +12,6 @@ down_revision = '4fdb65816b8d'
from alembic import op from alembic import op
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy.dialects import mysql
def upgrade(tables): def upgrade(tables):
### commands auto generated by Alembic - please adjust! ### ### commands auto generated by Alembic - please adjust! ###

View file

@ -124,7 +124,7 @@ def garbage_collect_storage(storage_id_whitelist):
def create_storage(location_name): def create_storage(location_name):
storage = ImageStorage.create() storage = ImageStorage.create(cas_path=False)
location = ImageStorageLocation.get(name=location_name) location = ImageStorageLocation.get(name=location_name)
ImageStoragePlacement.create(location=location, storage=storage) ImageStoragePlacement.create(location=location, storage=storage)
storage.locations = {location_name} storage.locations = {location_name}