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 07ea1787eb145bd5478ce4955bc35bf6b4413652
Author: nferraro <[email protected]>
AuthorDate: Fri Sep 28 12:17:52 2018 +0200

    First working version of Kaniko builder
---
 deploy/builder-pvc.yaml                            |  12 ++
 deploy/operator-deployment.yaml                    |   7 +
 deploy/resources.go                                |  23 +++
 pkg/build/build_manager.go                         |  30 ++-
 pkg/build/build_types.go                           |  15 +-
 pkg/build/packager/base.go                         | 172 ++++++++++++++++
 pkg/{install/operator.go => build/packager/doc.go} |  26 +--
 pkg/build/packager/factory.go                      |  49 +++++
 .../incremental.go}                                |  45 ++---
 .../operator.go => build/packager/types.go}        |  30 +--
 pkg/build/publish/kaniko_publisher.go              | 225 +++++++++++++++++++++
 pkg/build/publish/s2i_publisher.go                 |  97 +--------
 pkg/client/cmd/install.go                          |   4 +-
 pkg/install/common.go                              |  11 +-
 pkg/install/operator.go                            |  54 ++++-
 pkg/platform/build.go                              |  16 +-
 pkg/stub/action/platform/initialize.go             |  23 +--
 pkg/util/minishift/minishift.go                    |  60 ++++++
 .../operator.go => util/openshift/openshift.go}    |  37 ++--
 19 files changed, 729 insertions(+), 207 deletions(-)

diff --git a/deploy/builder-pvc.yaml b/deploy/builder-pvc.yaml
new file mode 100644
index 0000000..8e6ca4b
--- /dev/null
+++ b/deploy/builder-pvc.yaml
@@ -0,0 +1,12 @@
+kind: PersistentVolumeClaim
+apiVersion: v1
+metadata:
+  name: camel-k-builder
+  labels:
+    app: "camel-k"
+spec:
+  accessModes:
+  - ReadWriteMany
+  resources:
+    requests:
+      storage: 1Gi
diff --git a/deploy/operator-deployment.yaml b/deploy/operator-deployment.yaml
index e7a5180..c6e259f 100644
--- a/deploy/operator-deployment.yaml
+++ b/deploy/operator-deployment.yaml
@@ -33,3 +33,10 @@ spec:
                   fieldPath: metadata.namespace
             - name: OPERATOR_NAME
               value: "camel-k-operator"
