You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by lb...@apache.org on 2018/09/18 16:01:21 UTC

[camel-k] 01/03: Add sync and dev mode

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...@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)
+}