예제 #1
0
def revert(
    structures,
    state,
    conn_data,
    features=None,
    dry_run: bool = False,
    simulation: bool = False,
    version: int = None,
    force: bool = False,
):
    feature_version = None
    if version is not None:
        if len(features) != 1:
            raise ValueError("cannot specify version with multiple features")
        feature_version = features[0]
        raise NotImplementedError("Cannot revert to a version yet")
    if features is None:
        features = state.keys()
    orig_features = features
    features = calc_deps_list(structures, features, False)
    for feature in features:
        if feature not in state and not (force and feature in orig_features):
            continue
        struct = structures[feature]
        if feature == feature_version:
            pass  # TODO: implement
        print(struct.revert)
        if not dry_run:
            if not simulation:
                psql_command(None, struct.revert, **conn_data)
            declare_revert(state, feature, **conn_data)
예제 #2
0
def deploy(
    features,
    state,
    structures,
    conn_data,
    dry_run=False,
    simulation=False,
    reapply=False,
    except_target=False,
):
    if not features:
        features = structures.keys()
    base_features = features
    features = calc_deps_list(structures, features)
    reversions = [
        feature for feature in features
        if needs_revert(state, structures, feature)
    ]
    if reversions and (dry_run or not simulation):
        reversions = calc_deps_list(structures, reversions, False)
        for feature in reversions:
            struct = structures[feature]
            print(struct.revert)
            if not dry_run:
                psql_command(None, struct.revert, **conn_data)
            del state[feature]
            features.append(feature)
        features = calc_deps_list(structures, features)
    for feature in features:
        if except_target and feature in base_features:
            continue
        reapply_ = (reapply and feature in base_features
                    and structures[feature].head.idempotent)
        for feature_, path, prev_version, sha1sum in calc_apply_target(
                state, structures, feature, reapply_):
            print(path)
            if not dry_run:
                if not simulation:
                    psql_command(None, path, **conn_data)
                declare_deploy(state, feature, prev_version, sha1sum,
                               **conn_data)
예제 #3
0
def create_database(data, conn_data, dropdb=False):
    database = data["database"]
    member = database + "__member"
    if not test_user_exists(member, **conn_data):
        psql_command(f"CREATE ROLE {member}", **conn_data)
    for usert in ("owner", "client"):
        user = data[usert]
        has_pass = data.get(usert + "_password", None)
        password = has_pass or token_urlsafe(16)
        user_conn = conn_data.copy()
        user_conn.update(dict(user=user, password=password))
        if has_pass:
            # check connection
            try:
                test_user_exists(user, **user_conn)
                # assume permissions are ok
                continue
            except AssertionError:
                pass
        else:
            data[usert + "_password"] = password
        extra_perms = "CREATEROLE" if usert == "owner" else "NOINHERIT"
        if test_user_exists(user, **conn_data):
            psql_command(
                f"ALTER ROLE {user} WITH LOGIN {extra_perms} ENCRYPTED PASSWORD '{password}'",
                **conn_data,
            )
        else:
            psql_command(
                f"CREATE USER {user} WITH LOGIN {extra_perms} ENCRYPTED PASSWORD '{password}'",
                **conn_data,
            )
    auth_secret = data.get("auth_secret", None) or token_urlsafe(32)
    data["auth_secret"] = auth_secret
    owner = data["owner"]
    exists = test_db_exists(database, **conn_data)
    if exists and dropdb:
        extra_roles = psql_command(
            f"select string_agg(rolname, ', ') from pg_catalog.pg_roles where rolname like '{database}\_\__\_%'",
            **conn_data,
        ).strip()
        psql_command(f"DROP DATABASE {database}", **conn_data)
        if extra_roles:
            psql_command(f"DROP ROLE {extra_roles}", **conn_data)
        exists = False
    if not exists:
        psql_command(
            f"CREATE DATABASE {database} WITH OWNER {owner} ENCODING UTF8",
            **conn_data)
    else:
        if (psql_command(
                f"SELECT pg_catalog.pg_get_userbyid(datdba) FROM pg_catalog.pg_database WHERE datname='{database}'",
                **conn_data,
        ).strip() != owner):
            psql_command(f"ALTER {database} SET OWNER TO {owner}", **conn_data)

    # TODO: this may already be the case
    psql_command(f"ALTER GROUP {owner} ADD USER {data['client']}", **conn_data)
    psql_command(f"ALTER GROUP {member} ADD USER {data['client']}",
                 **conn_data)
    psql_command(
        f"ALTER DATABASE {database} SET \"app.jwt_secret\" TO '{auth_secret}'",
        **conn_data,
    )
    conn_data = conn_data.copy()
    conn_data["db"] = database
    psql_command(f"ALTER SCHEMA public OWNER TO {owner}", **conn_data)
    return data
예제 #4
0
def test_user_exists(role, **kwargs):
    return (psql_command(
        f"select rolname from pg_catalog.pg_roles where rolname='{role}'",
        **kwargs).strip() == role)
예제 #5
0
def test_db_exists(test, **kwargs):
    return (psql_command(
        f"select datname from pg_catalog.pg_database where datname='{test}'",
        **kwargs,
    ).strip() == test)
예제 #6
0
        "--remove",
        default=[],
        action="append",
        help="the permissions to remove (can be repeated)",
    )
    args = argp.parse_args()
    permissions = args.permissions or ["superadmin"]
    db = args.database
    user = args.user
    conn_data = dict(
        user=ini_file[db]["owner"],
        password=ini_file[db]["owner_password"],
        port=ini_file["postgres"].get("port", 5432),
        db=ini_file[db]["database"],
    )
    selector = f" WHERE email='{user}'" if "@" in user else f" WHERE handle='{user}'"
    existing = psql_command("SELECT permissions FROM members" + selector,
                            **conn_data)
    existing = set(
        existing.strip().strip('"').strip("{").strip("}").split(","))
    existing.discard("")
    permissions = (existing | set(permissions)) - set(args.remove)
    print("existing permissions:", existing)
    if existing != permissions:
        permissionsS = ",".join([f"'{p}'" for p in permissions])
        psql_command(
            f"UPDATE members SET permissions=ARRAY[{permissionsS}]::permission[] {selector}",
            **conn_data,
        )
        print("new permissions: ", permissions)
예제 #7
0
def declare_revert(state, feature, **conn_data):
    psql_command(f"DELETE FROM deploy_state WHERE feature='{feature}'",
                 **conn_data)
    state.pop(feature, None)
예제 #8
0
def declare_deploy(state, feature, prev_version, sha1sum, **conn_data):
    psql_command(
        f"""INSERT INTO deploy_state (feature, prev_version, sha1sum) VALUES ('{feature}', {prev_version}, '{sha1sum}') ON CONFLICT (feature) DO UPDATE SET prev_version=EXCLUDED.prev_version, sha1sum=EXCLUDED.sha1sum""",
        **conn_data,
    )
    state[feature] = (prev_version, sha1sum)
예제 #9
0
def init_db(conn_data):
    psql_command(
        """CREATE TABLE IF NOT EXISTS deploy_state(feature varchar primary key, prev_version smallint, sha1sum varchar(40))""",
        **conn_data,
    )
예제 #10
0
def db_state(conn_data):
    results = psql_command("select * from deploy_state", **conn_data)
    results = [x.split(",") for x in results.split("\n") if x]
    return {row[0]: (int(row[1]), row[2]) for row in results}