This is an automated email from the ASF dual-hosted git repository.

nferraro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit f42dedff26826e3e68a565fbc965bc9593b3a053
Author: nferraro <ni.ferr...@gmail.com>
AuthorDate: Thu Sep 6 13:04:19 2018 +0200

    Detect changes with digest and redeploy
---
 pkg/apis/camel/v1alpha1/types.go                  |  6 +--
 pkg/build/api/types.go                            |  7 ++-
 pkg/build/build_manager.go                        | 11 ++---
 pkg/build/build_manager_integration_test.go       | 18 +++++---
 pkg/build/local/local_builder.go                  | 10 ++---
 pkg/stub/action/build.go                          |  9 ++--
 pkg/stub/action/initialize.go                     |  5 +--
 pkg/stub/{handler.go => action/monitor.go}        | 52 +++++++++++------------
 pkg/stub/handler.go                               |  1 +
 pkg/{build/api/types.go => util/digest/digest.go} | 44 +++++++++----------
 10 files changed, 86 insertions(+), 77 deletions(-)

diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
index 0f58ff9..ec30eb9 100644
--- a/pkg/apis/camel/v1alpha1/types.go
+++ b/pkg/apis/camel/v1alpha1/types.go
@@ -48,9 +48,9 @@ type SourceSpec struct {
 }
 
 type IntegrationStatus struct {
-       Phase IntegrationPhase `json:"phase,omitempty"`
-       Hash  string           `json:"hash,omitempty"`
-       Image string           `json:"image,omitempty"`
+       Phase  IntegrationPhase `json:"phase,omitempty"`
+       Digest string           `json:"digest,omitempty"`
+       Image  string           `json:"image,omitempty"`
 }
 
 type IntegrationPhase string
diff --git a/pkg/build/api/types.go b/pkg/build/api/types.go
index 4500937..3358a6c 100644
--- a/pkg/build/api/types.go
+++ b/pkg/build/api/types.go
@@ -19,10 +19,15 @@ package api
 
 // a request to build a specific code
 type BuildSource struct {
-       Identifier      string
+       Identifier      BuildIdentifier
        Code            string
 }
 
