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