github.com/replicatedhq/ship@v0.55.0/web/init/src/components/shared/DetermineComponentForRoute.jsx (about)

     1  import React from "react";
     2  import PropTypes from "prop-types";
     3  import { withRouter } from "react-router-dom";
     4  import find from "lodash/find";
     5  import indexOf from "lodash/indexOf";
     6  
     7  import Loader from "./Loader";
     8  import StepMessage from "./StepMessage";
     9  import { RENDER_PHASE, StepBuildingAssets } from "./StepBuildingAssets";
    10  import StepHelmIntro from "../../containers/HelmChartInfo";
    11  import StepHelmValues from "../kustomize/HelmValuesEditor";
    12  import { TERRAFORM_PHASE, StepTerraform } from "./StepTerraform";
    13  import { KUBECTL_PHASE, StepKubectlApply } from "./StepKubectlApply";
    14  import KustomizeEmpty from "../kustomize/kustomize_overlay/KustomizeEmpty";
    15  import KustomizeOverlay from "../../containers/KustomizeOverlay";
    16  import ConfigOnly from "../../containers/ConfigOnly";
    17  import { fetchContentForStep } from "../../redux/data/appRoutes/actions";
    18  
    19  export class DetermineComponentForRoute extends React.Component {
    20    static propTypes = {
    21      /** Callback function to be invoked at the finalization of the Ship Init flow */
    22      onCompletion: PropTypes.func,
    23    }
    24  
    25    constructor(props) {
    26      super(props);
    27      this.state = {
    28        maxPollReached: false,
    29      };
    30    }
    31  
    32    componentDidMount() {
    33      this.getContentForStep();
    34    }
    35  
    36    handleAction = async (action, gotoNext) => {
    37      await this.props.finalizeStep({action});
    38      if (gotoNext) {
    39        this.gotoRoute();
    40      }
    41    }
    42  
    43    getContentForStep = () => {
    44      const { getContentForStep, currentRoute } = this.props;
    45      const { id: routeId } = currentRoute;
    46      getContentForStep(routeId);
    47    }
    48  
    49    gotoRoute = async(route) => {
    50      let nextRoute = route;
    51      const { basePath, routes, currentRoute, history, onCompletion } = this.props;
    52  
    53      if (!nextRoute) {
    54        const currRoute = find(routes, ["id", currentRoute.id]);
    55        const currIndex = indexOf(routes, currRoute);
    56        nextRoute = routes[currIndex + 1];
    57      }
    58  
    59      if (!nextRoute) {
    60        if (onCompletion) {
    61          await this.handleShutdown();
    62          return onCompletion();
    63        }
    64  
    65        await this.handleShutdown();
    66        return this.gotoDone();
    67      }
    68  
    69      history.push(`${basePath}/${nextRoute.id}`);
    70    }
    71  
    72    handleShutdown = async () => {
    73      const { apiEndpoint, shutdownApp } = this.props;
    74  
    75      const url = `${apiEndpoint}/shutdown`;
    76      await fetch(url, {
    77        method: "POST",
    78        headers: {
    79          "Accept": "application/json",
    80        },
    81      });
    82      await shutdownApp();
    83    }
    84  
    85    gotoDone = () => {
    86      const { basePath } = this.props;
    87      this.props.history.push(`${basePath}/done`);
    88    }
    89  
    90    skipKustomize = async () => {
    91      const { apiEndpoint } = this.props;
    92      const { actions, progress } = await fetchContentForStep(apiEndpoint, "kustomize");
    93  
    94      let stepValid = true;
    95      let kustomizeErrorMessage = "";
    96      if (progress && progress.detail) {
    97        const { status, message } = JSON.parse(progress.detail);
    98        stepValid = status !== "error";
    99        kustomizeErrorMessage = message;
   100      }
   101  
   102      if (stepValid) {
   103        const [finalizeKustomize] = actions;
   104        await this.props.finalizeStep({ action: finalizeKustomize });
   105  
   106        this.startPoll("kustomize", this.gotoRoute);
   107      } else {
   108        // TODO: Handle case where error detected in Kustomize step on skip
   109        //       This can occur even if no overlays created since kustomize
   110        //       build is always executed.
   111        alert(`Error detected, cancelling kustomize step skip. Error below: \n${kustomizeErrorMessage}`);
   112      }
   113    }
   114  
   115    startPoll = async (routeId, cb) => {
   116      if (!this.props.isPolling) {
   117        this.props.pollContentForStep(routeId, cb);
   118      }
   119    }
   120  
   121    startPollingStep = (routeId) => {
   122      const { initializeStep } = this.props;
   123      initializeStep(routeId);
   124      this.startPoll(routeId, () => {
   125        // Timeout to wait a little bit before transitioning to the next step
   126        setTimeout(this.gotoRoute, 500);
   127      });
   128    }
   129  
   130    renderStep = () => {
   131      const {
   132        currentStep = {},
   133        progress,
   134        actions,
   135        location,
   136        initializeStep,
   137        phase,
   138        currentRoute,
   139        routes
   140      } = this.props;
   141      const routeId = currentRoute.id;
   142      const firstRouteIdx = indexOf(routes, find(routes, ["id", currentRoute.id]));
   143      const firstRoute = firstRouteIdx === 0;
   144  
   145      if (!phase || !phase.length) return null;
   146      switch (phase) {
   147      case "requirementNotMet":
   148        return (
   149          <div className="flex1 flex-column justifyContent--center alignItems--center">
   150            <p className="u-fontSize--large u-fontWeight--medium u-color--tundora u-marginBottom--20">Whoa there, you're getting a little ahead of yourself. There are steps that need to be completed before you can be here.</p>
   151            <button className="btn primary" onClick={this.props.history.goBack}>Take me back</button>
   152          </div>
   153        )
   154      case "message":
   155        return (
   156          <StepMessage
   157            actions={actions}
   158            message={currentStep.message}
   159            level={currentStep.level}
   160            handleAction={this.handleAction}
   161            firstRoute={firstRoute}
   162            goBack={this.props.history.goBack}
   163            isLoading={this.props.dataLoading.submitActionLoading}
   164          />
   165        );
   166      case "config":
   167        return (
   168          <ConfigOnly
   169            actions={actions}
   170            handleAction={this.handleAction}
   171            routeId={routeId}
   172            firstRoute={firstRoute}
   173            goBack={this.props.history.goBack}
   174          />
   175        );
   176      case "stream":
   177        return (
   178          <StepMessage
   179            actions={actions}
   180            message={currentStep.message}
   181            level={currentStep.level}
   182            handleAction={this.handleAction}
   183            goBack={this.props.history.goBack}
   184            firstRoute={firstRoute}
   185            isLoading={this.props.dataLoading.submitActionLoading || !currentStep.message.contents}
   186          />
   187        );
   188      case RENDER_PHASE:
   189        return (
   190          <StepBuildingAssets
   191            startPollingStep={this.startPollingStep}
   192            location={location}
   193            status={progress || currentStep.status}
   194            currentRoute={currentRoute}
   195          />
   196        );
   197      case TERRAFORM_PHASE:
   198        return (
   199          <StepTerraform
   200            startPollingStep={this.startPollingStep}
   201            currentRoute={currentRoute}
   202            startPoll={this.startPoll}
   203            location={location}
   204            status={progress || currentStep.status}
   205            handleAction={this.handleAction}
   206            gotoRoute={this.gotoRoute}
   207            goBack={this.props.history.goBack}
   208            firstRoute={firstRoute}
   209            initializeStep={initializeStep}
   210          />
   211        );
   212      case KUBECTL_PHASE:
   213        return (
   214          <StepKubectlApply
   215            startPollingStep={this.startPollingStep}
   216            currentRoute={currentRoute}
   217            startPoll={this.startPoll}
   218            location={location}
   219            status={progress || currentStep.status}
   220            handleAction={this.handleAction}
   221            gotoRoute={this.gotoRoute}
   222            initializeStep={initializeStep}
   223          />
   224        );
   225      case "helm-intro":
   226        return (
   227          <StepHelmIntro
   228            actions={actions}
   229            isUpdate={currentStep.helmIntro.isUpdate}
   230            shipAppMetadata={this.props.shipAppMetadata}
   231            handleAction={this.handleAction}
   232            goBack={this.props.history.goBack}
   233            firstRoute={firstRoute}
   234            isLoading={this.props.dataLoading.submitActionLoading}
   235          />
   236        );
   237      case "helm-values":
   238        return (
   239          <StepHelmValues
   240            saveValues={this.props.saveHelmChartValues}
   241            getStep={currentStep.helmValues}
   242            shipAppMetadata={this.props.shipAppMetadata}
   243            actions={actions}
   244            handleAction={this.handleAction}
   245            goBack={this.props.history.goBack}
   246            firstRoute={firstRoute}
   247            isLoading={this.props.dataLoading.submitActionLoading}
   248          />
   249      );
   250      case "kustomize-intro":
   251        return (
   252          <KustomizeEmpty
   253            actions={actions}
   254            handleAction={this.handleAction}
   255            skipKustomize={this.skipKustomize}
   256            goBack={this.props.history.goBack}
   257            firstRoute={firstRoute}
   258          />
   259        );
   260      case "kustomize":
   261        return (
   262          <KustomizeOverlay
   263            startPoll={this.startPoll}
   264            getCurrentStep={this.getContentForStep}
   265            pollCallback={this.gotoRoute}
   266            routeId={routeId}
   267            actions={actions}
   268            isNavcycle={true}
   269            finalizeStep={this.props.finalizeStep}
   270            handleAction={this.handleAction}
   271            currentStep={currentStep}
   272            skipKustomize={this.skipKustomize}
   273            dataLoading={this.props.dataLoading}
   274            goBack={this.props.history.goBack}
   275            firstRoute={firstRoute}
   276          />
   277        );
   278      default:
   279        return (
   280          <div className="flex1 flex-column justifyContent--center alignItems--center">
   281            <Loader size="60" />
   282          </div>
   283        );
   284      }
   285    }
   286  
   287    render() {
   288      const { phase, dataLoading } = this.props;
   289      const isLoadingStep = phase === "loading";
   290      return (
   291        <div className="flex-column flex1">
   292          <div className="flex-column flex1 u-overflow--hidden u-position--relative">
   293            <div className="flex-1-auto flex u-overflow--auto">
   294              {(isLoadingStep || dataLoading.getMetadataLoading) && !this.state.maxPollReached ?
   295                <div className="flex1 flex-column justifyContent--center alignItems--center">
   296                  <Loader size="60" />
   297                </div>
   298                : this.state.maxPollReached ?
   299                  <div className="flex1 flex-column justifyContent--center alignItems--center">
   300                    <p className="u-fontSize--large u-fontWeight--medium u-color--tundora">Oops, something isn't quite right. If you continue to experience this problem contact <a href="mailto:support@replicated.com">support@replicated.com</a></p>
   301                  </div>
   302                  :
   303                  this.renderStep()
   304              }
   305            </div>
   306          </div>
   307        </div>
   308      );
   309    }
   310  }
   311  
   312  export default withRouter(DetermineComponentForRoute)