+type BuildIdentifier struct {
+       Name    string
+       Digest  string
+}
+
 // represents the result of a build
 type BuildResult struct {
        Source          *BuildSource
diff --git a/pkg/build/build_manager.go b/pkg/build/build_manager.go
index fee2315..170a5e6 100644
--- a/pkg/build/build_manager.go
+++ b/pkg/build/build_manager.go
@@ -26,19 +26,19 @@ import (
 
 // main facade to the image build system
 type BuildManager struct {
-       builds  map[string]*api.BuildResult
+       builds  map[api.BuildIdentifier]*api.BuildResult
        mutex   sync.Mutex
        builder api.Builder
 }
 
 func NewBuildManager(ctx context.Context, namespace string) *BuildManager {
        return &BuildManager{
-               builds: make(map[string]*api.BuildResult),
+               builds: make(map[api.BuildIdentifier]*api.BuildResult),
                builder: local.NewLocalBuilder(ctx, namespace),
        }
 }
 
-func (m *BuildManager) Get(identifier string) api.BuildResult {
+func (m *BuildManager) Get(identifier api.BuildIdentifier) api.BuildResult {
        m.mutex.Lock()
        defer m.mutex.Unlock()
 
@@ -53,7 +53,7 @@ func (m *BuildManager) Start(source api.BuildSource) {
        m.mutex.Lock()
        defer m.mutex.Unlock()
 
-       initialBuildInfo := initialBuildInfo()
+       initialBuildInfo := initialBuildInfo(&source)
        m.builds[source.Identifier] = &initialBuildInfo
 
        resChannel := m.builder.Build(source)
@@ -72,8 +72,9 @@ func noBuildInfo() api.BuildResult {
        }
 }
 
-func initialBuildInfo() api.BuildResult {
+func initialBuildInfo(source *api.BuildSource) api.BuildResult {
        return api.BuildResult{
+               Source: source,
                Status: api.BuildStatusStarted,
        }
 }
\ No newline at end of file
diff --git a/pkg/build/build_manager_integration_test.go 
b/pkg/build/build_manager_integration_test.go
index f74fd4e..dcf7c9e 100644
--- a/pkg/build/build_manager_integration_test.go
+++ b/pkg/build/build_manager_integration_test.go
@@ -31,16 +31,19 @@ import (
 func TestBuild(t *testing.T) {
        ctx := context.TODO()
        buildManager := NewBuildManager(ctx, test.GetTargetNamespace())
-
+       identifier := build.BuildIdentifier{
+               Name: "example",
+               Digest: "sadsadasdsadasdafwefwef",
+       }
        buildManager.Start(build.BuildSource{
-               Identifier: "1",
+               Identifier: identifier,
                Code: code(),
        })
 
        deadline := time.Now().Add(5 * time.Minute)
        var result build.BuildResult
        for time.Now().Before(deadline) {
-               result = buildManager.Get("1")
+               result = buildManager.Get(identifier)
                if result.Status == build.BuildStatusCompleted || result.Status 
== build.BuildStatusError {
                        break
                }
@@ -56,16 +59,19 @@ func TestFailedBuild(t *testing.T) {
 
        ctx := context.TODO()
        buildManager := NewBuildManager(ctx, test.GetTargetNamespace())
-
+       identifier := build.BuildIdentifier{
+               Name: "example",
+               Digest: "545454",
+       }
        buildManager.Start(build.BuildSource{
-               Identifier: "1",
+               Identifier: identifier,
                Code: code() + "XX",
        })
 
        deadline := time.Now().Add(5 * time.Minute)
        var result build.BuildResult
        for time.Now().Before(deadline) {
-               result = buildManager.Get("1")
+               result = buildManager.Get(identifier)
                if result.Status == build.BuildStatusCompleted || result.Status 
== build.BuildStatusError {
                        break
                }
diff --git a/pkg/build/local/local_builder.go b/pkg/build/local/local_builder.go
index 9874434..02bacf1 100644
--- a/pkg/build/local/local_builder.go
+++ b/pkg/build/local/local_builder.go
@@ -139,7 +139,7 @@ func (b *localBuilder) publish(tarFile string, source 
build.BuildSource) (string
                        Kind: "BuildConfig",
                },
                ObjectMeta: metav1.ObjectMeta{
-                       Name: "kamel",
+                       Name: "kamel-" + source.Identifier.Name,
                        Namespace: b.namespace,
                },
                Spec: buildv1.BuildConfigSpec{
@@ -158,7 +158,7 @@ func (b *localBuilder) publish(tarFile string, source 
build.BuildSource) (string
                                Output: buildv1.BuildOutput{
                                        To: &v1.ObjectReference{
                                                Kind: "ImageStreamTag",
-                                               Name: "kamel:latest",
+                                               Name: "kamel-" + 
source.Identifier.Name + ":" + source.Identifier.Digest,
                                        },
                                },
                        },
@@ -177,7 +177,7 @@ func (b *localBuilder) publish(tarFile string, source 
build.BuildSource) (string
                        Kind: "ImageStream",
                },
                ObjectMeta: metav1.ObjectMeta{
-                       Name: "kamel",
+                       Name: "kamel-" + source.Identifier.Name,
                        Namespace: b.namespace,
                },
                Spec: imagev1.ImageStreamSpec{
@@ -224,7 +224,7 @@ func (b *localBuilder) publish(tarFile string, source 
build.BuildSource) (string
                Namespace(b.namespace).
                Body(resource).
                Resource("buildconfigs").
-               Name("kamel").
+               Name("kamel-" + source.Identifier.Name).
                SubResource("instantiatebinary").
                Do()
 
@@ -269,7 +269,7 @@ func (b *localBuilder) publish(tarFile string, source 
build.BuildSource) (string
        if is.Status.DockerImageRepository == "" {
                return "", errors.New("dockerImageRepository not available in 
ImageStream")
        }
-       return is.Status.DockerImageRepository + ":latest", nil
+       return is.Status.DockerImageRepository + ":" + 
source.Identifier.Digest, nil
 }
 
 func (b *localBuilder) createTar(buildDir string, source build.BuildSource) 
(string, error) {
diff --git a/pkg/stub/action/build.go b/pkg/stub/action/build.go
index 3b7b942..62517b0 100644
--- a/pkg/stub/action/build.go
+++ b/pkg/stub/action/build.go
@@ -45,11 +45,14 @@ func (b *BuildAction) CanHandle(integration 
*v1alpha1.Integration) bool {
 }
 
 func (b *BuildAction) Handle(integration *v1alpha1.Integration) error {
-
-       buildResult := b.buildManager.Get(integration.Status.Hash)
+       buildIdentifier := api.BuildIdentifier{
+               Name: integration.Name,
+               Digest: integration.Status.Digest,
+       }
+       buildResult := b.buildManager.Get(buildIdentifier)
        if buildResult.Status == api.BuildStatusNotRequested {
                b.buildManager.Start(api.BuildSource{
-                       Identifier: integration.Status.Hash,
+                       Identifier: buildIdentifier,
                        Code: *integration.Spec.Source.Code, // FIXME possible 
panic
                })
                logrus.Info("Build started")
diff --git a/pkg/stub/action/initialize.go b/pkg/stub/action/initialize.go
index 52d8db9..8b5a445 100644
--- a/pkg/stub/action/initialize.go
+++ b/pkg/stub/action/initialize.go
@@ -20,8 +20,7 @@ package action
 import (
        "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
        "github.com/operator-framework/operator-sdk/pkg/sdk"
-       "math/rand"
-       "strconv"
+       "github.com/apache/camel-k/pkg/util/digest"
 )
 
 // initializes the integration status to trigger the deployment
@@ -50,6 +49,6 @@ func (b *InitializeAction) Handle(integration 
*v1alpha1.Integration) error {
        }
        // update the status
        target.Status.Phase = v1alpha1.IntegrationPhaseBuilding
-       target.Status.Hash = strconv.Itoa(rand.Int()) // TODO replace with hash
+       target.Status.Digest = digest.Compute(integration)
        return sdk.Update(target)
 }
diff --git a/pkg/stub/handler.go b/pkg/stub/action/monitor.go
similarity index 52%
copy from pkg/stub/handler.go
copy to pkg/stub/action/monitor.go
index ca46e54..deb30d6 100644
--- a/pkg/stub/handler.go
+++ b/pkg/stub/action/monitor.go
@@ -15,43 +15,43 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package stub
+package action
 
 import (
-       "context"
-
        "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
-
        "github.com/operator-framework/operator-sdk/pkg/sdk"
-       "github.com/apache/camel-k/pkg/stub/action"
+       "github.com/apache/camel-k/pkg/util/digest"
        "github.com/sirupsen/logrus"
 )
 
-func NewHandler(ctx context.Context, namespace string) sdk.Handler {
-       return &Handler{
-               actionPool: []action.Action{
-                       action.NewInitializeAction(),
-                       action.NewBuildAction(ctx, namespace),
-                       action.NewDeployAction(),
-               },
-       }
+type MonitorAction struct {
+}
+
+func NewMonitorAction() *MonitorAction {
+       return &MonitorAction{}
 }
 
-type Handler struct {
-       actionPool      []action.Action
+func (b *MonitorAction) Name() string {
+       return "monitor"
 }
 
-func (h *Handler) Handle(ctx context.Context, event sdk.Event) error {
-       switch o := event.Object.(type) {
-       case *v1alpha1.Integration:
-               for _, a := range h.actionPool {
-                       if a.CanHandle(o) {
-                               logrus.Info("Invoking action ", a.Name(), " on 
integration ", o.Name)
-                               if err := a.Handle(o); err != nil {
-                                       return err
-                               }
-                       }
-               }
+func (a *MonitorAction) CanHandle(integration *v1alpha1.Integration) bool {
+       return integration.Status.Phase == v1alpha1.IntegrationPhaseRunning ||
+               integration.Status.Phase == v1alpha1.IntegrationPhaseError
+}
+
+func (a *MonitorAction) Handle(integration *v1alpha1.Integration) error {
+
+       hash := digest.Compute(integration)
+       if hash != integration.Status.Digest {
+               logrus.Info("Integration ", integration.Name, " needs a 
rebuild")
+
+               target := integration.DeepCopy()
+               target.Status.Digest=hash
+               target.Status.Phase=v1alpha1.IntegrationPhaseBuilding
+               return sdk.Update(target)
        }
+
+       // TODO check also if deployment matches (e.g. replicas)
        return nil
 }
diff --git a/pkg/stub/handler.go b/pkg/stub/handler.go
index ca46e54..a46c16f 100644
--- a/pkg/stub/handler.go
+++ b/pkg/stub/handler.go
@@ -33,6 +33,7 @@ func NewHandler(ctx context.Context, namespace string) 
sdk.Handler {
                        action.NewInitializeAction(),
                        action.NewBuildAction(ctx, namespace),
                        action.NewDeployAction(),
+                       action.NewMonitorAction(),
                },
        }
 }
diff --git a/pkg/build/api/types.go b/pkg/util/digest/digest.go
similarity index 52%
copy from pkg/build/api/types.go
copy to pkg/util/digest/digest.go
index 4500937..c29c018 100644
--- a/pkg/build/api/types.go
+++ b/pkg/util/digest/digest.go
@@ -15,31 +15,25 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package api
+package digest
 
-// a request to build a specific code
-type BuildSource struct {
-       Identifier      string
-       Code            string
-}
-
-// represents the result of a build
-type BuildResult struct {
-       Source          *BuildSource
-       Status          BuildStatus
-       Image           string
-       Error           error
-}
+import (
+       "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+       "crypto/sha256"
+       "github.com/apache/camel-k/version"
+       "encoding/base64"
+)
 
-// supertype of all builders
-type Builder interface {
-       Build(BuildSource) <- chan BuildResult
+// Compute a digest of the fields that are relevant for the deployment
+// Produces a digest that can be used as docker image tag
+func Compute(integration *v1alpha1.Integration) string {
+       hash := sha256.New()
+       // Operator version is relevant
+       hash.Write([]byte(version.Version))
+       // Integration relevant fields
+       if integration.Spec.Source.Code != nil {
+               hash.Write([]byte(*integration.Spec.Source.Code))
+       }
+       // Add a letter at the beginning and use URL safe encoding
+       return "v" + base64.RawURLEncoding.EncodeToString(hash.Sum(nil))
 }
-
-type BuildStatus int
-const (
-       BuildStatusNotRequested         BuildStatus = iota
-       BuildStatusStarted
-       BuildStatusCompleted
-       BuildStatusError
-)
\ No newline at end of file

Reply via email to