You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2018/09/06 11:06:33 UTC
[camel-k] 05/05: Detect changes with digest and redeploy
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...@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