-
Notifications
You must be signed in to change notification settings - Fork 239
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: David Townley <[email protected]>
- Loading branch information
Showing
14 changed files
with
313 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package porter_app | ||
|
||
import ( | ||
"net/http" | ||
|
||
"github.com/porter-dev/porter/api/server/authz" | ||
"github.com/porter-dev/porter/api/server/shared/requestutils" | ||
|
||
"connectrpc.com/connect" | ||
|
||
porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1" | ||
|
||
"github.com/porter-dev/porter/internal/telemetry" | ||
|
||
"github.com/porter-dev/porter/api/server/handlers" | ||
"github.com/porter-dev/porter/api/server/shared" | ||
"github.com/porter-dev/porter/api/server/shared/apierrors" | ||
"github.com/porter-dev/porter/api/server/shared/config" | ||
"github.com/porter-dev/porter/api/types" | ||
"github.com/porter-dev/porter/internal/models" | ||
) | ||
|
||
// AppRunHandler handles requests to the /apps/{porter_app_name}/run endpoint | ||
type AppRunHandler struct { | ||
handlers.PorterHandlerReadWriter | ||
authz.KubernetesAgentGetter | ||
} | ||
|
||
// NewAppRunHandler returns a new AppRunHandler | ||
func NewAppRunHandler( | ||
config *config.Config, | ||
decoderValidator shared.RequestDecoderValidator, | ||
writer shared.ResultWriter, | ||
) *AppRunHandler { | ||
return &AppRunHandler{ | ||
PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer), | ||
KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config), | ||
} | ||
} | ||
|
||
// AppRunRequest is the request object for the /apps/{porter_app_name}/run endpoint | ||
type AppRunRequest struct { | ||
ServiceName string `json:"service_name"` | ||
DeploymentTargetID string `json:"deployment_target_id"` | ||
} | ||
|
||
// AppRunResponse is the response object for the /apps/{porter_app_name}/run endpoint | ||
type AppRunResponse struct { | ||
JobRunID string `json:"job_run_id"` | ||
} | ||
|
||
// ServeHTTP runs a one-off command in the same environment as the provided service, app and deployment target | ||
func (c *AppRunHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
ctx, span := telemetry.NewSpan(r.Context(), "serve-app-run") | ||
defer span.End() | ||
|
||
project, _ := ctx.Value(types.ProjectScope).(*models.Project) | ||
|
||
appName, reqErr := requestutils.GetURLParamString(r, types.URLParamPorterAppName) | ||
if reqErr != nil { | ||
e := telemetry.Error(ctx, span, reqErr, "error parsing app name from url") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(e, http.StatusBadRequest)) | ||
return | ||
} | ||
|
||
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "app-name", Value: appName}) | ||
|
||
request := &AppRunRequest{} | ||
if ok := c.DecodeAndValidate(w, r, request); !ok { | ||
err := telemetry.Error(ctx, span, nil, "error decoding request") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest)) | ||
return | ||
} | ||
|
||
if request.ServiceName == "" { | ||
err := telemetry.Error(ctx, span, nil, "service name is required") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest)) | ||
return | ||
} | ||
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "service-name", Value: request.ServiceName}) | ||
|
||
if request.DeploymentTargetID == "" { | ||
err := telemetry.Error(ctx, span, nil, "deployment target id is required") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest)) | ||
return | ||
} | ||
telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "deployment-target-id", Value: request.DeploymentTargetID}) | ||
|
||
manualServiceRunReq := connect.NewRequest(&porterv1.ManualServiceRunRequest{ | ||
ProjectId: int64(project.ID), | ||
AppName: appName, | ||
ServiceName: request.ServiceName, | ||
Command: nil, // use default command for job | ||
DeploymentTargetIdentifier: &porterv1.DeploymentTargetIdentifier{ | ||
Id: request.DeploymentTargetID, | ||
}, | ||
}) | ||
|
||
serviceResp, err := c.Config().ClusterControlPlaneClient.ManualServiceRun(ctx, manualServiceRunReq) | ||
if err != nil { | ||
err := telemetry.Error(ctx, span, err, "error getting app helm values from cluster control plane client") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError)) | ||
return | ||
} | ||
|
||
if serviceResp == nil || serviceResp.Msg == nil { | ||
err := telemetry.Error(ctx, span, err, "app helm values resp is nil") | ||
c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError)) | ||
return | ||
} | ||
|
||
response := AppRunResponse{ | ||
JobRunID: serviceResp.Msg.JobRunId, | ||
} | ||
|
||
c.WriteResult(w, r, response) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
dashboard/src/main/home/app-dashboard/validate-apply/jobs/TriggerJobButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import React, { useState } from "react"; | ||
import { useHistory } from "react-router"; | ||
|
||
import Button from "components/porter/Button"; | ||
import Container from "components/porter/Container"; | ||
import Error from "components/porter/Error"; | ||
|
||
import Spacer from "components/porter/Spacer"; | ||
import { useIntercom } from "lib/hooks/useIntercom"; | ||
import api from "shared/api"; | ||
import {z} from "zod"; | ||
import target from "assets/target.svg"; | ||
import Icon from "components/porter/Icon"; | ||
|
||
type Props = { | ||
projectId: number; | ||
clusterId: number; | ||
appName: string; | ||
jobName: string; | ||
deploymentTargetId: string; | ||
}; | ||
|
||
const TriggerJobButton: React.FC<Props> = ({ | ||
projectId, | ||
clusterId, | ||
appName, | ||
jobName, | ||
deploymentTargetId, | ||
}) => { | ||
const history = useHistory(); | ||
const { showIntercomWithMessage } = useIntercom(); | ||
|
||
const [errorMessage, setErrorMessage] = useState(""); | ||
const [status, setStatus] = useState(""); | ||
|
||
const triggerJobRun = async (): Promise<void> => { | ||
setStatus("loading"); | ||
setErrorMessage(""); | ||
|
||
try { | ||
const resp = await api.appRun( | ||
"<token>", | ||
{ | ||
deployment_target_id: deploymentTargetId, | ||
service_name: jobName, | ||
}, | ||
{ | ||
project_id: projectId, | ||
cluster_id: clusterId, | ||
porter_app_name: appName, | ||
}) | ||
|
||
const parsed = await z.object({job_run_id: z.string()}).parseAsync(resp.data) | ||
|
||
const jobRunID = parsed.job_run_id | ||
history.push( | ||
`/apps/${appName}/job-history?job_run_id=${jobRunID}&service=${jobName}` | ||
); | ||
} catch { | ||
setStatus(""); | ||
setErrorMessage("Unable to run job"); | ||
showIntercomWithMessage({ | ||
message: "I am running into an issue running my job.", | ||
}); | ||
} | ||
}; | ||
|
||
return ( | ||
<Container row> | ||
<Button | ||
onClick={triggerJobRun} | ||
loadingText={"Running..."} | ||
status={status} | ||
height={"33px"} | ||
> | ||
<Icon src={target} height={"15px"}/> | ||
<Spacer inline x={.5}/> | ||
Run once | ||
</Button> | ||
{errorMessage !== "" && ( | ||
<> | ||
<Spacer x={1} inline /> <Error message={errorMessage} /> | ||
</> | ||
)} | ||
</Container> | ||
); | ||
}; | ||
|
||
export default TriggerJobButton; |
4 changes: 3 additions & 1 deletion
4
dashboard/src/main/home/app-dashboard/validate-apply/jobs/utils.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.