From b631b0f27133f9cfc181f6e24e32e51471f73ac4 Mon Sep 17 00:00:00 2001 From: Sam Chow Date: Thu, 14 Jun 2018 17:18:58 -0400 Subject: [PATCH 1/9] Add missing files changed directive to upload --- config_app/js/components/files-changed.js | 18 +++++++++++++++ .../load-config/load-config.component.ts | 23 ++++++++++++++++++- .../components/load-config/load-config.html | 6 ++++- 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 config_app/js/components/files-changed.js diff --git a/config_app/js/components/files-changed.js b/config_app/js/components/files-changed.js new file mode 100644 index 000000000..997dd9144 --- /dev/null +++ b/config_app/js/components/files-changed.js @@ -0,0 +1,18 @@ +/** + * Raises the 'filesChanged' event on the scope if a file on the marked exists. + */ +angular.module('quay-config').directive("filesChanged", [function () { + return { + restrict: 'A', + scope: { + 'filesChanged': "&" + }, + link: function (scope, element, attributes) { + element.bind("change", function (changeEvent) { + scope.$apply(function() { + scope.filesChanged({'files': changeEvent.target.files}); + }); + }); + } + } +}]); diff --git a/config_app/js/components/load-config/load-config.component.ts b/config_app/js/components/load-config/load-config.component.ts index f8a3ba47a..933a5469e 100644 --- a/config_app/js/components/load-config/load-config.component.ts +++ b/config_app/js/components/load-config/load-config.component.ts @@ -6,6 +6,27 @@ const templateUrl = require('./load-config.html'); templateUrl, }) export class LoadConfigComponent { - constructor() { + private resetUpload: number = 0; + + private handleTarballSelected(files, callback) { + console.log('hi world') + /* + $scope.certsUploading = true; + $upload.upload({ + url: '/api/v1/superuser/customcerts/' + files[0].name, + method: 'POST', + data: {'_csrf_token': window.__token}, + file: files[0] + }).success(function() { + callback(true); + $scope.resetUpload++; + loadCertificates(); + }).error(function(r) { + bootbox.alert('Could not upload certificate') + callback(false); + $scope.resetUpload++; + loadCertificates(); + }); + */ } } \ No newline at end of file diff --git a/config_app/js/components/load-config/load-config.html b/config_app/js/components/load-config/load-config.html index 60f78ff2b..3a5d0606e 100644 --- a/config_app/js/components/load-config/load-config.html +++ b/config_app/js/components/load-config/load-config.html @@ -10,7 +10,11 @@
Please upload a tarball -
+
From c7513199df9e7e72716f91ee88f94fe4570dee3a Mon Sep 17 00:00:00 2001 From: Sam Chow Date: Fri, 15 Jun 2018 15:42:10 -0400 Subject: [PATCH 2/9] link config comp to upload files to endpoint --- config_app/config_endpoints/api/__init__.py | 1 + .../config_endpoints/api/tar_config_loader.py | 47 +++++++++++++++ config_app/js/components/file-upload-box.js | 15 ++--- .../load-config/load-config.component.ts | 57 ++++++++++++------- .../js/components/load-config/load-config.css | 4 ++ .../components/load-config/load-config.html | 12 +++- 6 files changed, 105 insertions(+), 31 deletions(-) create mode 100644 config_app/config_endpoints/api/tar_config_loader.py create mode 100644 config_app/js/components/load-config/load-config.css diff --git a/config_app/config_endpoints/api/__init__.py b/config_app/config_endpoints/api/__init__.py index d539639eb..0adf080be 100644 --- a/config_app/config_endpoints/api/__init__.py +++ b/config_app/config_endpoints/api/__init__.py @@ -137,4 +137,5 @@ import config_endpoints.api.discovery import config_endpoints.api.suconfig import config_endpoints.api.superuser import config_endpoints.api.user +import config_endpoints.api.tar_config_loader diff --git a/config_app/config_endpoints/api/tar_config_loader.py b/config_app/config_endpoints/api/tar_config_loader.py new file mode 100644 index 000000000..1891f1f09 --- /dev/null +++ b/config_app/config_endpoints/api/tar_config_loader.py @@ -0,0 +1,47 @@ +import logging +from flask import abort, request + +from config_app.config_endpoints.api import resource, ApiResource, nickname, validate_json_request + +logger = logging.getLogger(__name__) + +@resource('/v1/configapp/tarconfig') +class TarConfigLoader(ApiResource): + """ Resource for validating a block of configuration against an external service. """ + schemas = { + 'ValidateUploadConfig': { + 'type': 'object', + 'description': '', + 'required': [ + 'config' + ], + 'properties': { + 'config': { + 'type': 'object' + }, + }, + }, + } + + @nickname('uploadTarballConfig') + # @validate_json_request('ValidateUploadConfig') + def post(self): + """ Extracts and validates all configuration from """ + logger.debug('hello world!') + logger.debug(request) + logger.debug('hello world!') + logger.debug(request.files) + # logger.debug('hello world!') + # logger.debug(request.body) + # logger.debug(request.data) + logger.debug('hello world!') + logger.debug(request.files) + logger.debug('hello world!') + logger.debug(request.mimetype) + logger.debug('hello world!') + logger.debug(request.get_data()) + logger.debug('hello world!') + logger.debug(request.get_json()) + logger.debug('hello world!') + + abort(418) diff --git a/config_app/js/components/file-upload-box.js b/config_app/js/components/file-upload-box.js index 2a48b06d0..6fee20a14 100644 --- a/config_app/js/components/file-upload-box.js +++ b/config_app/js/components/file-upload-box.js @@ -34,7 +34,7 @@ angular.module('quay-config').directive('fileUploadBox', function () { var conductUpload = function(file, url, fileId, mimeType, progressCb, doneCb) { var request = new XMLHttpRequest(); - request.open('PUT', url, true); + request.open('POST', url, true); request.setRequestHeader('Content-Type', mimeType); request.onprogress = function(e) { $scope.$apply(function() { @@ -103,12 +103,13 @@ angular.module('quay-config').directive('fileUploadBox', function () { $scope.currentlyUploadingFile = currentFile; $scope.uploadProgress = 0; - ApiService.getFiledropUrl(data).then(function(resp) { - // Perform the upload. - conductUpload(currentFile, resp.url, resp.file_id, mimeType, progressCb, doneCb); - }, function() { - callback(false, 'Could not retrieve upload URL'); - }); + // ApiService.getFiledropUrl(data).then(function(resp) { + // // Perform the upload. + // conductUpload(currentFile, resp.url, resp.file_id, mimeType, progressCb, doneCb); + // }, function() { + // callback(false, 'Could not retrieve upload URL'); + // }); + conductUpload(currentFile, '/api/v1/configapp/tarconfig','fileIdIsUnused', mimeType, progressCb, doneCb); }; // Start the uploading. diff --git a/config_app/js/components/load-config/load-config.component.ts b/config_app/js/components/load-config/load-config.component.ts index 933a5469e..2279420c4 100644 --- a/config_app/js/components/load-config/load-config.component.ts +++ b/config_app/js/components/load-config/load-config.component.ts @@ -1,32 +1,47 @@ import { Input, Component, Inject } from 'ng-metadata/core'; const templateUrl = require('./load-config.html'); +const styleUrl = require('./load-config.css'); + +declare let bootbox: any; +declare let window: any; @Component({ selector: 'load-config', templateUrl, + styleUrls: [ styleUrl ], }) export class LoadConfigComponent { - private resetUpload: number = 0; + private isReady: boolean = false; + private uploadFunc: Function; - private handleTarballSelected(files, callback) { - console.log('hi world') - /* - $scope.certsUploading = true; - $upload.upload({ - url: '/api/v1/superuser/customcerts/' + files[0].name, - method: 'POST', - data: {'_csrf_token': window.__token}, - file: files[0] - }).success(function() { - callback(true); - $scope.resetUpload++; - loadCertificates(); - }).error(function(r) { - bootbox.alert('Could not upload certificate') - callback(false); - $scope.resetUpload++; - loadCertificates(); - }); - */ + private constructor(@Inject('ApiService') private apiService: any) { } + + private handleTarballSelected(files: File[], callback: Function) { + this.isReady = true; + callback(true) + } + + private handleTarballCleared() { + this.isReady = false; + } + + private uploadTarball() { + this.uploadFunc(resp => { + console.log('hi') + console.log(resp) + }); + } + + /** + * When files are validated, this is called by the child to give us + * the callback function to upload + * @param files: files to upload + * @param uploadFiles: function to call to upload files + */ + private filesValidated(files, uploadFiles) { + this.uploadFunc = uploadFiles; + } + + } \ No newline at end of file diff --git a/config_app/js/components/load-config/load-config.css b/config_app/js/components/load-config/load-config.css new file mode 100644 index 000000000..a557fd3a9 --- /dev/null +++ b/config_app/js/components/load-config/load-config.css @@ -0,0 +1,4 @@ + + +.load-config__body strong { +} \ No newline at end of file diff --git a/config_app/js/components/load-config/load-config.html b/config_app/js/components/load-config/load-config.html index 3a5d0606e..142c3f44f 100644 --- a/config_app/js/components/load-config/load-config.html +++ b/config_app/js/components/load-config/load-config.html @@ -8,14 +8,20 @@ -
- Please upload a tarball + +
From 8aa18a29a87574e1eb62f66f7d13bb7613a23c2a Mon Sep 17 00:00:00 2001 From: Sam Chow Date: Mon, 18 Jun 2018 11:01:14 -0400 Subject: [PATCH 3/9] Add writing config to file, modal for validation --- .../config_endpoints/api/tar_config_loader.py | 46 +++++-------------- .../load-config/load-config.component.ts | 19 ++++---- .../components/load-config/load-config.html | 21 ++++++++- .../config-certificates-field.html | 1 + 4 files changed, 43 insertions(+), 44 deletions(-) diff --git a/config_app/config_endpoints/api/tar_config_loader.py b/config_app/config_endpoints/api/tar_config_loader.py index 1891f1f09..d235dad3b 100644 --- a/config_app/config_endpoints/api/tar_config_loader.py +++ b/config_app/config_endpoints/api/tar_config_loader.py @@ -1,6 +1,8 @@ import logging -from flask import abort, request +from flask import request, make_response + +from config_app.c_app import config_provider from config_app.config_endpoints.api import resource, ApiResource, nickname, validate_json_request logger = logging.getLogger(__name__) @@ -8,40 +10,16 @@ logger = logging.getLogger(__name__) @resource('/v1/configapp/tarconfig') class TarConfigLoader(ApiResource): """ Resource for validating a block of configuration against an external service. """ - schemas = { - 'ValidateUploadConfig': { - 'type': 'object', - 'description': '', - 'required': [ - 'config' - ], - 'properties': { - 'config': { - 'type': 'object' - }, - }, - }, - } @nickname('uploadTarballConfig') - # @validate_json_request('ValidateUploadConfig') def post(self): - """ Extracts and validates all configuration from """ - logger.debug('hello world!') - logger.debug(request) - logger.debug('hello world!') - logger.debug(request.files) - # logger.debug('hello world!') - # logger.debug(request.body) - # logger.debug(request.data) - logger.debug('hello world!') - logger.debug(request.files) - logger.debug('hello world!') - logger.debug(request.mimetype) - logger.debug('hello world!') - logger.debug(request.get_data()) - logger.debug('hello world!') - logger.debug(request.get_json()) - logger.debug('hello world!') + """ Loads tarball config into the config provider """ + input_stream = request.stream - abort(418) + # since we're working with a tar file, shouldn't be larger than ~20KB, so just read the whole thing into mem + buf = input_stream.read(-1) + + # TODO(sam): refactor config provider to accept a stream write to avoid loading into memory + config_provider.write_volume_file('test_tar.tar.gz', buf) + + return make_response('OK') diff --git a/config_app/js/components/load-config/load-config.component.ts b/config_app/js/components/load-config/load-config.component.ts index 2279420c4..892958e03 100644 --- a/config_app/js/components/load-config/load-config.component.ts +++ b/config_app/js/components/load-config/load-config.component.ts @@ -1,10 +1,7 @@ -import { Input, Component, Inject } from 'ng-metadata/core'; +import { Component, Inject } from 'ng-metadata/core'; const templateUrl = require('./load-config.html'); const styleUrl = require('./load-config.css'); -declare let bootbox: any; -declare let window: any; - @Component({ selector: 'load-config', templateUrl, @@ -13,6 +10,7 @@ declare let window: any; export class LoadConfigComponent { private isReady: boolean = false; private uploadFunc: Function; + private state: 'load' | 'validate' = 'load'; private constructor(@Inject('ApiService') private apiService: any) { } @@ -27,9 +25,14 @@ export class LoadConfigComponent { } private uploadTarball() { - this.uploadFunc(resp => { - console.log('hi') - console.log(resp) + this.uploadFunc(success => { + if (success) { + this.state = 'validate'; + } + else { + this.apiService.errorDisplay('Could not upload configuration. Please reload the page and try again.\n' + + 'If this problem persists, please contact support')(); + } }); } @@ -42,6 +45,4 @@ export class LoadConfigComponent { private filesValidated(files, uploadFiles) { this.uploadFunc = uploadFiles; } - - } \ No newline at end of file diff --git a/config_app/js/components/load-config/load-config.html b/config_app/js/components/load-config/load-config.html index 142c3f44f..3dd982f0d 100644 --- a/config_app/js/components/load-config/load-config.html +++ b/config_app/js/components/load-config/load-config.html @@ -1,4 +1,4 @@ -
+
+ +
+ +
diff --git a/config_app/js/config-field-templates/config-certificates-field.html b/config_app/js/config-field-templates/config-certificates-field.html index f20e4c459..1cbe5d569 100644 --- a/config_app/js/config-field-templates/config-certificates-field.html +++ b/config_app/js/config-field-templates/config-certificates-field.html @@ -19,6 +19,7 @@ Upload certificates: +
Date: Mon, 18 Jun 2018 16:01:30 -0400 Subject: [PATCH 4/9] Read tarball into in-memory config provider --- .../config_endpoints/api/tar_config_loader.py | 7 +- config_app/config_util/config/__init__.py | 4 ++ .../config_util/config/inmemoryprovider.py | 66 +++++++++++++++++++ .../load-config/load-config.component.ts | 11 ++-- .../components/load-config/load-config.html | 4 +- 5 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 config_app/config_util/config/inmemoryprovider.py diff --git a/config_app/config_endpoints/api/tar_config_loader.py b/config_app/config_endpoints/api/tar_config_loader.py index d235dad3b..d7e41d446 100644 --- a/config_app/config_endpoints/api/tar_config_loader.py +++ b/config_app/config_endpoints/api/tar_config_loader.py @@ -1,4 +1,6 @@ import logging +import tarfile +import cStringIO from flask import request, make_response @@ -17,9 +19,10 @@ class TarConfigLoader(ApiResource): input_stream = request.stream # since we're working with a tar file, shouldn't be larger than ~20KB, so just read the whole thing into mem - buf = input_stream.read(-1) + buf = input_stream.read() + config = tarfile.open(mode="r:gz", fileobj=cStringIO.StringIO(buf)) # TODO(sam): refactor config provider to accept a stream write to avoid loading into memory - config_provider.write_volume_file('test_tar.tar.gz', buf) + config_provider.load_from_tarball(config) return make_response('OK') diff --git a/config_app/config_util/config/__init__.py b/config_app/config_util/config/__init__.py index d32c159d8..018d5cc8c 100644 --- a/config_app/config_util/config/__init__.py +++ b/config_app/config_util/config/__init__.py @@ -1,9 +1,13 @@ from config_app.config_util.config.fileprovider import FileConfigProvider from config_app.config_util.config.testprovider import TestConfigProvider +from config_app.config_util.config.inmemoryprovider import InMemoryProvider def get_config_provider(config_volume, yaml_filename, py_filename, testing=False): """ Loads and returns the config provider for the current environment. """ + if True: + return InMemoryProvider() + if testing: return TestConfigProvider() diff --git a/config_app/config_util/config/inmemoryprovider.py b/config_app/config_util/config/inmemoryprovider.py new file mode 100644 index 000000000..750cf1aed --- /dev/null +++ b/config_app/config_util/config/inmemoryprovider.py @@ -0,0 +1,66 @@ +import logging +import yaml + +from config_app.config_util.config.baseprovider import BaseProvider + + +logger = logging.getLogger(__name__) + +CONFIG_FILENAME = 'config.yaml' + +class InMemoryProvider(BaseProvider): + def __init__(self): + self.files = {} + self.config = {} + + @property + def provider_id(self): + return 'memory' + + def update_app_config(self, app_config): + self.config = app_config + + def get_config(self): + return self.config + + def save_config(self, config_object): + raise Exception('Not implemented yet') + + def config_exists(self): + raise Exception('Not implemented yet') + + def volume_exists(self): + raise Exception('Not implemented yet') + + def volume_file_exists(self, filename): + return filename in self.files + + def get_volume_file(self, filename, mode='r'): + return self.files[filename] + + def write_volume_file(self, filename, contents): + raise Exception('Not implemented yet') + + def remove_volume_file(self, filename): + raise Exception('Not implemented yet') + + def list_volume_directory(self, path): + return [ name for name in self.files ] + + def save_volume_file(self, filename, flask_file): + raise Exception('Not implemented yet') + + def requires_restart(self, app_config): + raise Exception('Not implemented yet') + + def get_volume_path(self, directory, filename): + raise Exception('Not implemented yet') + + def load_from_tarball(self, tarfile): + for tarinfo in tarfile.getmembers(): + if tarinfo.isfile(): + if tarinfo.name == CONFIG_FILENAME: + self.config = yaml.load(tarfile.extractfile(tarinfo.name).read()) + else: + self.files[tarinfo.name] = tarfile.extractfile(tarinfo.name).read() + diff --git a/config_app/js/components/load-config/load-config.component.ts b/config_app/js/components/load-config/load-config.component.ts index 892958e03..a4d745af2 100644 --- a/config_app/js/components/load-config/load-config.component.ts +++ b/config_app/js/components/load-config/load-config.component.ts @@ -8,7 +8,7 @@ const styleUrl = require('./load-config.css'); styleUrls: [ styleUrl ], }) export class LoadConfigComponent { - private isReady: boolean = false; + private readyToSubmit: boolean = false; private uploadFunc: Function; private state: 'load' | 'validate' = 'load'; @@ -16,12 +16,12 @@ export class LoadConfigComponent { } private handleTarballSelected(files: File[], callback: Function) { - this.isReady = true; + this.readyToSubmit = true; callback(true) } private handleTarballCleared() { - this.isReady = false; + this.readyToSubmit = false; } private uploadTarball() { @@ -30,7 +30,8 @@ export class LoadConfigComponent { this.state = 'validate'; } else { - this.apiService.errorDisplay('Could not upload configuration. Please reload the page and try again.\n' + + this.apiService.errorDisplay('Error loading configuration', + 'Could not upload configuration. Please reload the page and try again.\n' + 'If this problem persists, please contact support')(); } }); @@ -42,7 +43,7 @@ export class LoadConfigComponent { * @param files: files to upload * @param uploadFiles: function to call to upload files */ - private filesValidated(files, uploadFiles) { + private tarballValidatedByUploadBox(files, uploadFiles) { this.uploadFunc = uploadFiles; } } \ No newline at end of file diff --git a/config_app/js/components/load-config/load-config.html b/config_app/js/components/load-config/load-config.html index 3dd982f0d..bc1aee793 100644 --- a/config_app/js/components/load-config/load-config.html +++ b/config_app/js/components/load-config/load-config.html @@ -14,11 +14,11 @@ select-message="Select a previous configuration to modify. Must be in tar.gz format" files-selected="$ctrl.handleTarballSelected(files, callback)" files-cleared="$ctrl.handleFilesCleared()" - files-validated="$ctrl.filesValidated(files, uploadFiles)" + files-validated="$ctrl.tarballValidatedByUploadBox(files, uploadFiles)" extensions="['application/gzip', '.gz']">
From b5f630ba29f6478cca79b9ee9a724c5f1f0e32c9 Mon Sep 17 00:00:00 2001 From: Sam Chow Date: Tue, 19 Jun 2018 13:46:34 -0400 Subject: [PATCH 5/9] Fix alembic migrations importing app Ensure we connect to loaded config db --- config_app/c_app.py | 4 +++- config_app/config_endpoints/api/suconfig.py | 9 ++------ .../api/suconfig_models_pre_oci.py | 2 ++ config_app/config_endpoints/api/superuser.py | 15 +++++++------ .../config_endpoints/api/tar_config_loader.py | 10 ++++++++- config_app/config_util/config/__init__.py | 6 +++--- .../config_util/config/inmemoryprovider.py | 16 ++++++++------ .../config-setup-app.component.html | 2 +- .../config-setup-app.component.ts | 4 ++++ .../load-config/load-config.component.ts | 9 ++++---- .../js/components/load-config/load-config.css | 4 ---- .../components/load-config/load-config.html | 21 +------------------ .../js/core-config-setup/core-config-setup.js | 7 ++++--- config_app/js/setup/setup.component.js | 14 ++++++------- config_app/js/setup/setup.html | 8 +++---- data/migrations/env.py | 14 ++++++------- data/runmigration.py | 3 ++- endpoints/api/suconfig.py | 2 +- 18 files changed, 69 insertions(+), 81 deletions(-) diff --git a/config_app/c_app.py b/config_app/c_app.py index 049aef619..62002a15a 100644 --- a/config_app/c_app.py +++ b/config_app/c_app.py @@ -3,7 +3,7 @@ import logging from flask import Flask -from data import database +from data import database, model from util.config.superusermanager import SuperUserManager from util.ipresolver import NoopIPResolver @@ -35,3 +35,5 @@ else: config_provider.update_app_config(app.config) superusers = SuperUserManager(app) ip_resolver = NoopIPResolver() + +model.config.app_config = app.config diff --git a/config_app/config_endpoints/api/suconfig.py b/config_app/config_endpoints/api/suconfig.py index 539a6599c..37eb72bd3 100644 --- a/config_app/config_endpoints/api/suconfig.py +++ b/config_app/config_endpoints/api/suconfig.py @@ -22,9 +22,6 @@ logger = logging.getLogger(__name__) def database_is_valid(): """ Returns whether the database, as configured, is valid. """ - if app.config['TESTING']: - return False - return model.is_valid() @@ -103,9 +100,6 @@ class SuperUserConfig(ApiResource): # Link the existing user to the external user. model.attach_federated_login(current_user.username, service_name, result.username) - # Ensure database is up-to-date with config - sync_database_with_config(config_object) - return { 'exists': True, 'config': config_object @@ -182,11 +176,12 @@ class SuperUserSetupDatabase(ApiResource): configure(combined) app.config['DB_URI'] = combined['DB_URI'] + db_uri = app.config['DB_URI'] log_handler = _AlembicLogHandler() try: - run_alembic_migration(log_handler) + run_alembic_migration(db_uri, log_handler) except Exception as ex: return { 'error': str(ex) diff --git a/config_app/config_endpoints/api/suconfig_models_pre_oci.py b/config_app/config_endpoints/api/suconfig_models_pre_oci.py index df83b8e9f..655b0c1da 100644 --- a/config_app/config_endpoints/api/suconfig_models_pre_oci.py +++ b/config_app/config_endpoints/api/suconfig_models_pre_oci.py @@ -4,6 +4,8 @@ from config_app.config_endpoints.api.suconfig_models_interface import SuperuserC class PreOCIModel(SuperuserConfigDataInterface): + # Note: this method is different than has_users: the user select will throw if the user + # table does not exist, whereas has_users assumes the table is valid def is_valid(self): try: list(User.select().limit(1)) diff --git a/config_app/config_endpoints/api/superuser.py b/config_app/config_endpoints/api/superuser.py index c061adacf..c672edb80 100644 --- a/config_app/config_endpoints/api/superuser.py +++ b/config_app/config_endpoints/api/superuser.py @@ -79,14 +79,13 @@ class SuperUserCustomCertificates(ApiResource): cert_views = [] for extra_cert_path in extra_certs_found: try: - cert_full_path = config_provider.get_volume_path(EXTRA_CA_DIRECTORY, extra_cert_path) - with config_provider.get_volume_file(cert_full_path) as f: - certificate = load_certificate(f.read()) - cert_views.append({ - 'path': extra_cert_path, - 'names': list(certificate.names), - 'expired': certificate.expired, - }) + cert = config_provider.get_volume_path(EXTRA_CA_DIRECTORY, extra_cert_path) + certificate = load_certificate(cert) + cert_views.append({ + 'path': extra_cert_path, + 'names': list(certificate.names), + 'expired': certificate.expired, + }) except CertInvalidException as cie: cert_views.append({ 'path': extra_cert_path, diff --git a/config_app/config_endpoints/api/tar_config_loader.py b/config_app/config_endpoints/api/tar_config_loader.py index d7e41d446..32e907316 100644 --- a/config_app/config_endpoints/api/tar_config_loader.py +++ b/config_app/config_endpoints/api/tar_config_loader.py @@ -4,7 +4,9 @@ import cStringIO from flask import request, make_response -from config_app.c_app import config_provider +from data.database import configure + +from config_app.c_app import app, config_provider from config_app.config_endpoints.api import resource, ApiResource, nickname, validate_json_request logger = logging.getLogger(__name__) @@ -25,4 +27,10 @@ class TarConfigLoader(ApiResource): # TODO(sam): refactor config provider to accept a stream write to avoid loading into memory config_provider.load_from_tarball(config) + # now try to connect to the db provided in their config + combined = dict(**app.config) + combined.update(config_provider.get_config()) + + configure(combined) + return make_response('OK') diff --git a/config_app/config_util/config/__init__.py b/config_app/config_util/config/__init__.py index 018d5cc8c..1c2e77bfb 100644 --- a/config_app/config_util/config/__init__.py +++ b/config_app/config_util/config/__init__.py @@ -5,12 +5,12 @@ from config_app.config_util.config.inmemoryprovider import InMemoryProvider def get_config_provider(config_volume, yaml_filename, py_filename, testing=False): """ Loads and returns the config provider for the current environment. """ - if True: - return InMemoryProvider() if testing: return TestConfigProvider() - return FileConfigProvider(config_volume, yaml_filename, py_filename) + else: + return InMemoryProvider() + diff --git a/config_app/config_util/config/inmemoryprovider.py b/config_app/config_util/config/inmemoryprovider.py index 750cf1aed..e900f3381 100644 --- a/config_app/config_util/config/inmemoryprovider.py +++ b/config_app/config_util/config/inmemoryprovider.py @@ -12,6 +12,7 @@ class InMemoryProvider(BaseProvider): def __init__(self): self.files = {} self.config = {} + self.was_loaded = False @property def provider_id(self): @@ -24,16 +25,17 @@ class InMemoryProvider(BaseProvider): return self.config def save_config(self, config_object): - raise Exception('Not implemented yet') + self.config = config_object + self.was_loaded = True def config_exists(self): - raise Exception('Not implemented yet') + return self.was_loaded def volume_exists(self): - raise Exception('Not implemented yet') + return True def volume_file_exists(self, filename): - return filename in self.files + return any([ name.startswith(filename) for name in self.files ]) def get_volume_file(self, filename, mode='r'): return self.files[filename] @@ -45,7 +47,7 @@ class InMemoryProvider(BaseProvider): raise Exception('Not implemented yet') def list_volume_directory(self, path): - return [ name for name in self.files ] + return [ name for name in self.files if name.startswith(path) ] def save_volume_file(self, filename, flask_file): raise Exception('Not implemented yet') @@ -54,7 +56,8 @@ class InMemoryProvider(BaseProvider): raise Exception('Not implemented yet') def get_volume_path(self, directory, filename): - raise Exception('Not implemented yet') + # Here we can just access the filename since we're storing the tarball files with their full path + return self.files[filename] def load_from_tarball(self, tarfile): for tarinfo in tarfile.getmembers(): @@ -63,4 +66,5 @@ class InMemoryProvider(BaseProvider): self.config = yaml.load(tarfile.extractfile(tarinfo.name).read()) else: self.files[tarinfo.name] = tarfile.extractfile(tarinfo.name).read() + self.was_loaded = True diff --git a/config_app/js/components/config-setup-app/config-setup-app.component.html b/config_app/js/components/config-setup-app/config-setup-app.component.html index 52430bc35..d1ea58f31 100644 --- a/config_app/js/components/config-setup-app/config-setup-app.component.html +++ b/config_app/js/components/config-setup-app/config-setup-app.component.html @@ -23,4 +23,4 @@
- + diff --git a/config_app/js/components/config-setup-app/config-setup-app.component.ts b/config_app/js/components/config-setup-app/config-setup-app.component.ts index 6b002e1db..e22ca57c0 100644 --- a/config_app/js/components/config-setup-app/config-setup-app.component.ts +++ b/config_app/js/components/config-setup-app/config-setup-app.component.ts @@ -22,4 +22,8 @@ export class ConfigSetupAppComponent { private chooseLoad(): void { this.state = 'load'; } + + private configLoaded(): void { + this.state = 'setup'; + } } diff --git a/config_app/js/components/load-config/load-config.component.ts b/config_app/js/components/load-config/load-config.component.ts index a4d745af2..612702686 100644 --- a/config_app/js/components/load-config/load-config.component.ts +++ b/config_app/js/components/load-config/load-config.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject } from 'ng-metadata/core'; +import {Component, EventEmitter, Inject, Output} from 'ng-metadata/core'; const templateUrl = require('./load-config.html'); const styleUrl = require('./load-config.css'); @@ -10,7 +10,7 @@ const styleUrl = require('./load-config.css'); export class LoadConfigComponent { private readyToSubmit: boolean = false; private uploadFunc: Function; - private state: 'load' | 'validate' = 'load'; + @Output() public configLoaded: EventEmitter = new EventEmitter(); private constructor(@Inject('ApiService') private apiService: any) { } @@ -27,9 +27,8 @@ export class LoadConfigComponent { private uploadTarball() { this.uploadFunc(success => { if (success) { - this.state = 'validate'; - } - else { + this.configLoaded.emit({}); + } else { this.apiService.errorDisplay('Error loading configuration', 'Could not upload configuration. Please reload the page and try again.\n' + 'If this problem persists, please contact support')(); diff --git a/config_app/js/components/load-config/load-config.css b/config_app/js/components/load-config/load-config.css index a557fd3a9..e69de29bb 100644 --- a/config_app/js/components/load-config/load-config.css +++ b/config_app/js/components/load-config/load-config.css @@ -1,4 +0,0 @@ - - -.load-config__body strong { -} \ No newline at end of file diff --git a/config_app/js/components/load-config/load-config.html b/config_app/js/components/load-config/load-config.html index bc1aee793..155435baa 100644 --- a/config_app/js/components/load-config/load-config.html +++ b/config_app/js/components/load-config/load-config.html @@ -1,4 +1,4 @@ -
+
- -
- -
diff --git a/config_app/js/core-config-setup/core-config-setup.js b/config_app/js/core-config-setup/core-config-setup.js index d7f91d9bf..4e7afdac2 100644 --- a/config_app/js/core-config-setup/core-config-setup.js +++ b/config_app/js/core-config-setup/core-config-setup.js @@ -190,9 +190,10 @@ angular.module("quay-config") }; $scope.validateHostname = function(hostname) { - if (hostname.indexOf('127.0.0.1') == 0 || hostname.indexOf('localhost') == 0) { - return 'Please specify a non-localhost hostname. "localhost" will refer to the container, not your machine.' - } + // TODO(sam): maybe revert? + // if (hostname.indexOf('127.0.0.1') == 0 || hostname.indexOf('localhost') == 0) { + // return 'Please specify a non-localhost hostname. "localhost" will refer to the container, not your machine.' + // } return null; }; diff --git a/config_app/js/setup/setup.component.js b/config_app/js/setup/setup.component.js index 3e828214c..5e668ccaa 100644 --- a/config_app/js/setup/setup.component.js +++ b/config_app/js/setup/setup.component.js @@ -31,9 +31,10 @@ const templateUrl = require('./setup.html'); $scope.HOSTNAME_REGEX = '^[a-zA-Z-0-9_\.\-]+(:[0-9]+)?$'; $scope.validateHostname = function(hostname) { - if (hostname.indexOf('127.0.0.1') == 0 || hostname.indexOf('localhost') == 0) { - return 'Please specify a non-localhost hostname. "localhost" will refer to the container, not your machine.' - } + // TODO(sam): maybe revert? + // if (hostname.indexOf('127.0.0.1') == 0 || hostname.indexOf('localhost') == 0) { + // return 'Please specify a non-localhost hostname. "localhost" will refer to the container, not your machine.' + // } return null; }; @@ -65,9 +66,6 @@ const templateUrl = require('./setup.html'); // Database is being setup. 'DB_SETUP': 'setup-db', - // Database setup has succeeded. - 'DB_SETUP_SUCCESS': 'setup-db-success', - // An error occurred when setting up the database. 'DB_SETUP_ERROR': 'setup-db-error', @@ -262,7 +260,7 @@ const templateUrl = require('./setup.html'); $scope.createSuperUser = function() { $scope.currentStep = $scope.States.CREATING_SUPERUSER; ApiService.scCreateInitialSuperuser($scope.superUser, null).then(function(resp) { - UserService.load(); + // UserService.load(); $scope.checkStatus(); }, function(resp) { $scope.currentStep = $scope.States.SUPERUSER_ERROR; @@ -277,7 +275,7 @@ const templateUrl = require('./setup.html'); $scope.currentStep = $scope.States.DB_SETUP_ERROR; $scope.errors.DatabaseSetupError = resp['error']; } else { - $scope.currentStep = $scope.States.DB_SETUP_SUCCESS; + $scope.currentStep = $scope.States.CREATE_SUPERUSER; } }, ApiService.errorDisplay('Could not setup database. Please report this to support.')) }; diff --git a/config_app/js/setup/setup.html b/config_app/js/setup/setup.html index bfbc194dc..753c8cceb 100644 --- a/config_app/js/setup/setup.html +++ b/config_app/js/setup/setup.html @@ -24,8 +24,8 @@
Configure your Redis database and other settings below
- +
@@ -39,7 +39,7 @@ - + @@ -95,7 +95,7 @@