Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix HotReloadCapable Build command #6696

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/website/docs/command-reference/dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ won't be applied immediately, but the next time the user presses the `p` key.
Depending on the local changes, different events can occur on the cluster:

- if source files are modified, they are pushed to the container running the application, and:
- if the `build` command is marked as `HotReloadCapable`, the application is responsible for building the application with the new changes
- if the `build` command is not marked as `HotReloadCapable`, the `build` command is executed again
- if the `run` command is marked as `HotReloadCapable`, the application is responsible for applying the new changes
- if the `run` command is not marked as `HotReloadCapable`, the application is stopped, then restarted by odo using the `build` and `run` commands again.
- if the `run` command is not marked as `HotReloadCapable`, the application is stopped, then restarted by odo using the `run` command again.
- if the Devfile is modified, the deployment of the application is modified with the new changes. In some circumstances, this may
cause the restart of the container running the application and therefore the application itself.

Expand Down
6 changes: 3 additions & 3 deletions docs/website/docs/development/architecture/how-odo-works.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ In a nutshell, when running [`odo dev`](../../command-reference/dev):
If the local Devfile is modified, `odo` may need to change the resources it previously created, which might result in recreating the running containers.
Note that synchronization and push to the cluster can also be triggered on demand by pressing `p` at any time.
See [the command reference on `odo dev`](../../command-reference/dev#applying-local-changes-to-the-application-on-the-cluster) for more details.
6. `odo` **optionally restarts the running application** if the command is not marked as `hotReloadCapable` in the Devfile.
If the command is marked as `hotReloadCapable`, the application is supposed to handle source code changes on its own; so `odo` does not restart the application.
Otherwise, `odo` restarts the running application by stopping the process started previously, then executes the command again in the container.
6. `odo` **optionally rebuilds and restarts the running application** if the commands are not marked as `hotReloadCapable` in the Devfile.
If the Build of Run command is marked as `hotReloadCapable`, the application is supposed to handle source code changes on its own; so `odo` does not run this command again.
Otherwise, `odo` rebuilds the application then restarts the running application by stopping the process started previously, then executes the command again in the container.
Again, it maintains a connection to that process as long as it is running in the container.
7. `odo` then **sets up port-forwarding** for each endpoint declared in the Devfile, and reports the local port in its output.
8. When `odo dev` is stopped via `Ctrl+C`, it **deletes all the resources created previously** and stops port-forwarding and code synchronization.
Expand Down
4 changes: 2 additions & 2 deletions docs/website/docs/development/devfile.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ The "loop" is cancelled once the user inputs Ctrl+C.

#### Hot Reloading

`hotReloadCapable` is a special boolean within `exec` that allows you to specify if a framework is "hot reloadable".
`hotReloadCapable` is a special boolean within an `exec` command that allows you to specify if a command is "hot reloadable".

If set to `true`, the container won't be restarted as the framework will handle file changes on its own.
If set to `true`, the command won't be restarted as the framework will handle file changes on its own.

#### Full Example

Expand Down
2 changes: 1 addition & 1 deletion pkg/component/delete/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ func (do *DeleteComponentClient) ExecutePreStopEvents(ctx context.Context, devfi

klog.V(4).Infof("Executing %q event commands for component %q", libdevfile.PreStop, componentName)
// ignore the failures if any; delete should not fail because preStop events failed to execute
err = libdevfile.ExecPreStopEvents(ctx, devfileObj, component.NewExecHandler(do.kubeClient, do.execClient, appName, componentName, pod.Name, "Executing pre-stop command in container", false))
err = libdevfile.ExecPreStopEvents(ctx, devfileObj, component.NewExecHandler(do.kubeClient, do.execClient, appName, componentName, pod.Name, "Executing pre-stop command in container", false, false))
if err != nil {
klog.V(4).Infof("Failed to execute %q event commands for component %q, cause: %v", libdevfile.PreStop, componentName, err.Error())
}
Expand Down
39 changes: 24 additions & 15 deletions pkg/component/exec_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"io"

"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"k8s.io/klog"
"k8s.io/utils/pointer"

"github.com/redhat-developer/odo/pkg/exec"
"github.com/redhat-developer/odo/pkg/libdevfile"
Expand All @@ -16,28 +18,30 @@ import (
)

type execHandler struct {
platformClient platform.Client
execClient exec.Client
appName string
componentName string
podName string
msg string
show bool
platformClient platform.Client
execClient exec.Client
appName string
componentName string
podName string
msg string
show bool
componentExists bool
}

var _ libdevfile.Handler = (*execHandler)(nil)

const ShellExecutable string = "/bin/sh"

func NewExecHandler(platformClient platform.Client, execClient exec.Client, appName, cmpName, podName, msg string, show bool) *execHandler {
func NewExecHandler(platformClient platform.Client, execClient exec.Client, appName, cmpName, podName, msg string, show bool, componentExists bool) *execHandler {
return &execHandler{
platformClient: platformClient,
execClient: execClient,
appName: appName,
componentName: cmpName,
podName: podName,
msg: msg,
show: show,
platformClient: platformClient,
execClient: execClient,
appName: appName,
componentName: cmpName,
podName: podName,
msg: msg,
show: show,
componentExists: componentExists,
}
}

Expand All @@ -54,6 +58,11 @@ func (o *execHandler) ApplyOpenShift(openshift v1alpha2.Component) error {
}

func (o *execHandler) Execute(ctx context.Context, command v1alpha2.Command) error {
if o.componentExists && command.Exec != nil && pointer.BoolDeref(command.Exec.HotReloadCapable, false) {
klog.V(2).Infof("command is hot-reload capable, not executing %q again", command.Id)
return nil
rm3l marked this conversation as resolved.
Show resolved Hide resolved
}

msg := o.msg
if msg == "" {
msg = fmt.Sprintf("Executing %s command on container %q", command.Id, command.Exec.Component)
Expand Down
21 changes: 6 additions & 15 deletions pkg/dev/kubedev/innerloop.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
odocontext "github.com/redhat-developer/odo/pkg/odo/context"
"github.com/redhat-developer/odo/pkg/port"
"github.com/redhat-developer/odo/pkg/sync"
"github.com/redhat-developer/odo/pkg/util"
"github.com/redhat-developer/odo/pkg/watch"

"k8s.io/klog"
Expand Down Expand Up @@ -90,7 +89,7 @@ func (o *DevClient) innerloop(ctx context.Context, parameters common.PushParamet
// PostStart events from the devfile will only be executed when the component
// didn't previously exist
if !componentStatus.PostStartEventsDone && libdevfile.HasPostStartEvents(parameters.Devfile) {
err = libdevfile.ExecPostStartEvents(ctx, parameters.Devfile, component.NewExecHandler(o.kubernetesClient, o.execClient, appName, componentName, pod.Name, "Executing post-start command in container", parameters.Show))
err = libdevfile.ExecPostStartEvents(ctx, parameters.Devfile, component.NewExecHandler(o.kubernetesClient, o.execClient, appName, componentName, pod.Name, "Executing post-start command in container", parameters.Show, false))
if err != nil {
return err
}
Expand Down Expand Up @@ -144,22 +143,14 @@ func (o *DevClient) innerloop(ctx context.Context, parameters common.PushParamet
// the handler we pass will be called for each command in that composite command.
doExecuteBuildCommand := func() error {
execHandler := component.NewExecHandler(o.kubernetesClient, o.execClient, appName, componentName, pod.Name,
"Building your application in container", parameters.Show)
"Building your application in container", parameters.Show, running)
return libdevfile.Build(ctx, parameters.Devfile, parameters.StartOptions.BuildCommand, execHandler)
}
if running {
if cmd.Exec == nil || !util.SafeGetBool(cmd.Exec.HotReloadCapable) {
if err = doExecuteBuildCommand(); err != nil {
componentStatus.SetState(watch.StateReady)
return err
}
}
} else {
if err = doExecuteBuildCommand(); err != nil {
componentStatus.SetState(watch.StateReady)
return err
}
if err = doExecuteBuildCommand(); err != nil {
componentStatus.SetState(watch.StateReady)
return err
}

err = libdevfile.ExecuteCommandByNameAndKind(ctx, parameters.Devfile, cmdName, cmdKind, &cmdHandler, false)
if err != nil {
return err
Expand Down
3 changes: 3 additions & 0 deletions pkg/dev/podmandev/reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func (o *DevClient) reconcile(
pod.Name,
"Executing post-start command in container",
false, /* TODO */
false,
)
err = libdevfile.ExecPostStartEvents(ctx, devfileObj, execHandler)
if err != nil {
Expand All @@ -91,9 +92,11 @@ func (o *DevClient) reconcile(
pod.Name,
"Building your application in container",
false, /* TODO */
componentStatus.RunExecuted,
)
return libdevfile.Build(ctx, devfileObj, options.BuildCommand, execHandler)
}

err = doExecuteBuildCommand()
if err != nil {
return err
Expand Down
16 changes: 16 additions & 0 deletions tests/examples/source/angular/.browserslistrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries

# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support

# You can see what browsers were selected by your queries by running:
# npx browserslist

last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
16 changes: 16 additions & 0 deletions tests/examples/source/angular/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.ts]
quote_type = single

[*.md]
max_line_length = off
trim_trailing_whitespace = false
42 changes: 42 additions & 0 deletions tests/examples/source/angular/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.

# Compiled output
/dist
/tmp
/out-tsc
/bazel-out

# Node
/node_modules
npm-debug.log
yarn-error.log

# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*

# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings

# System files
.DS_Store
Thumbs.db
27 changes: 27 additions & 0 deletions tests/examples/source/angular/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# DevfileStackNodejsAngular

This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 14.0.5.

## Development server

Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.

## Code scaffolding

Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.

## Build

Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.

## Running unit tests

Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).

## Running end-to-end tests

Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.

## Further help

To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
101 changes: 101 additions & 0 deletions tests/examples/source/angular/angular.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"devfile-stack-nodejs-angular": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/devfile-stack-nodejs-angular",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "devfile-stack-nodejs-angular:build:production"
},
"development": {
"browserTarget": "devfile-stack-nodejs-angular:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "devfile-stack-nodejs-angular:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
}
}
}
}
}
Loading