Create transient config provider, temp dir logic
Allows us to have a new config provider for each setup, with no overlap of the directories used, and automatic cleanup of those directories.
This commit is contained in:
parent
2d0a599aab
commit
db757edcd2
9 changed files with 70 additions and 37 deletions
|
@ -110,20 +110,6 @@ class SuperUserRegistryStatus(ApiResource):
|
||||||
def get(self):
|
def get(self):
|
||||||
""" Returns the status of the registry. """
|
""" Returns the status of the registry. """
|
||||||
|
|
||||||
# If we have SETUP_COMPLETE, then we're ready to go!
|
|
||||||
if app.config.get('SETUP_COMPLETE', False):
|
|
||||||
return {
|
|
||||||
'provider_id': config_provider.provider_id,
|
|
||||||
'requires_restart': config_provider.requires_restart(app.config),
|
|
||||||
'status': 'ready'
|
|
||||||
}
|
|
||||||
|
|
||||||
# If there is no conf/stack volume, then report that status.
|
|
||||||
if not config_provider.volume_exists():
|
|
||||||
return {
|
|
||||||
'status': 'missing-config-dir'
|
|
||||||
}
|
|
||||||
|
|
||||||
# If there is no config file, we need to setup the database.
|
# If there is no config file, we need to setup the database.
|
||||||
if not config_provider.config_exists():
|
if not config_provider.config_exists():
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import tempfile
|
||||||
import tarfile
|
import tarfile
|
||||||
|
|
||||||
from flask import request, make_response, send_file
|
from flask import request, make_response, send_file
|
||||||
|
@ -9,6 +10,19 @@ from util.config.validator import EXTRA_CA_DIRECTORY
|
||||||
from config_app.c_app import app, config_provider
|
from config_app.c_app import app, config_provider
|
||||||
from config_app.config_endpoints.api import resource, ApiResource, nickname
|
from config_app.config_endpoints.api import resource, ApiResource, nickname
|
||||||
|
|
||||||
|
@resource('/v1/configapp/initialization')
|
||||||
|
class ConfigInitialization(ApiResource):
|
||||||
|
"""
|
||||||
|
Resource for dealing with any initialization logic for the config app
|
||||||
|
"""
|
||||||
|
|
||||||
|
@nickname('scStartNewConfig')
|
||||||
|
def get(self):
|
||||||
|
config_provider.new_config_dir()
|
||||||
|
|
||||||
|
return make_response('OK')
|
||||||
|
|
||||||
|
|
||||||
@resource('/v1/configapp/tarconfig')
|
@resource('/v1/configapp/tarconfig')
|
||||||
class TarConfigLoader(ApiResource):
|
class TarConfigLoader(ApiResource):
|
||||||
"""
|
"""
|
||||||
|
@ -18,7 +32,7 @@ class TarConfigLoader(ApiResource):
|
||||||
|
|
||||||
@nickname('scGetConfigTarball')
|
@nickname('scGetConfigTarball')
|
||||||
def get(self):
|
def get(self):
|
||||||
config_path = config_provider.config_volume
|
config_path = config_provider.get_config_dir_path()
|
||||||
|
|
||||||
# remove the initial trailing / from the prefix path, and add the last dir one
|
# remove the initial trailing / from the prefix path, and add the last dir one
|
||||||
tar_dir_prefix = config_path[1:] + '/'
|
tar_dir_prefix = config_path[1:] + '/'
|
||||||
|
@ -33,28 +47,28 @@ class TarConfigLoader(ApiResource):
|
||||||
|
|
||||||
return tarinfo
|
return tarinfo
|
||||||
|
|
||||||
# Remove the tar if it already exists so we don't write on top of existing tarball
|
temp = tempfile.NamedTemporaryFile()
|
||||||
if os.path.isfile('quay-config.tar.gz'):
|
|
||||||
os.remove('quay-config.tar.gz')
|
|
||||||
|
|
||||||
tar = tarfile.open('quay-config.tar.gz', mode="w|gz")
|
tar = tarfile.open(temp.name, mode="w|gz")
|
||||||
|
|
||||||
for name in os.listdir(config_path):
|
for name in os.listdir(config_path):
|
||||||
tar.add(os.path.join(config_path, name), filter=tarinfo_filter)
|
tar.add(os.path.join(config_path, name), filter=tarinfo_filter)
|
||||||
|
|
||||||
tar.close()
|
tar.close()
|
||||||
|
|
||||||
return send_file('quay-config.tar.gz', mimetype='application/gzip')
|
return send_file(temp.name, mimetype='application/gzip')
|
||||||
|
|
||||||
@nickname('scUploadTarballConfig')
|
@nickname('scUploadTarballConfig')
|
||||||
def put(self):
|
def put(self):
|
||||||
""" Loads tarball config into the config provider """
|
""" Loads tarball config into the config provider """
|
||||||
|
# Generate a new empty dir to load the config into
|
||||||
|
config_provider.new_config_dir()
|
||||||
|
|
||||||
input_stream = request.stream
|
input_stream = request.stream
|
||||||
with tarfile.open(mode="r|gz", fileobj=input_stream) as tar_stream:
|
with tarfile.open(mode="r|gz", fileobj=input_stream) as tar_stream:
|
||||||
# TODO: find a way to remove the contents of the directory on shutdown?
|
tar_stream.extractall(config_provider.get_config_dir_path())
|
||||||
tar_stream.extractall(config_provider.config_volume)
|
|
||||||
|
|
||||||
# now try to connect to the db provided in their config
|
# now try to connect to the db provided in their config to validate it works
|
||||||
combined = dict(**app.config)
|
combined = dict(**app.config)
|
||||||
combined.update(config_provider.get_config())
|
combined.update(config_provider.get_config())
|
||||||
|
|
||||||
|
|
30
config_app/config_util/config/TransientDirectoryProvider.py
Normal file
30
config_app/config_util/config/TransientDirectoryProvider.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import os
|
||||||
|
from backports.tempfile import TemporaryDirectory
|
||||||
|
|
||||||
|
from config_app.config_util.config.fileprovider import FileConfigProvider
|
||||||
|
|
||||||
|
class TransientDirectoryProvider(FileConfigProvider):
|
||||||
|
""" Implementation of the config provider that reads and writes the data
|
||||||
|
from/to the file system, only using temporary directories,
|
||||||
|
deleting old dirs and creating new ones as requested.
|
||||||
|
"""
|
||||||
|
def __init__(self, config_volume, yaml_filename, py_filename):
|
||||||
|
# Create a temp directory that will be cleaned up when we change the config path
|
||||||
|
# This should ensure we have no "pollution" of different configs:
|
||||||
|
# no uploaded config should ever affect subsequent config modifications/creations
|
||||||
|
temp_dir = TemporaryDirectory()
|
||||||
|
self.temp_dir = temp_dir
|
||||||
|
super(TransientDirectoryProvider, self).__init__(temp_dir.name, yaml_filename, py_filename)
|
||||||
|
|
||||||
|
def new_config_dir(self):
|
||||||
|
"""
|
||||||
|
Update the path with a new temporary directory, deleting the old one in the process
|
||||||
|
"""
|
||||||
|
temp_dir = TemporaryDirectory()
|
||||||
|
|
||||||
|
self.config_volume = temp_dir.name
|
||||||
|
self.temp_dir = temp_dir
|
||||||
|
self.yaml_path = os.path.join(temp_dir.name, self.yaml_filename)
|
||||||
|
|
||||||
|
def get_config_dir_path(self):
|
||||||
|
return self.config_volume
|
|
@ -1,5 +1,6 @@
|
||||||
from config_app.config_util.config.fileprovider import FileConfigProvider
|
from config_app.config_util.config.fileprovider import FileConfigProvider
|
||||||
from config_app.config_util.config.testprovider import TestConfigProvider
|
from config_app.config_util.config.testprovider import TestConfigProvider
|
||||||
|
from config_app.config_util.config.TransientDirectoryProvider import TransientDirectoryProvider
|
||||||
|
|
||||||
|
|
||||||
def get_config_provider(config_volume, yaml_filename, py_filename, testing=False):
|
def get_config_provider(config_volume, yaml_filename, py_filename, testing=False):
|
||||||
|
@ -8,4 +9,4 @@ def get_config_provider(config_volume, yaml_filename, py_filename, testing=False
|
||||||
if testing:
|
if testing:
|
||||||
return TestConfigProvider()
|
return TestConfigProvider()
|
||||||
|
|
||||||
return FileConfigProvider(config_volume, yaml_filename, py_filename)
|
return TransientDirectoryProvider(config_volume, yaml_filename, py_filename)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component } from 'ng-metadata/core';
|
import { Component, Inject } from 'ng-metadata/core';
|
||||||
const templateUrl = require('./config-setup-app.component.html');
|
const templateUrl = require('./config-setup-app.component.html');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,12 +17,18 @@ export class ConfigSetupAppComponent {
|
||||||
|
|
||||||
private loadedConfig = false;
|
private loadedConfig = false;
|
||||||
|
|
||||||
constructor() {
|
constructor(@Inject('ApiService') private apiService) {
|
||||||
this.state = 'choice';
|
this.state = 'choice';
|
||||||
}
|
}
|
||||||
|
|
||||||
private chooseSetup(): void {
|
private chooseSetup(): void {
|
||||||
this.state = 'setup';
|
this.apiService.scStartNewConfig()
|
||||||
|
.then(() => {
|
||||||
|
this.state = 'setup';
|
||||||
|
})
|
||||||
|
.catch(this.apiService.errorDisplay(
|
||||||
|
'Could not initialize new setup. Please report this error'
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private chooseLoad(): void {
|
private chooseLoad(): void {
|
||||||
|
|
|
@ -27,7 +27,7 @@ export class DownloadTarballModalComponent {
|
||||||
// We need to set the response type to 'blob', to ensure it's never encoded as a string
|
// We need to set the response type to 'blob', to ensure it's never encoded as a string
|
||||||
// (string encoded binary data can be difficult to transform with js)
|
// (string encoded binary data can be difficult to transform with js)
|
||||||
// and to make it easier to save (FileSaver expects a blob)
|
// and to make it easier to save (FileSaver expects a blob)
|
||||||
this.ApiService.scGetConfigTarball(null, null, null, null, true).then(function(resp) {
|
this.ApiService.scGetConfigTarball(null, null, null, null, 'blob').then(function(resp) {
|
||||||
FileSaver.saveAs(resp, 'quay-config.tar.gz');
|
FileSaver.saveAs(resp, 'quay-config.tar.gz');
|
||||||
}, errorDisplay);
|
}, errorDisplay);
|
||||||
}
|
}
|
||||||
|
|
|
@ -417,10 +417,6 @@ angular.module("quay-config")
|
||||||
$scope.saveConfiguration = function() {
|
$scope.saveConfiguration = function() {
|
||||||
$scope.savingConfiguration = true;
|
$scope.savingConfiguration = true;
|
||||||
|
|
||||||
// Make sure to note that fully verified setup is completed. We use this as a signal
|
|
||||||
// in the setup tool.
|
|
||||||
// $scope.config['SETUP_COMPLETE'] = true;
|
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
'config': $scope.config,
|
'config': $scope.config,
|
||||||
'hostname': window.location.host,
|
'hostname': window.location.host,
|
||||||
|
@ -441,7 +437,6 @@ angular.module("quay-config")
|
||||||
|
|
||||||
$('#validateAndSaveModal').modal('hide');
|
$('#validateAndSaveModal').modal('hide');
|
||||||
|
|
||||||
// $scope.configurationSaved({'config': $scope.config});
|
|
||||||
$scope.setupCompleted();
|
$scope.setupCompleted();
|
||||||
}, errorDisplay);
|
}, errorDisplay);
|
||||||
};
|
};
|
||||||
|
|
|
@ -212,17 +212,17 @@ angular.module('quay-config').factory('ApiService', ['Restangular', '$q', 'UtilS
|
||||||
var urlPath = path['x-path'];
|
var urlPath = path['x-path'];
|
||||||
|
|
||||||
// Add the operation itself.
|
// Add the operation itself.
|
||||||
apiService[operationName] = function(opt_options, opt_parameters, opt_background, opt_forceget, opt_blobresp) {
|
apiService[operationName] = function(opt_options, opt_parameters, opt_background, opt_forceget, opt_responseType) {
|
||||||
var one = Restangular.one(buildUrl(urlPath, opt_parameters));
|
var one = Restangular.one(buildUrl(urlPath, opt_parameters));
|
||||||
|
|
||||||
if (opt_background || opt_blobresp) {
|
if (opt_background || opt_responseType) {
|
||||||
let httpConfig = {};
|
let httpConfig = {};
|
||||||
|
|
||||||
if (opt_background) {
|
if (opt_background) {
|
||||||
httpConfig['ignoreLoadingBar'] = true;
|
httpConfig['ignoreLoadingBar'] = true;
|
||||||
}
|
}
|
||||||
if (opt_blobresp) {
|
if (opt_responseType) {
|
||||||
httpConfig['responseType'] = 'blob';
|
httpConfig['responseType'] = opt_responseType;
|
||||||
}
|
}
|
||||||
|
|
||||||
one.withHttpConfig(httpConfig);
|
one.withHttpConfig(httpConfig);
|
||||||
|
|
|
@ -20,6 +20,7 @@ azure-storage-blob==1.1.0
|
||||||
azure-storage-common==1.1.0
|
azure-storage-common==1.1.0
|
||||||
azure-storage-nspkg==3.0.0
|
azure-storage-nspkg==3.0.0
|
||||||
Babel==2.5.3
|
Babel==2.5.3
|
||||||
|
backports.tempfile==1.0
|
||||||
beautifulsoup4==4.6.0
|
beautifulsoup4==4.6.0
|
||||||
bencode==1.0
|
bencode==1.0
|
||||||
bintrees==2.0.7
|
bintrees==2.0.7
|
||||||
|
|
Reference in a new issue