Skip to content

Commit

Permalink
feat: migration improvements (#1068)
Browse files Browse the repository at this point in the history
Closes #1026
  • Loading branch information
vfried authored Dec 2, 2020
1 parent 60bfa61 commit 7b220e6
Show file tree
Hide file tree
Showing 6 changed files with 363 additions and 136 deletions.
25 changes: 23 additions & 2 deletions client/src/api-client/migration.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,36 @@ export default function addMigrationMethods(client) {
}));
};

client.performMigration = (projectId) => {
/**
* Performs project migrations
*
* - force_template_update: set to true to update the template even
* if automated_template_update is not set on the template (probably not a good idea...)
* - skip_template_update: don't try to update the template (superseedes force_template_update)
* - skip_docker_update: don't try to update the Dockerfile.
* - skip_migrations: don't execute migrations.
*/

client.performMigration = (projectId,
{ force_template_update = false,
skip_template_update = false,
skip_docker_update = false,
skip_migrations = false }
) => {
let headers = client.getBasicHeaders();
headers.append("Content-Type", "application/json");
headers.append("X-Requested-With", "XMLHttpRequest");

return client.clientFetch(`${client.baseUrl}/renku/cache.migrate`, {
method: "POST",
headers: headers,
body: JSON.stringify({ project_id: projectId })
body: JSON.stringify({
project_id: projectId,
force_template_update,
skip_template_update,
skip_docker_update,
skip_migrations
})
}).catch((error)=>
({ data: { error: { reason: error.case } }
}));
Expand Down
4 changes: 4 additions & 0 deletions client/src/model/RenkuModels.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ const projectSchema = new Schema({
schema: {
migration_required: { initial: null },
project_supported: { initial: null },
docker_update_possible: { initial: null }, //boolean
latest_version: { initial: null }, //string
project_version: { initial: null }, //string
template_update_possible: { initial: null }, //boolean
migrating: { initial: false },
migration_status: { initial: null },
migration_error: { initial: null },
Expand Down
8 changes: 5 additions & 3 deletions client/src/project/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,9 @@ class View extends Component {
}
}

migrateProject() { this.projectState.migrateProject(this.props.client); }
migrateProject(params) {
this.projectState.migrateProject(this.props.client, params);
}

redirectProjectWithNumericId(projectId) {
this.props.client.getProjectById(projectId)
Expand Down Expand Up @@ -636,8 +638,8 @@ class View extends Component {
fetchBranches: () => {
return this.fetchBranches();
},
onMigrateProject: () => {
return this.migrateProject();
onMigrateProject: (params) => {
return this.migrateProject(params);
}
};

Expand Down
139 changes: 17 additions & 122 deletions client/src/project/Project.present.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ import React, { Component, Fragment, useState, useEffect } from "react";

import { Link, Route, Switch } from "react-router-dom";
import {
Container, Row, Col, Alert, DropdownItem, Table, Nav, NavItem, Button, ButtonGroup, Badge, Spinner,
Card, CardBody, CardHeader, Form, FormGroup, FormText, Label, Input, UncontrolledTooltip, ListGroupItem,
UncontrolledCollapse
Container, Row, Col, Alert, DropdownItem, Table, Nav, NavItem, Button, ButtonGroup, Badge,
Card, CardBody, CardHeader, Form, FormGroup, FormText, Label, Input, UncontrolledTooltip, ListGroupItem
} from "reactstrap";
import qs from "query-string";

Expand All @@ -39,7 +38,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faStar as faStarRegular } from "@fortawesome/free-regular-svg-icons";
import {
faCodeBranch, faInfoCircle, faStar as faStarSolid,
faExclamationTriangle, faLock, faUserFriends, faGlobe, faSearch, faCheck
faExclamationTriangle, faLock, faUserFriends, faGlobe, faSearch
} from "@fortawesome/free-solid-svg-icons";
import { faGitlab } from "@fortawesome/free-brands-svg-icons";

Expand All @@ -55,7 +54,8 @@ import { CollaborationList, collaborationListTypeMap } from "../collaboration/li
import FilesTreeView from "./filestreeview/FilesTreeView";
import DatasetsListView from "./datasets/DatasetsListView";
import { ACCESS_LEVELS } from "../api-client";
import { withProjectMapped, MigrationStatus } from "./Project";
import { withProjectMapped } from "./Project";
import ProjectVersionStatus from "./status/ProjectVersionStatus.present";
import { NamespaceProjects } from "../namespace";
import { CommitsView } from "../utils/Commits";

Expand Down Expand Up @@ -106,19 +106,22 @@ class ProjectVisibilityLabel extends Component {
*
* @param {Object} webhook - project.webhook store object
* @param {bool} migration_required - whether it's necessary to migrate the project or not
* @param {bool} template_update_possible - whether it's necessary to migrate the template or not
* @param {bool} docker_update_possible - whether it's necessary to migrate the docker image or not
* @param {Object} history - react history object
* @param {string} overviewStatusUrl - overview status url
*/
class ProjectStatusIcon extends Component {
render() {
const { webhook, migration_required, overviewStatusUrl, history } = this.props;
const { webhook, migration_required, docker_update_possible, template_update_possible,
overviewStatusUrl, history } = this.props;
const kgDown = isKgDown(webhook);

if (!migration_required && !kgDown)
if (!migration_required && !docker_update_possible && !template_update_possible && !kgDown)
return null;

const versionInfo = migration_required ?
"Current Renku version is outdated. " :
const versionInfo = (migration_required || docker_update_possible || template_update_possible) ?
"Current project is outdated. " :
null;
const kgInfo = kgDown ?
"Knowledge Graph integration not active. " :
Expand Down Expand Up @@ -239,6 +242,8 @@ class ProjectViewHeaderOverview extends Component {
webhook={this.props.webhook}
overviewStatusUrl={this.props.overviewStatusUrl}
migration_required={this.props.migration.migration_required}
template_update_possible={this.props.migration.template_update_possible}
docker_update_possible={this.props.migration.docker_update_possible}
/>{core.title} <ProjectVisibilityLabel visibilityLevel={this.props.visibility.level} />
</h3>
<p>
Expand Down Expand Up @@ -445,117 +450,7 @@ class ProjectViewStats extends Component {
}
}

class ProjectViewVersion extends Component {
render() {
const { migration_required, project_supported, migration_status, check_error } = this.props.migration;
const loading = isRequestPending(this.props, "readme") || migration_required === null;
const maintainer = this.props.visibility.accessLevel >= ACCESS_LEVELS.MAINTAINER;

let body = null;
if (loading) {
body = (<Loader />);
}
else {
// print the error if any
if (check_error || migration_status === MigrationStatus.ERROR) {
const error = check_error ?
check_error :
migration_status;
body = (
<Alert color="danger">
<p>
Error while { check_error ? "checking" : "updating" } the version. Please reload the page to try again.
If the problem persists you should contact the development team on&nbsp;
<a href="https://gitter.im/SwissDataScienceCenter/renku"
target="_blank" rel="noreferrer noopener">Gitter</a> or create an issue in&nbsp;
<a href="https://github.com/SwissDataScienceCenter/renku/issues"
target="_blank" rel="noreferrer noopener">GitHub</a>.
</p>
<div><strong>Error Message</strong><pre>{error}</pre></div>
</Alert>
);
}
// migration required
else if (migration_required) {
let updateSection = null;
const updateInstruction = (
<Fragment>
You can launch
an <Link to={this.props.launchNotebookUrl}>interactive environment</Link> and follow the
{/* eslint-disable-next-line max-len */}
&nbsp;<a href="https://renku.readthedocs.io/en/latest/user/upgrading_renku.html#upgrading-your-image-to-use-the-latest-renku-cli-version">
instructions for upgrading</a>.
When finished, you will need to run <code>renku migrate</code>.
</Fragment>
);
if (maintainer) {
if (project_supported) {
updateSection = (
<Fragment>
<Button
color="warning"
disabled={migration_status === MigrationStatus.MIGRATING}
onClick={this.props.onMigrateProject}
>
{migration_status === MigrationStatus.MIGRATING ?
<span><Spinner size="sm" /> Updating...</span> : "Update"
}
</Button>
<Button color="link" id="btn_instructions"><i>Do you prefer manual instructions?</i></Button>
<UncontrolledCollapse toggler="#btn_instructions">
<br />
<p>{updateInstruction}</p>
</UncontrolledCollapse>
</Fragment>
);
}
else {
updateSection = (
<p>
<strong>Updating this project automatically is not possible.</strong>
<br /> {updateInstruction}
</p>
);
}
}
else {
updateSection = (
<p>
<strong>You do not have the required permissions to upgrade this project.</strong>
&nbsp;You can <ExternalLink role="text" size="sm"
title="ask a project maintainer" url={`${this.props.externalUrl}/project_members`} /> to
do that for you.
</p>
);
}
body = (
<Alert color="warning">
<p>
<FontAwesomeIcon icon={faExclamationTriangle} /> A new version of <strong>renku</strong> is available.
The project needs to be migrated to keep working.
</p>
{updateSection}
</Alert>
);
}
// migration not needed
else {
body = (<Alert color="success"><FontAwesomeIcon icon={faCheck} /> The current version is up to date.</Alert>);
}
}

return (
<Card className="border-0">
<CardHeader>Renku Version</CardHeader>
<CardBody>
<Row><Col>{body}</Col></Row>
</CardBody>
</Card>
);
}
}

function ProjectViewKG(props) {
function ProjectKGStatus(props) {
const loading = false;

let body = null;
Expand Down Expand Up @@ -658,8 +553,8 @@ class ProjectViewOverview extends Component {
/>
<Route exact path={this.props.overviewStatusUrl} render={props =>
<Fragment>
<ProjectViewVersion {...this.props} />
<ProjectViewKG {...this.props} />
<ProjectVersionStatus {...this.props} isLoading={isRequestPending(this.props, "readme")} />
<ProjectKGStatus {...this.props} />
</Fragment>}
/>
</Switch>
Expand Down
25 changes: 16 additions & 9 deletions client/src/project/Project.state.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ class ProjectModel extends StateModel {
super(projectSchema, stateBinding, stateHolder, initialState);
}

fetchMigrationCheck(client, user) {
client.getProjectIdFromService(this.get("system.http_url"))
fetchMigrationCheck(client) {
return client.getProjectIdFromService(this.get("system.http_url"))
.then((response)=>{
if (response.data && response.data.error !== undefined) {
this.set("migration.check_error", response.data.error.reason);
Expand All @@ -57,33 +57,40 @@ class ProjectModel extends StateModel {
client.performMigrationCheck(response)
.then((response)=>{
if (response.data && response.data.error !== undefined) {
this.set("migration.check_error", response.data.error.reason);
this.set("migration.check_error", response.data.error);
}
else {
this.set("migration.migration_required", response.data.result.migration_required);
this.set("migration.project_supported", response.data.result.project_supported);
this.set("migration.docker_update_possible", response.data.result.docker_update_possible);
this.set("migration.latest_version", response.data.result.latest_version);
this.set("migration.project_version", response.data.result.project_version);
this.set("migration.template_update_possible", response.data.result.template_update_possible);
this.set("migration.latest_template_version", response.data.result.latest_template_version);
this.set("migration.current_template_version", response.data.result.current_template_version);
}
});
}
});
}

migrateProject(client) {
migrateProject(client, params) {
if (this.get("migration.migration_status") === MigrationStatus.MIGRATING)
return;
this.set("migration.migration_status", MigrationStatus.MIGRATING);
client.getProjectIdFromService(this.get("system.http_url"))
.then((projectId)=>{
client.performMigration(projectId)
client.performMigration(projectId, params)
.then((response)=>{
if (response.data.error) {
this.set("migration.migration_status", MigrationStatus.ERROR);
this.set("migration.migration_error", response.data.error.reason);
this.set("migration.migration_error", response.data.error);
}
else {
this.fetchMigrationCheck(client);
this.set("migration.migration_status", MigrationStatus.FINISHED);
this.set("migration.migration_error", null);
this.fetchMigrationCheck(client).then(response => {
this.set("migration.migration_status", MigrationStatus.FINISHED);
this.set("migration.migration_error", null);
});
}
});
});
Expand Down
Loading

0 comments on commit 7b220e6

Please sign in to comment.