This is an automated email from the ASF dual-hosted git repository. lburgazzoli pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit 14f411996fcfdb14ca9c5c432415e84ef317a98a Author: nferraro <ni.ferr...@gmail.com> AuthorDate: Tue Sep 18 15:58:34 2018 +0200 Add sync and dev mode --- Gopkg.lock | 9 ++++ cmd/camel-k-operator/kamel_k_operator.go | 3 ++ cmd/kamel/kamel.go | 4 ++ pkg/client/cmd/run.go | 54 +++++++++++++++++++++--- cmd/kamel/kamel.go => pkg/util/sync/file.go | 48 +++++++++++++-------- pkg/util/sync/file_test.go | 65 +++++++++++++++++++++++++++++ 6 files changed, 160 insertions(+), 23 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 823b59c..a1699bb 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -318,6 +318,14 @@ revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92" [[projects]] + digest = "1:669828a2363f1ecad15fff9f008dd1d07d449fb25c9060998b15f83fec896458" + name = "github.com/radovskyb/watcher" + packages = ["."] + pruneopts = "NUT" + revision = "6145e1439b9de93806925353403f91d2abbad8a5" + version = "v1.0.2" + +[[projects]] digest = "1:0975c74a2cd70df6c2ae353c6283a25ce759dda7e1e706e5c07458baf3faca22" name = "github.com/rs/xid" packages = ["."] @@ -719,6 +727,7 @@ "github.com/operator-framework/operator-sdk/pkg/util/k8sutil", "github.com/operator-framework/operator-sdk/version", "github.com/pkg/errors", + "github.com/radovskyb/watcher", "github.com/rs/xid", "github.com/sirupsen/logrus", "github.com/spf13/cobra", diff --git a/cmd/camel-k-operator/kamel_k_operator.go b/cmd/camel-k-operator/kamel_k_operator.go index ff9e088..32ee754 100644 --- a/cmd/camel-k-operator/kamel_k_operator.go +++ b/cmd/camel-k-operator/kamel_k_operator.go @@ -19,6 +19,7 @@ package main import ( "context" + "math/rand" "runtime" "time" @@ -45,6 +46,8 @@ func watch(resource string, kind string, namespace string, resyncPeriod time.Dur } func main() { + rand.Seed(time.Now().UTC().UnixNano()) + printVersion() sdk.ExposeMetricsPort() diff --git a/cmd/kamel/kamel.go b/cmd/kamel/kamel.go index b1cba54..6c0fff2 100644 --- a/cmd/kamel/kamel.go +++ b/cmd/kamel/kamel.go @@ -21,11 +21,15 @@ import ( "fmt" "github.com/apache/camel-k/pkg/client/cmd" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + "math/rand" "os" "context" + "time" ) func main() { + rand.Seed(time.Now().UTC().UnixNano()) + ctx := context.Background() rootCmd, err := cmd.NewKamelCommand(ctx) exitOnError(err) diff --git a/pkg/client/cmd/run.go b/pkg/client/cmd/run.go index 22c0aa4..335cf84 100644 --- a/pkg/client/cmd/run.go +++ b/pkg/client/cmd/run.go @@ -20,6 +20,8 @@ package cmd import ( "errors" "fmt" + "github.com/apache/camel-k/pkg/util/sync" + "github.com/sirupsen/logrus" "io/ioutil" "os" "strconv" @@ -60,6 +62,8 @@ func NewCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command { cmd.Flags().StringSliceVar(&options.ConfigMaps, "configmap", nil, "Add a ConfigMap") cmd.Flags().StringSliceVar(&options.Secrets, "secret", nil, "Add a Secret") cmd.Flags().BoolVar(&options.Logs, "logs", false, "Print integration logs") + cmd.Flags().BoolVar(&options.Sync, "sync", false, "Synchronize the local source file with the cluster, republishing at each change") + cmd.Flags().BoolVar(&options.Dev, "dev", false, "Enable Dev mode (equivalent to \"-w --logs --sync\")") // completion support configureKnownCompletions(&cmd) @@ -78,6 +82,8 @@ type runCmdOptions struct { Secrets []string Wait bool Logs bool + Sync bool + Dev bool } func (*runCmdOptions) validateArgs(cmd *cobra.Command, args []string) error { @@ -98,18 +104,29 @@ func (o *runCmdOptions) run(cmd *cobra.Command, args []string) error { if err != nil { return err } - if o.Wait { + if o.Sync || o.Dev { + err = o.syncIntegration(args[0]) + if err != nil { + return err + } + } + if o.Wait || o.Dev { err = o.waitForIntegrationReady(integration) if err != nil { return err } } - if o.Logs { + if o.Logs || o.Dev { err = o.printLogs(integration) if err != nil { return err } } + + if o.Sync && !o.Logs && !o.Dev { + // Let's add a wait point, otherwise the script terminates + <- o.Context.Done() + } return nil } @@ -166,8 +183,33 @@ func (o *runCmdOptions) printLogs(integration *v1alpha1.Integration) error { return nil } +func (o *runCmdOptions) syncIntegration(file string) error { + changes, err := sync.File(o.Context, file) + if err != nil { + return err + } + go func() { + for { + select { + case <- o.Context.Done(): + return + case <- changes: + _, err := o.updateIntegrationCode(file) + if err != nil { + logrus.Error("Unable to sync integration: ", err) + } + } + } + }() + return nil +} + func (o *runCmdOptions) createIntegration(cmd *cobra.Command, args []string) (*v1alpha1.Integration, error) { - code, err := o.loadCode(args[0]) + return o.updateIntegrationCode(args[0]) +} + +func (o *runCmdOptions) updateIntegrationCode(filename string) (*v1alpha1.Integration, error) { + code, err := o.loadCode(filename) if err != nil { return nil, err } @@ -179,15 +221,15 @@ func (o *runCmdOptions) createIntegration(cmd *cobra.Command, args []string) (*v name = o.IntegrationName name = kubernetes.SanitizeName(name) } else { - name = kubernetes.SanitizeName(args[0]) + name = kubernetes.SanitizeName(filename) if name == "" { name = "integration" } } - codeName := args[0] + codeName := filename - if idx := strings.LastIndexByte(args[0], os.PathSeparator); idx > -1 { + if idx := strings.LastIndexByte(filename, os.PathSeparator); idx > -1 { codeName = codeName[idx+1:] } diff --git a/cmd/kamel/kamel.go b/pkg/util/sync/file.go similarity index 52% copy from cmd/kamel/kamel.go copy to pkg/util/sync/file.go index b1cba54..1be83b8 100644 --- a/cmd/kamel/kamel.go +++ b/pkg/util/sync/file.go @@ -15,28 +15,42 @@ See the License for the specific language governing permissions and limitations under the License. */ -package main +// Package sync provides useful tools to get notified when a file system resource changes +package sync import ( - "fmt" - "github.com/apache/camel-k/pkg/client/cmd" - _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" - "os" "context" + "github.com/radovskyb/watcher" + "github.com/sirupsen/logrus" + "time" ) -func main() { - ctx := context.Background() - rootCmd, err := cmd.NewKamelCommand(ctx) - exitOnError(err) +// File returns a channel that signals each time the content of the file changes +func File(ctx context.Context, path string) (<-chan bool, error) { + w := watcher.New() + if err := w.Add(path); err != nil { + return nil, err + } + w.FilterOps(watcher.Write) - err = rootCmd.Execute() - exitOnError(err) -} + out := make(chan bool) + go func() { + for { + select { + case <-ctx.Done(): + return + case <-w.Event: + out <- true + } + } + }() -func exitOnError(err error) { - if err != nil { - fmt.Println("Error:", err) - os.Exit(1) - } + go func() { + if err := w.Start(200 * time.Millisecond); err != nil { + logrus.Error("Error while starting watcher: ", err) + close(out) + } + }() + + return out, nil } diff --git a/pkg/util/sync/file_test.go b/pkg/util/sync/file_test.go new file mode 100644 index 0000000..266b55c --- /dev/null +++ b/pkg/util/sync/file_test.go @@ -0,0 +1,65 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sync + +import ( + "context" + "github.com/stretchr/testify/assert" + "io/ioutil" + "math/rand" + "os" + "path" + "strconv" + "testing" + "time" +) + +func TestFile(t *testing.T) { + tempdir := os.TempDir() + fileName := path.Join(tempdir, "camel-k-test-"+strconv.FormatUint(rand.Uint64(), 10)) + _, err := os.Create(fileName) + assert.Nil(t, err) + defer os.Remove(fileName) + + ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(100*time.Second)) + changes, err := File(ctx, fileName) + assert.Nil(t, err) + + time.Sleep(100 * time.Millisecond) + expectedNumChanges := 3 + for i := 0; i < expectedNumChanges; i++ { + ioutil.WriteFile(fileName, []byte("data-"+strconv.Itoa(i)), 0777) + time.Sleep(350 * time.Millisecond) + } + + numChanges := 0 +watch: + for { + select { + case <-ctx.Done(): + return + case <-changes: + numChanges++ + if (numChanges == expectedNumChanges) { + break watch + } + } + } + + assert.Equal(t, expectedNumChanges, numChanges) +}