-
Notifications
You must be signed in to change notification settings - Fork 244
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Deploy, Events without devfile/adapters (#5460)
* Execute devfile command * Undeploy * cleanup devfile/adapters * refactor * Move GetOnePod to component package * Move DoesComponentExist and Log from devfile/adapter to component package * Exec without devfile/adapters * Move Delete from devfile/adapters to component * Remove old Deploy code * review * Add tests for issue 5454 * Review
- Loading branch information
Showing
61 changed files
with
1,951 additions
and
2,583 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
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,156 @@ | ||
package component | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"io" | ||
"os" | ||
|
||
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" | ||
"github.com/pkg/errors" | ||
"github.com/redhat-developer/odo/pkg/kclient" | ||
"github.com/redhat-developer/odo/pkg/log" | ||
"github.com/redhat-developer/odo/pkg/machineoutput" | ||
"github.com/redhat-developer/odo/pkg/util" | ||
"k8s.io/klog" | ||
) | ||
|
||
type execHandler struct { | ||
kubeClient kclient.ClientInterface | ||
podName string | ||
show bool | ||
} | ||
|
||
const ShellExecutable string = "/bin/sh" | ||
|
||
func NewExecHandler(kubeClient kclient.ClientInterface, podName string, show bool) *execHandler { | ||
return &execHandler{ | ||
kubeClient: kubeClient, | ||
podName: podName, | ||
show: show, | ||
} | ||
} | ||
|
||
func (o *execHandler) ApplyImage(image v1alpha2.Component) error { | ||
return nil | ||
} | ||
|
||
func (o *execHandler) ApplyKubernetes(kubernetes v1alpha2.Component) error { | ||
return nil | ||
} | ||
|
||
func (o *execHandler) Execute(command v1alpha2.Command) error { | ||
msg := fmt.Sprintf("Executing %s command %q on container %q", command.Id, command.Exec.CommandLine, command.Exec.Component) | ||
spinner := log.Spinner(msg) | ||
defer spinner.End(false) | ||
|
||
logger := machineoutput.NewMachineEventLoggingClient() | ||
stdoutWriter, stdoutChannel, stderrWriter, stderrChannel := logger.CreateContainerOutputWriter() | ||
|
||
cmdline := getCmdline(command) | ||
err := executeCommand(o.kubeClient, command.Exec.Component, o.podName, cmdline, o.show, stdoutWriter, stderrWriter) | ||
|
||
closeWriterAndWaitForAck(stdoutWriter, stdoutChannel, stderrWriter, stderrChannel) | ||
|
||
spinner.End(true) | ||
return err | ||
} | ||
|
||
func getCmdline(command v1alpha2.Command) []string { | ||
// deal with environment variables | ||
var cmdLine string | ||
setEnvVariable := util.GetCommandStringFromEnvs(command.Exec.Env) | ||
|
||
if setEnvVariable == "" { | ||
cmdLine = command.Exec.CommandLine | ||
} else { | ||
cmdLine = setEnvVariable + " && " + command.Exec.CommandLine | ||
} | ||
|
||
// Change to the workdir and execute the command | ||
var cmd []string | ||
if command.Exec.WorkingDir != "" { | ||
// since we are using /bin/sh -c, the command needs to be within a single double quote instance, for example "cd /tmp && pwd" | ||
cmd = []string{ShellExecutable, "-c", "cd " + command.Exec.WorkingDir + " && " + cmdLine} | ||
} else { | ||
cmd = []string{ShellExecutable, "-c", cmdLine} | ||
} | ||
return cmd | ||
} | ||
|
||
func closeWriterAndWaitForAck(stdoutWriter *io.PipeWriter, stdoutChannel chan interface{}, stderrWriter *io.PipeWriter, stderrChannel chan interface{}) { | ||
if stdoutWriter != nil { | ||
_ = stdoutWriter.Close() | ||
<-stdoutChannel | ||
} | ||
if stderrWriter != nil { | ||
_ = stderrWriter.Close() | ||
<-stderrChannel | ||
} | ||
} | ||
|
||
// ExecuteCommand executes the given command in the pod's container | ||
func executeCommand(client kclient.ClientInterface, containerName string, podName string, command []string, show bool, consoleOutputStdout *io.PipeWriter, consoleOutputStderr *io.PipeWriter) (err error) { | ||
stdoutReader, stdoutWriter := io.Pipe() | ||
stderrReader, stderrWriter := io.Pipe() | ||
|
||
var cmdOutput string | ||
|
||
klog.V(2).Infof("Executing command %v for pod: %v in container: %v", command, podName, containerName) | ||
|
||
// Read stdout and stderr, store their output in cmdOutput, and also pass output to consoleOutput Writers (if non-nil) | ||
stdoutCompleteChannel := startReaderGoroutine(stdoutReader, show, &cmdOutput, consoleOutputStdout) | ||
stderrCompleteChannel := startReaderGoroutine(stderrReader, show, &cmdOutput, consoleOutputStderr) | ||
|
||
err = client.ExecCMDInContainer(containerName, podName, command, stdoutWriter, stderrWriter, nil, false) | ||
|
||
// Block until we have received all the container output from each stream | ||
_ = stdoutWriter.Close() | ||
<-stdoutCompleteChannel | ||
_ = stderrWriter.Close() | ||
<-stderrCompleteChannel | ||
|
||
if err != nil { | ||
// It is safe to read from cmdOutput here, as the goroutines are guaranteed to have terminated at this point. | ||
klog.V(2).Infof("ExecuteCommand returned an an err: %v. for command '%v'. output: %v", err, command, cmdOutput) | ||
|
||
return errors.Wrapf(err, "unable to exec command %v: \n%v", command, cmdOutput) | ||
} | ||
|
||
return | ||
} | ||
|
||
// This goroutine will automatically pipe the output from the writer (passed into ExecCMDInContainer) to | ||
// the loggers. | ||
// The returned channel will contain a single nil entry once the reader has closed. | ||
func startReaderGoroutine(reader io.Reader, show bool, cmdOutput *string, consoleOutput *io.PipeWriter) chan interface{} { | ||
|
||
result := make(chan interface{}) | ||
|
||
go func() { | ||
scanner := bufio.NewScanner(reader) | ||
for scanner.Scan() { | ||
line := scanner.Text() | ||
|
||
if log.IsDebug() || show { | ||
_, err := fmt.Fprintln(os.Stdout, line) | ||
if err != nil { | ||
log.Errorf("Unable to print to stdout: %s", err.Error()) | ||
} | ||
} | ||
|
||
*cmdOutput += fmt.Sprintln(line) | ||
|
||
if consoleOutput != nil { | ||
_, err := consoleOutput.Write([]byte(line + "\n")) | ||
if err != nil { | ||
log.Errorf("Error occurred on writing string to consoleOutput writer: %s", err.Error()) | ||
} | ||
} | ||
} | ||
result <- nil | ||
}() | ||
|
||
return result | ||
|
||
} |
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,82 @@ | ||
package deploy | ||
|
||
import ( | ||
"strings" | ||
|
||
"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" | ||
"github.com/devfile/library/pkg/devfile/parser" | ||
devfilefs "github.com/devfile/library/pkg/testingutil/filesystem" | ||
|
||
"github.com/pkg/errors" | ||
|
||
componentlabels "github.com/redhat-developer/odo/pkg/component/labels" | ||
"github.com/redhat-developer/odo/pkg/devfile/image" | ||
"github.com/redhat-developer/odo/pkg/kclient" | ||
"github.com/redhat-developer/odo/pkg/libdevfile" | ||
"github.com/redhat-developer/odo/pkg/log" | ||
"github.com/redhat-developer/odo/pkg/service" | ||
) | ||
|
||
type DeployClient struct { | ||
kubeClient kclient.ClientInterface | ||
} | ||
|
||
func NewDeployClient(kubeClient kclient.ClientInterface) *DeployClient { | ||
return &DeployClient{ | ||
kubeClient: kubeClient, | ||
} | ||
} | ||
|
||
func (o *DeployClient) Deploy(devfileObj parser.DevfileObj, path string, appName string) error { | ||
deployHandler := newDeployHandler(devfileObj, path, o.kubeClient, appName) | ||
return libdevfile.Deploy(devfileObj, deployHandler) | ||
} | ||
|
||
type deployHandler struct { | ||
devfileObj parser.DevfileObj | ||
path string | ||
kubeClient kclient.ClientInterface | ||
appName string | ||
} | ||
|
||
func newDeployHandler(devfileObj parser.DevfileObj, path string, kubeClient kclient.ClientInterface, appName string) *deployHandler { | ||
return &deployHandler{ | ||
devfileObj: devfileObj, | ||
path: path, | ||
kubeClient: kubeClient, | ||
appName: appName, | ||
} | ||
} | ||
|
||
func (o *deployHandler) ApplyImage(img v1alpha2.Component) error { | ||
return image.BuildPushSpecificImage(o.devfileObj, o.path, img, true) | ||
} | ||
|
||
func (o *deployHandler) ApplyKubernetes(kubernetes v1alpha2.Component) error { | ||
// validate if the GVRs represented by Kubernetes inlined components are supported by the underlying cluster | ||
_, err := service.ValidateResourceExist(o.kubeClient, kubernetes, o.path) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
labels := componentlabels.GetLabels(kubernetes.Name, o.appName, true) | ||
u, err := service.GetK8sComponentAsUnstructured(kubernetes.Kubernetes, o.path, devfilefs.DefaultFs{}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
log.Infof("\nDeploying Kubernetes %s: %s", u.GetKind(), u.GetName()) | ||
isOperatorBackedService, err := service.PushKubernetesResource(o.kubeClient, u, labels) | ||
if err != nil { | ||
return errors.Wrap(err, "failed to create service(s) associated with the component") | ||
} | ||
if isOperatorBackedService { | ||
log.Successf("Kubernetes resource %q on the cluster; refer %q to know how to link it to the component", strings.Join([]string{u.GetKind(), u.GetName()}, "/"), "odo link -h") | ||
|
||
} | ||
return nil | ||
} | ||
|
||
func (o *deployHandler) Execute(command v1alpha2.Command) error { | ||
return errors.New("Exec command is not implemented for Deploy") | ||
} |
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,8 @@ | ||
package deploy | ||
|
||
import "github.com/devfile/library/pkg/devfile/parser" | ||
|
||
type Client interface { | ||
// Deploy resources from a devfile located in path, for the specified appName | ||
Deploy(devfileObj parser.DevfileObj, path string, appName string) error | ||
} |
Oops, something went wrong.