+          volumeMounts:
+          - mountPath: /workspace
+            name: camel-k-builder
+      volumes:
+      - name: camel-k-builder
+        persistentVolumeClaim:
+          claimName: camel-k-builder
diff --git a/deploy/resources.go b/deploy/resources.go
index a3274cb..39ab8ee 100644
--- a/deploy/resources.go
+++ b/deploy/resources.go
@@ -24,6 +24,22 @@ var Resources map[string]string
 func init() {
        Resources = make(map[string]string)
 
+       Resources["builder-pvc.yaml"] =
+               `
+kind: PersistentVolumeClaim
+apiVersion: v1
+metadata:
+  name: camel-k-builder
+  labels:
+    app: "camel-k"
+spec:
+  accessModes:
+  - ReadWriteMany
+  resources:
+    requests:
+      storage: 1Gi
+
+`
        Resources["camel-catalog.yaml"] =
                `
 artifacts:
@@ -2227,6 +2243,13 @@ spec:
                   fieldPath: metadata.namespace
             - name: OPERATOR_NAME
               value: "camel-k-operator"
+          volumeMounts:
+          - mountPath: /workspace
+            name: camel-k-builder
+      volumes:
+      - name: camel-k-builder
+        persistentVolumeClaim:
+          claimName: camel-k-builder
 
 `
        Resources["operator-role-binding.yaml"] =
diff --git a/pkg/build/build_manager.go b/pkg/build/build_manager.go
index 895575b..83c0f8a 100644
--- a/pkg/build/build_manager.go
+++ b/pkg/build/build_manager.go
@@ -28,14 +28,16 @@ type Manager struct {
        ctx       context.Context
        builds    sync.Map
        assembler Assembler
+       packager  Packager
        publisher Publisher
 }
 
-// NewManager creates an instance of the build manager using the given builder
-func NewManager(ctx context.Context, assembler Assembler, publisher Publisher) 
*Manager {
+// NewManager creates an instance of the build manager using the given 
assembler, packager and publisher
+func NewManager(ctx context.Context, assembler Assembler, packager Packager, 
publisher Publisher) *Manager {
        return &Manager{
                ctx:       ctx,
                assembler: assembler,
+               packager:  packager,
                publisher: publisher,
        }
 }
@@ -68,7 +70,21 @@ func (m *Manager) Start(request Request) {
                        }
                }
 
-               publishChannel := m.publisher.Publish(request, assembled)
+               packageChannel := m.packager.Package(request, assembled)
+               var packaged PackagedOutput
+               select {
+               case <-m.ctx.Done():
+                       m.builds.Store(request.Identifier, 
canceledBuildInfo(request))
+                       return
+               case packaged = <-packageChannel:
+                       if packaged.Error != nil {
+                               m.builds.Store(request.Identifier, 
failedPackageBuildInfo(request, packaged))
+                               return
+                       }
+               }
+               defer m.packager.Cleanup(packaged)
+
+               publishChannel := m.publisher.Publish(request, assembled, 
packaged)
                var published PublishedOutput
                select {
                case <-m.ctx.Done():
@@ -114,6 +130,14 @@ func failedAssembleBuildInfo(request Request, output 
AssembledOutput) Result {
        }
 }
 
+func failedPackageBuildInfo(request Request, output PackagedOutput) Result {
+       return Result{
+               Request: request,
+               Error:   output.Error,
+               Status:  StatusError,
+       }
+}
+
 func failedPublishBuildInfo(request Request, output PublishedOutput) Result {
        return Result{
                Request: request,
diff --git a/pkg/build/build_types.go b/pkg/build/build_types.go
index 22ff178..bf83245 100644
--- a/pkg/build/build_types.go
+++ b/pkg/build/build_types.go
@@ -63,6 +63,19 @@ type Assembler interface {
        Assemble(Request) <-chan AssembledOutput
 }
 
+// PackagedOutput is the new image layer that needs to be pushed
+type PackagedOutput struct {
+       Error     error
+       BaseImage string
+       TarFile   string
+}
+
+// A Packager produces the image layer that needs to be pushed
+type Packager interface {
+       Package(Request, AssembledOutput) <-chan PackagedOutput
+       Cleanup(PackagedOutput)
+}
+
 // PublishedOutput is the output of the publish phase
 type PublishedOutput struct {
        Error error
@@ -71,7 +84,7 @@ type PublishedOutput struct {
 
 // A Publisher publishes a docker image of a build request
 type Publisher interface {
-       Publish(Request, AssembledOutput) <-chan PublishedOutput
+       Publish(Request, AssembledOutput, PackagedOutput) <-chan PublishedOutput
 }
 
 // Status --
diff --git a/pkg/build/packager/base.go b/pkg/build/packager/base.go
new file mode 100644
index 0000000..38cfdc7
--- /dev/null
+++ b/pkg/build/packager/base.go
@@ -0,0 +1,172 @@
+/*
+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 packager
+
+import (
+       "context"
+       "io/ioutil"
+       "os"
+       "path"
+       "time"
+
+       "github.com/apache/camel-k/pkg/build"
+       "github.com/apache/camel-k/pkg/util/maven"
+       "github.com/apache/camel-k/pkg/util/tar"
+       "github.com/pkg/errors"
+       "github.com/sirupsen/logrus"
+)
+
+const (
+       sharedDir         = "/workspace"
+       artifactDirPrefix = "layer-"
+)
+
+type commonPackager struct {
+       buffer chan packageOperation
+       uploadedArtifactsSelector
+}
+
+type packageOperation struct {
+       request   build.Request
+       assembled build.AssembledOutput
+       output    chan build.PackagedOutput
+}
+
+type uploadedArtifactsSelector func([]build.ClasspathEntry) (string, 
[]build.ClasspathEntry, error)
+
+func newBasePackager(ctx context.Context, rootImage string) build.Packager {
+       identitySelector := func(entries []build.ClasspathEntry) (string, 
[]build.ClasspathEntry, error) {
+               return rootImage, entries, nil
+       }
+       return newBasePackagerWithSelector(ctx, identitySelector)
+}
+
+func newBasePackagerWithSelector(ctx context.Context, 
uploadedArtifactsSelector uploadedArtifactsSelector) *commonPackager {
+       pack := commonPackager{
+               buffer: make(chan packageOperation, 100),
+               uploadedArtifactsSelector: uploadedArtifactsSelector,
+       }
+       go pack.packageCycle(ctx)
+       return &pack
+}
+
+func (b *commonPackager) Package(request build.Request, assembled 
build.AssembledOutput) <-chan build.PackagedOutput {
+       res := make(chan build.PackagedOutput, 1)
+       op := packageOperation{
+               request:   request,
+               assembled: assembled,
+               output:    res,
+       }
+       b.buffer <- op
+       return res
+}
+
+func (b *commonPackager) Cleanup(output build.PackagedOutput) {
+       parentDir, _ := path.Split(output.TarFile)
+       err := os.RemoveAll(parentDir)
+       if err != nil {
+               logrus.Warn("Could not remove temporary directory ", parentDir)
+       }
+}
+
+func (b *commonPackager) packageCycle(ctx context.Context) {
+       for {
+               select {
+               case <-ctx.Done():
+                       b.buffer = nil
+                       return
+               case op := <-b.buffer:
+                       now := time.Now()
+                       logrus.Info("Starting a new image packaging")
+                       res := b.execute(op.request, op.assembled)
+                       elapsed := time.Now().Sub(now)
+
+                       if res.Error != nil {
+                               logrus.Error("Error during packaging (total 
time ", elapsed.Seconds(), " seconds): ", res.Error)
+                       } else {
+                               logrus.Info("Packaging completed in ", 
elapsed.Seconds(), " seconds")
+                       }
+
+                       op.output <- res
+               }
+       }
+}
+
+func (b *commonPackager) execute(request build.Request, assembled 
build.AssembledOutput) build.PackagedOutput {
+       baseImageName, selectedArtifacts, err := 
b.uploadedArtifactsSelector(assembled.Classpath)
+       if err != nil {
+               return build.PackagedOutput{Error: err}
+       }
+
+       tarFile, err := b.createTar(assembled, selectedArtifacts)
+       if err != nil {
+               return build.PackagedOutput{Error: err}
+       }
+
+       return build.PackagedOutput{
+               BaseImage: baseImageName,
+               TarFile:   tarFile,
+       }
+}
+
+func (b *commonPackager) createTar(assembled build.AssembledOutput, 
selectedArtifacts []build.ClasspathEntry) (string, error) {
+       artifactDir, err := ioutil.TempDir(sharedDir, artifactDirPrefix)
+       if err != nil {
+               return "", errors.Wrap(err, "could not create temporary dir for 
packaged artifacts")
+       }
+
+       tarFileName := path.Join(artifactDir, "occi.tar")
+       tarAppender, err := tar.NewAppender(tarFileName)
+       if err != nil {
+               return "", err
+       }
+       defer tarAppender.Close()
+
+       tarDir := "dependencies/"
+       for _, entry := range selectedArtifacts {
+               gav, err := maven.ParseGAV(entry.ID)
+               if err != nil {
+                       return "", nil
+               }
+
+               tarPath := path.Join(tarDir, gav.GroupID)
+               _, err = tarAppender.AddFile(entry.Location, tarPath)
+               if err != nil {
+                       return "", err
+               }
+       }
+
+       cp := ""
+       for _, entry := range assembled.Classpath {
+               gav, err := maven.ParseGAV(entry.ID)
+               if err != nil {
+                       return "", nil
+               }
+               tarPath := path.Join(tarDir, gav.GroupID)
+               _, fileName := path.Split(entry.Location)
+               fileName = path.Join(tarPath, fileName)
+               cp += fileName + "\n"
+       }
+
+       err = tarAppender.AppendData([]byte(cp), "classpath")
+       if err != nil {
+               return "", err
+       }
+
+       return tarFileName, nil
+}
diff --git a/pkg/install/operator.go b/pkg/build/packager/doc.go
similarity index 57%
copy from pkg/install/operator.go
copy to pkg/build/packager/doc.go
index eb3ac4c..cfa21c6 100644
--- a/pkg/install/operator.go
+++ b/pkg/build/packager/doc.go
@@ -15,27 +15,5 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package install
-
-// Operator --
-func Operator(namespace string) error {
-       return Resources(namespace,
-               "operator-service-account.yaml",
-               "operator-role-openshift.yaml", // TODO distinguish between 
Openshift and Kubernetes
-               "operator-role-binding.yaml",
-               "operator-deployment.yaml",
-               "operator-service.yaml",
-       )
-}
-
-// Platform installs the platform custom resource
-func Platform(namespace string) error {
-       return Resource(namespace, "platform-cr.yaml")
-}
-
-// Example --
-func Example(namespace string) error {
-       return Resources(namespace,
-               "cr-example.yaml",
-       )
-}
+// Package packager contains strategies for building layers of a Docker image
+package packager
diff --git a/pkg/build/packager/factory.go b/pkg/build/packager/factory.go
new file mode 100644
index 0000000..48ad665
--- /dev/null
+++ b/pkg/build/packager/factory.go
@@ -0,0 +1,49 @@
+/*
+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 packager
+
+import (
+       "context"
+       "github.com/apache/camel-k/pkg/build"
+)
+
+const (
+       s2iRootImage = "fabric8/s2i-java:2.3"
+       //javaRootImage = "fabric8/java-jboss-openjdk8-jdk:1.5.1"
+       javaRootImage = "fabric8/java-alpine-openjdk8-jdk:1.5.1"
+)
+
+// NewS2IStandardPackager creates a standard packager for S2I builds
+func NewS2IStandardPackager(ctx context.Context) build.Packager {
+       return newBasePackager(ctx, s2iRootImage)
+}
+
+// NewS2IIncrementalPackager creates a incremental packager for S2I builds
+func NewS2IIncrementalPackager(ctx context.Context, lister 
PublishedImagesLister) build.Packager {
+       return newIncrementalPackager(ctx, lister, s2iRootImage)
+}
+
+// NewJavaStandardPackager creates a standard packager for Java Docker builds
+func NewJavaStandardPackager(ctx context.Context) build.Packager {
+       return newBasePackager(ctx, javaRootImage)
+}
+
+// NewJavaIncrementalPackager creates a incremental packager for Java Docker 
builds
+func NewJavaIncrementalPackager(ctx context.Context, lister 
PublishedImagesLister) build.Packager {
+       return newIncrementalPackager(ctx, lister, javaRootImage)
+}
diff --git a/pkg/build/publish/s2i_incremental_publisher.go 
b/pkg/build/packager/incremental.go
similarity index 62%
rename from pkg/build/publish/s2i_incremental_publisher.go
rename to pkg/build/packager/incremental.go
index ae996e4..cb9334b 100644
--- a/pkg/build/publish/s2i_incremental_publisher.go
+++ b/pkg/build/packager/incremental.go
@@ -15,7 +15,7 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package publish
+package packager
 
 import (
        "context"
@@ -23,36 +23,31 @@ import (
        "github.com/apache/camel-k/pkg/build"
 )
 
-type s2iIncrementalPublisher struct {
-       s2iPublisher *s2iPublisher
-       lister       PublishedImagesLister
+type incrementalPackager struct {
+       commonPackager *commonPackager
+       lister         PublishedImagesLister
+       rootImage      string
 }
 
-// PublishedImage represent a base image that can be used as starting point
-type PublishedImage struct {
-       Image     string
-       Classpath []string
-}
-
-// PublishedImagesLister allows to list all images already published
-type PublishedImagesLister interface {
-       ListPublishedImages() ([]PublishedImage, error)
+// newIncrementalPackager creates a new packager that is able to create a 
layer on top of a existing image
+func newIncrementalPackager(ctx context.Context, lister PublishedImagesLister, 
rootImage string) build.Packager {
+       layeredPackager := incrementalPackager{
+               lister:    lister,
+               rootImage: rootImage,
+       }
+       layeredPackager.commonPackager = newBasePackagerWithSelector(ctx, 
layeredPackager.selectArtifactsToUpload)
+       return &layeredPackager
 }
 
-// NewS2IIncrementalPublisher creates a new publisher that is able to do a 
Openshift S2I binary builds on top of other builds
-func NewS2IIncrementalPublisher(ctx context.Context, namespace string, lister 
PublishedImagesLister) build.Publisher {
-       layeredPublisher := s2iIncrementalPublisher{
-               lister: lister,
-       }
-       layeredPublisher.s2iPublisher = newS2IPublisher(ctx, namespace, 
layeredPublisher.selectArtifactsToUpload)
-       return &layeredPublisher
+func (p *incrementalPackager) Package(req build.Request, assembled 
build.AssembledOutput) <-chan build.PackagedOutput {
+       return p.commonPackager.Package(req, assembled)
 }
 
-func (p *s2iIncrementalPublisher) Publish(req build.Request, assembled 
build.AssembledOutput) <-chan build.PublishedOutput {
-       return p.s2iPublisher.Publish(req, assembled)
+func (p *incrementalPackager) Cleanup(output build.PackagedOutput) {
+       p.commonPackager.Cleanup(output)
 }
 
-func (p *s2iIncrementalPublisher) selectArtifactsToUpload(entries 
[]build.ClasspathEntry) (string, []build.ClasspathEntry, error) {
+func (p *incrementalPackager) selectArtifactsToUpload(entries 
[]build.ClasspathEntry) (string, []build.ClasspathEntry, error) {
        images, err := p.lister.ListPublishedImages()
        if err != nil {
                return "", nil, err
@@ -71,10 +66,10 @@ func (p *s2iIncrementalPublisher) 
selectArtifactsToUpload(entries []build.Classp
        }
 
        // return default selection
-       return baseImage, entries, nil
+       return p.rootImage, entries, nil
 }
 
-func (p *s2iIncrementalPublisher) findBestImage(images []PublishedImage, 
entries []build.ClasspathEntry) (*PublishedImage, map[string]bool) {
+func (p *incrementalPackager) findBestImage(images []PublishedImage, entries 
[]build.ClasspathEntry) (*PublishedImage, map[string]bool) {
        if len(images) == 0 {
                return nil, nil
        }
diff --git a/pkg/install/operator.go b/pkg/build/packager/types.go
similarity index 57%
copy from pkg/install/operator.go
copy to pkg/build/packager/types.go
index eb3ac4c..4965e28 100644
--- a/pkg/install/operator.go
+++ b/pkg/build/packager/types.go
@@ -15,27 +15,15 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package install
+package packager
 
-// Operator --
-func Operator(namespace string) error {
-       return Resources(namespace,
-               "operator-service-account.yaml",
-               "operator-role-openshift.yaml", // TODO distinguish between 
Openshift and Kubernetes
-               "operator-role-binding.yaml",
-               "operator-deployment.yaml",
-               "operator-service.yaml",
-       )
+// PublishedImage represent a base image that can be used as starting point
+type PublishedImage struct {
+       Image     string
+       Classpath []string
 }
 
-// Platform installs the platform custom resource
-func Platform(namespace string) error {
-       return Resource(namespace, "platform-cr.yaml")
-}
-
-// Example --
-func Example(namespace string) error {
-       return Resources(namespace,
-               "cr-example.yaml",
-       )
-}
+// PublishedImagesLister allows to list all images already published
+type PublishedImagesLister interface {
+       ListPublishedImages() ([]PublishedImage, error)
+}
\ No newline at end of file
diff --git a/pkg/build/publish/kaniko_publisher.go 
b/pkg/build/publish/kaniko_publisher.go
new file mode 100644
index 0000000..7c5a511
--- /dev/null
+++ b/pkg/build/publish/kaniko_publisher.go
@@ -0,0 +1,225 @@
+/*
+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 publish
+
+import (
+       tarutils "archive/tar"
+       "context"
+       "github.com/apache/camel-k/pkg/util/kubernetes"
+       "io"
+       "io/ioutil"
+       "os"
+       "path"
+       "time"
+
+       "github.com/apache/camel-k/pkg/build"
+       "github.com/operator-framework/operator-sdk/pkg/sdk"
+       "github.com/pkg/errors"
+       "github.com/sirupsen/logrus"
+       "k8s.io/api/core/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+type kanikoPublisher struct {
+       buffer    chan kanikoPublishOperation
+       namespace string
+       registry  string
+}
+
+type kanikoPublishOperation struct {
+       request   build.Request
+       assembled build.AssembledOutput
+       packaged  build.PackagedOutput
+       output    chan build.PublishedOutput
+}
+
+// NewKanikoPublisher creates a new publisher doing a Kaniko image push
+func NewKanikoPublisher(ctx context.Context, namespace string, registry 
string) build.Publisher {
+       publisher := kanikoPublisher{
+               buffer:    make(chan kanikoPublishOperation, 100),
+               namespace: namespace,
+               registry:  registry,
+       }
+       go publisher.publishCycle(ctx)
+       return &publisher
+}
+
+func (b *kanikoPublisher) Publish(request build.Request, assembled 
build.AssembledOutput, packaged build.PackagedOutput) <-chan 
build.PublishedOutput {
+       res := make(chan build.PublishedOutput, 1)
+       op := kanikoPublishOperation{
+               request:   request,
+               assembled: assembled,
+               packaged:  packaged,
+               output:    res,
+       }
+       b.buffer <- op
+       return res
+}
+
+func (b *kanikoPublisher) publishCycle(ctx context.Context) {
+       for {
+               select {
+               case <-ctx.Done():
+                       b.buffer = nil
+                       return
+               case op := <-b.buffer:
+                       now := time.Now()
+                       logrus.Info("Starting a new image publication")
+                       res := b.execute(op.request, op.assembled, op.packaged)
+                       elapsed := time.Now().Sub(now)
+
+                       if res.Error != nil {
+                               logrus.Error("Error during publication (total 
time ", elapsed.Seconds(), " seconds): ", res.Error)
+                       } else {
+                               logrus.Info("Publication completed in ", 
elapsed.Seconds(), " seconds")
+                       }
+
+                       op.output <- res
+               }
+       }
+}
+
+func (b *kanikoPublisher) execute(request build.Request, assembled 
build.AssembledOutput, packaged build.PackagedOutput) build.PublishedOutput {
+       image, err := b.publish(packaged.TarFile, packaged.BaseImage, request)
+       if err != nil {
+               return build.PublishedOutput{Error: err}
+       }
+
+       return build.PublishedOutput{Image: image}
+}
+
+func (b *kanikoPublisher) publish(tarFile string, baseImageName string, source 
build.Request) (string, error) {
+       image := b.registry + "/" + b.namespace + "/camel-k-" + 
source.Identifier.Name + ":" + source.Identifier.Qualifier
+       contextDir, err := b.prepareContext(tarFile, baseImageName)
+       if err != nil {
+               return "", err
+       }
+       pod := v1.Pod{
+               TypeMeta: metav1.TypeMeta{
+                       APIVersion: v1.SchemeGroupVersion.String(),
+                       Kind:       "Pod",
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Namespace: b.namespace,
+                       Name:      "camel-k-" + source.Identifier.Name,
+               },
+               Spec: v1.PodSpec{
+                       Containers: []v1.Container{
+                               {
+                                       Name:  "kaniko",
+                                       Image: 
"gcr.io/kaniko-project/executor:latest",
+                                       Args: []string{
+                                               "--dockerfile=Dockerfile",
+                                               "--context=" + contextDir,
+                                               "--destination=" + image,
+                                               "--insecure",
+                                       },
+                                       VolumeMounts: []v1.VolumeMount{
+                                               {
+                                                       Name:      
"camel-k-builder",
+                                                       MountPath: "/workspace",
+                                               },
+                                       },
+                               },
+                       },
+                       RestartPolicy: v1.RestartPolicyNever,
+                       Volumes: []v1.Volume{
+                               {
+                                       Name: "camel-k-builder",
+                                       VolumeSource: v1.VolumeSource{
+                                               PersistentVolumeClaim: 
&v1.PersistentVolumeClaimVolumeSource{
+                                                       ClaimName: 
"camel-k-builder",
+                                                       ReadOnly:  true,
+                                               },
+                                       },
+                               },
+                       },
+               },
+       }
+
+       sdk.Delete(&pod)
+       err = sdk.Create(&pod)
+       if err != nil {
+               return "", errors.Wrap(err, "cannot create kaniko builder pod")
+       }
+
+       err = kubernetes.WaitCondition(&pod, func(obj interface{}) (bool, 
error) {
+               if val, ok := obj.(*v1.Pod); ok {
+                       if val.Status.Phase == v1.PodSucceeded {
+                               return true, nil
+                       } else if val.Status.Phase == v1.PodFailed {
+                               return false, errors.New("build failed")
+                       }
+               }
+               return false, nil
+       }, 5*time.Minute)
+
+       if err != nil {
+               return "", err
+       }
+
+       return image, nil
+}
+
+func (b *kanikoPublisher) prepareContext(tarName string, baseImage string) 
(string, error) {
+       baseDir, _ := path.Split(tarName)
+       contextDir := path.Join(baseDir, "context")
+       if err := b.unTar(tarName, contextDir); err != nil {
+               return "", err
+       }
+
+       dockerFileContent := []byte(`
+               FROM ` + baseImage + `
+               ADD . /deployments
+       `)
+       if err := ioutil.WriteFile(path.Join(contextDir, "Dockerfile"), 
dockerFileContent, 0777); err != nil {
+               return "", err
+       }
+       return contextDir, nil
+}
+
+func (b *kanikoPublisher) unTar(tarName string, dir string) error {
+       file, err := os.Open(tarName)
+       if err != nil {
+               return err
+       }
+       defer file.Close()
+       reader := tarutils.NewReader(file)
+       for {
+               header, err := reader.Next()
+               if err == io.EOF {
+                       break
+               }
+               if err != nil {
+                       return err
+               }
+               targetName := path.Join(dir, header.Name)
+               targetDir, _ := path.Split(targetName)
+               if err := os.MkdirAll(targetDir, 0777); err != nil {
+                       return err
+               }
+               buffer, err := ioutil.ReadAll(reader)
+               if err != nil {
+                       return err
+               }
+               if err := ioutil.WriteFile(targetName, buffer, 
os.FileMode(header.Mode)); err != nil {
+                       return err
+               }
+       }
+       return nil
+}
diff --git a/pkg/build/publish/s2i_publisher.go 
b/pkg/build/publish/s2i_publisher.go
index 97147a7..09a0105 100644
--- a/pkg/build/publish/s2i_publisher.go
+++ b/pkg/build/publish/s2i_publisher.go
@@ -20,14 +20,11 @@ package publish
 import (
        "context"
        "io/ioutil"
-       "path"
        "time"
 
        "github.com/apache/camel-k/pkg/build"
        "github.com/apache/camel-k/pkg/util/kubernetes"
        "github.com/apache/camel-k/pkg/util/kubernetes/customclient"
-       "github.com/apache/camel-k/pkg/util/maven"
-       "github.com/apache/camel-k/pkg/util/tar"
        buildv1 "github.com/openshift/api/build/v1"
        imagev1 "github.com/openshift/api/image/v1"
        "github.com/operator-framework/operator-sdk/pkg/sdk"
@@ -39,49 +36,34 @@ import (
        "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 )
 
-const (
-       artifactDirPrefix = "s2i-"
-       baseImage         = "fabric8/s2i-java:2.3"
-)
-
 type s2iPublisher struct {
-       buffer    chan publishOperation
+       buffer    chan s2iPublishOperation
        namespace string
-       uploadedArtifactsSelector
 }
 
-type publishOperation struct {
+type s2iPublishOperation struct {
        request   build.Request
        assembled build.AssembledOutput
+       packaged  build.PackagedOutput
        output    chan build.PublishedOutput
 }
 
-type uploadedArtifactsSelector func([]build.ClasspathEntry) (string, 
[]build.ClasspathEntry, error)
-
 // NewS2IPublisher creates a new publisher doing a Openshift S2I binary build
 func NewS2IPublisher(ctx context.Context, namespace string) build.Publisher {
-       identitySelector := func(entries []build.ClasspathEntry) (string, 
[]build.ClasspathEntry, error) {
-               return baseImage, entries, nil
-       }
-       return newS2IPublisher(ctx, namespace, identitySelector)
-}
-
-// NewS2IPublisher creates a new publisher doing a Openshift S2I binary build
-func newS2IPublisher(ctx context.Context, namespace string, 
uploadedArtifactsSelector uploadedArtifactsSelector) *s2iPublisher {
        publisher := s2iPublisher{
-               buffer:                    make(chan publishOperation, 100),
-               namespace:                 namespace,
-               uploadedArtifactsSelector: uploadedArtifactsSelector,
+               buffer:    make(chan s2iPublishOperation, 100),
+               namespace: namespace,
        }
        go publisher.publishCycle(ctx)
        return &publisher
 }
 
-func (b *s2iPublisher) Publish(request build.Request, assembled 
build.AssembledOutput) <-chan build.PublishedOutput {
+func (b *s2iPublisher) Publish(request build.Request, assembled 
build.AssembledOutput, packaged build.PackagedOutput) <-chan 
build.PublishedOutput {
        res := make(chan build.PublishedOutput, 1)
-       op := publishOperation{
+       op := s2iPublishOperation{
                request:   request,
                assembled: assembled,
+               packaged:  packaged,
                output:    res,
        }
        b.buffer <- op
@@ -97,7 +79,7 @@ func (b *s2iPublisher) publishCycle(ctx context.Context) {
                case op := <-b.buffer:
                        now := time.Now()
                        logrus.Info("Starting a new image publication")
-                       res := b.execute(op.request, op.assembled)
+                       res := b.execute(op.request, op.assembled, op.packaged)
                        elapsed := time.Now().Sub(now)
 
                        if res.Error != nil {
@@ -111,18 +93,8 @@ func (b *s2iPublisher) publishCycle(ctx context.Context) {
        }
 }
 
-func (b *s2iPublisher) execute(request build.Request, assembled 
build.AssembledOutput) build.PublishedOutput {
-       baseImageName, selectedArtifacts, err := 
b.uploadedArtifactsSelector(assembled.Classpath)
-       if err != nil {
-               return build.PublishedOutput{Error: err}
-       }
-
-       tarFile, err := b.createTar(assembled, selectedArtifacts)
-       if err != nil {
-               return build.PublishedOutput{Error: err}
-       }
-
-       image, err := b.publish(tarFile, baseImageName, request)
+func (b *s2iPublisher) execute(request build.Request, assembled 
build.AssembledOutput, packaged build.PackagedOutput) build.PublishedOutput {
+       image, err := b.publish(packaged.TarFile, packaged.BaseImage, request)
        if err != nil {
                return build.PublishedOutput{Error: err}
        }
@@ -253,50 +225,3 @@ func (b *s2iPublisher) publish(tarFile string, imageName 
string, source build.Re
        }
        return is.Status.DockerImageRepository + ":" + 
source.Identifier.Qualifier, nil
 }
-
-func (b *s2iPublisher) createTar(assembled build.AssembledOutput, 
selectedArtifacts []build.ClasspathEntry) (string, error) {
-       artifactDir, err := ioutil.TempDir("", artifactDirPrefix)
-       if err != nil {
-               return "", errors.Wrap(err, "could not create temporary dir for 
s2i artifacts")
-       }
-
-       tarFileName := path.Join(artifactDir, "occi.tar")
-       tarAppender, err := tar.NewAppender(tarFileName)
-       if err != nil {
-               return "", err
-       }
-       defer tarAppender.Close()
-
-       tarDir := "dependencies/"
-       for _, entry := range selectedArtifacts {
-               gav, err := maven.ParseGAV(entry.ID)
-               if err != nil {
-                       return "", nil
-               }
-
-               tarPath := path.Join(tarDir, gav.GroupID)
-               _, err = tarAppender.AddFile(entry.Location, tarPath)
-               if err != nil {
-                       return "", err
-               }
-       }
-
-       cp := ""
-       for _, entry := range assembled.Classpath {
-               gav, err := maven.ParseGAV(entry.ID)
-               if err != nil {
-                       return "", nil
-               }
-               tarPath := path.Join(tarDir, gav.GroupID)
-               _, fileName := path.Split(entry.Location)
-               fileName = path.Join(tarPath, fileName)
-               cp += fileName + "\n"
-       }
-
-       err = tarAppender.AppendData([]byte(cp), "classpath")
-       if err != nil {
-               return "", err
-       }
-
-       return tarFileName, nil
-}
diff --git a/pkg/client/cmd/install.go b/pkg/client/cmd/install.go
index 0d20021..abe61c5 100644
--- a/pkg/client/cmd/install.go
+++ b/pkg/client/cmd/install.go
@@ -41,6 +41,7 @@ func newCmdInstall(rootCmdOptions *RootCmdOptions) 
*cobra.Command {
 
        cmd.Flags().BoolVar(&options.clusterSetupOnly, "cluster-setup", false, 
"Execute cluster-wide operations only (may require admin rights)")
        cmd.Flags().BoolVar(&options.exampleSetup, "example", false, "Install 
example integration")
+       cmd.Flags().StringVar(&options.registry, "registry", "", "A Docker 
registry that can be used to publish images")
        cmd.ParseFlags(os.Args)
 
        return &cmd
@@ -50,6 +51,7 @@ type installCmdOptions struct {
        *RootCmdOptions
        clusterSetupOnly bool
        exampleSetup     bool
+       registry         string
 }
 
 func (o *installCmdOptions) install(cmd *cobra.Command, args []string) error {
@@ -69,7 +71,7 @@ func (o *installCmdOptions) install(cmd *cobra.Command, args 
[]string) error {
                        return err
                }
 
-               err = install.Platform(namespace)
+               err = install.Platform(namespace, o.registry)
                if err != nil {
                        return err
                }
diff --git a/pkg/install/common.go b/pkg/install/common.go
index bef7f6f..6d7c61f 100644
--- a/pkg/install/common.go
+++ b/pkg/install/common.go
@@ -24,6 +24,7 @@ import (
        "github.com/operator-framework/operator-sdk/pkg/sdk"
        "k8s.io/apimachinery/pkg/api/errors"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/runtime"
 )
 
 // Resources installs named resources from the project resource directory
@@ -43,11 +44,16 @@ func Resource(namespace string, name string) error {
                return err
        }
 
+       return RuntimeObject(namespace, obj)
+}
+
+// RuntimeObject installs a single runtime object
+func RuntimeObject(namespace string, obj runtime.Object) error {
        if metaObject, ok := obj.(metav1.Object); ok {
                metaObject.SetNamespace(namespace)
        }
 
-       err = sdk.Create(obj)
+       err := sdk.Create(obj)
        if err != nil && errors.IsAlreadyExists(err) {
                // Don't recreate Service object
                if obj.GetObjectKind().GroupVersionKind().Kind == "Service" {
@@ -60,6 +66,9 @@ func Resource(namespace string, name string) error {
                if obj.GetObjectKind().GroupVersionKind().Kind == 
v1alpha1.IntegrationPlatformKind {
                        return nil
                }
+               if obj.GetObjectKind().GroupVersionKind().Kind == 
"PersistentVolumeClaim" {
+                       return nil
+               }
                return sdk.Update(obj)
        }
        return err
diff --git a/pkg/install/operator.go b/pkg/install/operator.go
index eb3ac4c..f0ec970 100644
--- a/pkg/install/operator.go
+++ b/pkg/install/operator.go
@@ -17,20 +17,68 @@ limitations under the License.
 
 package install
 
+import (
+       "errors"
+       "github.com/apache/camel-k/deploy"
+       "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+       "github.com/apache/camel-k/pkg/util/kubernetes"
+       "github.com/apache/camel-k/pkg/util/minishift"
+       "github.com/apache/camel-k/pkg/util/openshift"
+)
+
 // Operator --
 func Operator(namespace string) error {
+       isOpenshift, err := openshift.IsOpenShift()
+       if err != nil {
+               return err
+       }
+       var operatorRole string
+       if isOpenshift {
+               operatorRole = "operator-role-openshift.yaml"
+       } else {
+               operatorRole = "operator-role-kubernetes.yaml"
+       }
        return Resources(namespace,
                "operator-service-account.yaml",
-               "operator-role-openshift.yaml", // TODO distinguish between 
Openshift and Kubernetes
+               operatorRole,
                "operator-role-binding.yaml",
+               "builder-pvc.yaml",
                "operator-deployment.yaml",
                "operator-service.yaml",
        )
 }
 
 // Platform installs the platform custom resource
-func Platform(namespace string) error {
-       return Resource(namespace, "platform-cr.yaml")
+func Platform(namespace string, registry string) error {
+       isOpenshift, err := openshift.IsOpenShift()
+       if err != nil {
+               return err
+       }
+       if isOpenshift {
+               return Resource(namespace, "platform-cr.yaml")
+       }
+       platform, err := 
kubernetes.LoadResourceFromYaml(deploy.Resources["platform-cr.yaml"])
+       if err != nil {
+               return err
+       }
+       if pl, ok := platform.(*v1alpha1.IntegrationPlatform); !ok {
+               panic("cannot find integration platform template")
+       } else {
+               if registry == "" {
+                       // This operation should be done here in the installer
+                       // because the operator is not allowed to look into the 
"kube-system" namespace
+                       minishiftRegistry, err := minishift.FindRegistry()
+                       if err != nil {
+                               return err
+                       }
+                       if minishiftRegistry == nil {
+                               return errors.New("cannot find automatically a 
registry where to push images")
+                       }
+                       registry = *minishiftRegistry
+               }
+               pl.Spec.Build.Registry = registry
+               return RuntimeObject(namespace, pl)
+       }
 }
 
 // Example --
diff --git a/pkg/platform/build.go b/pkg/platform/build.go
index 994ced6..6e75ad2 100644
--- a/pkg/platform/build.go
+++ b/pkg/platform/build.go
@@ -23,6 +23,7 @@ import (
        "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
        "github.com/apache/camel-k/pkg/build"
        "github.com/apache/camel-k/pkg/build/assemble"
+       "github.com/apache/camel-k/pkg/build/packager"
        "github.com/apache/camel-k/pkg/build/publish"
        "github.com/operator-framework/operator-sdk/pkg/sdk"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -44,8 +45,13 @@ func GetPlatformBuildManager(ctx context.Context, namespace 
string) (*build.Mana
 
        assembler := assemble.NewMavenAssembler(ctx)
        if pl.Spec.Build.PublishStrategy == 
v1alpha1.IntegrationPlatformBuildPublishStrategyS2I {
-               publisher := publish.NewS2IIncrementalPublisher(ctx, namespace, 
newContextLister(namespace))
-               buildManager = build.NewManager(ctx, assembler, publisher)
+               packaging := packager.NewS2IIncrementalPackager(ctx, 
newContextLister(namespace))
+               publisher := publish.NewS2IPublisher(ctx, namespace)
+               buildManager = build.NewManager(ctx, assembler, packaging, 
publisher)
+       } else if pl.Spec.Build.PublishStrategy == 
v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko && 
pl.Spec.Build.Registry != "" {
+               packaging := packager.NewJavaStandardPackager(ctx)
+               publisher := publish.NewKanikoPublisher(ctx, namespace, 
pl.Spec.Build.Registry)
+               buildManager = build.NewManager(ctx, assembler, packaging, 
publisher)
        }
 
        if buildManager == nil {
@@ -66,14 +72,14 @@ func newContextLister(namespace string) contextLister {
        }
 }
 
-func (l contextLister) ListPublishedImages() ([]publish.PublishedImage, error) 
{
+func (l contextLister) ListPublishedImages() ([]packager.PublishedImage, 
error) {
        list := v1alpha1.NewIntegrationContextList()
 
        err := sdk.List(l.namespace, &list, 
sdk.WithListOptions(&metav1.ListOptions{}))
        if err != nil {
                return nil, err
        }
-       images := make([]publish.PublishedImage, 0)
+       images := make([]packager.PublishedImage, 0)
        for _, ctx := range list.Items {
                if ctx.Status.Phase != v1alpha1.IntegrationContextPhaseReady || 
ctx.Labels == nil {
                        continue
@@ -82,7 +88,7 @@ func (l contextLister) ListPublishedImages() 
([]publish.PublishedImage, error) {
                        continue
                }
 
-               images = append(images, publish.PublishedImage{
+               images = append(images, packager.PublishedImage{
                        Image:     ctx.Status.Image,
                        Classpath: ctx.Status.Classpath,
                })
diff --git a/pkg/stub/action/platform/initialize.go 
b/pkg/stub/action/platform/initialize.go
index 4215daa..07ea31f 100644
--- a/pkg/stub/action/platform/initialize.go
+++ b/pkg/stub/action/platform/initialize.go
@@ -18,12 +18,12 @@ limitations under the License.
 package platform
 
 import (
+       "errors"
        "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
        platformutils "github.com/apache/camel-k/pkg/platform"
-       "github.com/operator-framework/operator-sdk/pkg/k8sclient"
+       "github.com/apache/camel-k/pkg/util/openshift"
        "github.com/operator-framework/operator-sdk/pkg/sdk"
        "github.com/sirupsen/logrus"
-       "k8s.io/apimachinery/pkg/api/errors"
 )
 
 // NewInitializeAction returns a action that initializes the platform 
configuration when not provided by the user
@@ -63,9 +63,9 @@ func (action *initializeAction) Handle(platform 
*v1alpha1.IntegrationPlatform) e
        // update missing fields in the resource
        if target.Spec.Cluster == "" {
                // determine the kind of cluster the platform in installed into
-               if openshift, err := action.isOpenshift(); err != nil {
+               if isOpenshift, err := openshift.IsOpenShift(); err != nil {
                        return err
-               } else if openshift {
+               } else if isOpenshift {
                        target.Spec.Cluster = 
v1alpha1.IntegrationPlatformClusterOpenShift
                } else {
                        target.Spec.Cluster = 
v1alpha1.IntegrationPlatformClusterKubernetes
@@ -77,26 +77,19 @@ func (action *initializeAction) Handle(platform 
*v1alpha1.IntegrationPlatform) e
                        target.Spec.Build.PublishStrategy = 
v1alpha1.IntegrationPlatformBuildPublishStrategyS2I
                } else {
                        target.Spec.Build.PublishStrategy = 
v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko
-                       // TODO discover registry location
                }
        }
 
+       if target.Spec.Build.PublishStrategy == 
v1alpha1.IntegrationPlatformBuildPublishStrategyKaniko && 
target.Spec.Build.Registry == "" {
+               return errors.New("no registry specified for publishing images")
+       }
+
        // next status
        logrus.Info("Platform ", target.Name, " transitioning to state ", 
v1alpha1.IntegrationPlatformPhaseCreating)
        target.Status.Phase = v1alpha1.IntegrationPlatformPhaseCreating
        return sdk.Update(target)
 }
 
-func (action *initializeAction) isOpenshift() (bool, error) {
-       _, err := 
k8sclient.GetKubeClient().Discovery().ServerResourcesForGroupVersion("image.openshift.io/v1")
-       if err != nil && errors.IsNotFound(err) {
-               return false, nil
-       } else if err != nil {
-               return false, err
-       }
-       return true, nil
-}
-
 func (action *initializeAction) isDuplicate(thisPlatform 
*v1alpha1.IntegrationPlatform) (bool, error) {
        platforms, err := platformutils.ListPlatforms(thisPlatform.Namespace)
        if err != nil {
diff --git a/pkg/util/minishift/minishift.go b/pkg/util/minishift/minishift.go
new file mode 100644
index 0000000..9e5465c
--- /dev/null
+++ b/pkg/util/minishift/minishift.go
@@ -0,0 +1,60 @@
+/*
+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 minishift contains utilities for Minishift deployments
+package minishift
+
+import (
+       "github.com/operator-framework/operator-sdk/pkg/sdk"
+       "k8s.io/api/core/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "strconv"
+)
+
+const (
+       registryNamespace = "kube-system"
+)
+
+// FindRegistry returns the Minishift registry location if any
+func FindRegistry() (*string, error) {
+       svcs := v1.ServiceList{
+               TypeMeta: metav1.TypeMeta{
+                       APIVersion: v1.SchemeGroupVersion.String(),
+                       Kind:       "Service",
+               },
+       }
+       options := metav1.ListOptions{
+               LabelSelector: "kubernetes.io/minikube-addons=registry",
+       }
+       if err := sdk.List(registryNamespace, &svcs, 
sdk.WithListOptions(&options)); err != nil {
+               return nil, err
+       }
+       if len(svcs.Items) == 0 {
+               return nil, nil
+       }
+       svc := svcs.Items[0]
+       ip := svc.Spec.ClusterIP
+       portStr := ""
+       if len(svc.Spec.Ports) > 0 {
+               port := svc.Spec.Ports[0].Port
+               if port > 0 && port != 80 {
+                       portStr = ":" + strconv.FormatInt(int64(port), 10)
+               }
+       }
+       registry := ip + portStr
+       return &registry, nil
+}
diff --git a/pkg/install/operator.go b/pkg/util/openshift/openshift.go
similarity index 57%
copy from pkg/install/operator.go
copy to pkg/util/openshift/openshift.go
index eb3ac4c..f5b69b0 100644
--- a/pkg/install/operator.go
+++ b/pkg/util/openshift/openshift.go
@@ -15,27 +15,20 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package install
+package openshift
 
-// Operator --
-func Operator(namespace string) error {
-       return Resources(namespace,
-               "operator-service-account.yaml",
-               "operator-role-openshift.yaml", // TODO distinguish between 
Openshift and Kubernetes
-               "operator-role-binding.yaml",
-               "operator-deployment.yaml",
-               "operator-service.yaml",
-       )
-}
+import (
+       "github.com/operator-framework/operator-sdk/pkg/k8sclient"
+       "k8s.io/apimachinery/pkg/api/errors"
+)
 
-// Platform installs the platform custom resource
-func Platform(namespace string) error {
-       return Resource(namespace, "platform-cr.yaml")
-}
-
-// Example --
-func Example(namespace string) error {
-       return Resources(namespace,
-               "cr-example.yaml",
-       )
-}
+// IsOpenShift returns true if we are connected to a OpenShift cluster
+func IsOpenShift() (bool, error) {
+       _, err := 
k8sclient.GetKubeClient().Discovery().ServerResourcesForGroupVersion("image.openshift.io/v1")
+       if err != nil && errors.IsNotFound(err) {
+               return false, nil
+       } else if err != nil {
+               return false, err
+       }
+       return true, nil
+}
\ No newline at end of file

Reply via email to