Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@ var (
Short: "Run Cluster Version Controller",
Long: "",
}

rootOpts struct {
releaseImage string
}
)

func init() {
rootCmd.PersistentFlags().AddGoFlagSet(flag.CommandLine)
rootCmd.PersistentFlags().StringVar(&rootOpts.releaseImage, "release-image", "", "The Openshift release image url.")
}

func main() {
Expand Down
43 changes: 43 additions & 0 deletions cmd/render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"flag"

"github.com/golang/glog"
"github.com/spf13/cobra"

"github.com/openshift/cluster-version-operator/pkg/cvo"
)

var (
renderCmd = &cobra.Command{
Use: "render",
Short: "Renders the UpdatePayload to disk.",
Long: "",
Run: runRenderCmd,
}

renderOpts struct {
outputDir string
}
)

func init() {
rootCmd.AddCommand(renderCmd)
renderCmd.PersistentFlags().StringVar(&renderOpts.outputDir, "output-dir", "", "The output directory where the manifests will be rendered.")
}

func runRenderCmd(cmd *cobra.Command, args []string) {
flag.Set("logtostderr", "true")
flag.Parse()

if renderOpts.outputDir == "" {
glog.Fatalf("missing --output-dir flag, it is required")
}
if rootOpts.releaseImage == "" {
glog.Fatalf("missing --release-image flag, it is required")
}
if err := cvo.Render(renderOpts.outputDir, rootOpts.releaseImage); err != nil {
glog.Fatalf("Render command failed: %v", err)
}
}
5 changes: 5 additions & 0 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ func runStartCmd(cmd *cobra.Command, args []string) {
startOpts.nodeName = name
}

if rootOpts.releaseImage == "" {
glog.Fatalf("missing --release-image flag, it is required")
}

cb, err := newClientBuilder(startOpts.kubeconfig)
if err != nil {
glog.Fatalf("error creating clients: %v", err)
Expand Down Expand Up @@ -226,6 +230,7 @@ func startControllers(ctx *controllerContext) error {
go cvo.New(
startOpts.nodeName,
componentNamespace, componentName,
rootOpts.releaseImage,
ctx.InformerFactory.Clusterversion().V1().CVOConfigs(),
ctx.InformerFactory.Operatorstatus().V1().OperatorStatuses(),
ctx.APIExtInformerFactory.Apiextensions().V1beta1().CustomResourceDefinitions(),
Expand Down
3 changes: 2 additions & 1 deletion install/00_clusterversionoperator_04_deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ spec:
spec:
containers:
- name: cluster-version-operator
image: docker.io/origin/origin-cluster-version-operator:v4.0.0
image: {{.ReleaseImage}}
imagePullPolicy: Always
args:
- "start"
- "--release-image={{.ReleaseImage}}"
- "--enable-auto-update=false"
- "--v=4"
volumeMounts:
Expand Down
6 changes: 6 additions & 0 deletions lib/resourcebuilder/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ func (rm *ResourceMapper) AddToMap(irm *ResourceMapper) {
}
}

// Exist returns true when gvk is known.
func (rm *ResourceMapper) Exists(gvk schema.GroupVersionKind) bool {
_, ok := rm.gvkToNew[gvk]
return ok
}

// RegisterGVK adds GVK to NewInteraceFunc mapping.
// It does not lock before adding the mapping.
func (rm *ResourceMapper) RegisterGVK(gvk schema.GroupVersionKind, f NewInteraceFunc) {
Expand Down
10 changes: 9 additions & 1 deletion pkg/cvo/cvo.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ type Operator struct {
nodename string
// namespace and name are used to find the CVOConfig, OperatorStatus.
namespace, name string
// releaseImage allows templating CVO deployment manifest.
releaseImage string

// restConfig is used to create resourcebuilder.
restConfig *rest.Config
Expand Down Expand Up @@ -79,6 +81,7 @@ type Operator struct {
func New(
nodename string,
namespace, name string,
releaseImage string,
cvoConfigInformer cvinformersv1.CVOConfigInformer,
operatorStatusInformer osinformersv1.OperatorStatusInformer,
crdInformer apiextinformersv1beta1.CustomResourceDefinitionInformer,
Expand All @@ -96,6 +99,7 @@ func New(
nodename: nodename,
namespace: namespace,
name: name,
releaseImage: releaseImage,
restConfig: restConfig,
client: client,
kubeClient: kubeClient,
Expand Down Expand Up @@ -224,7 +228,11 @@ func (optr *Operator) sync(key string) error {
if err != nil {
return err
}
payload, err := loadUpdatePayload(payloadDir)
releaseImage := optr.releaseImage
if config.DesiredUpdate.Payload != "" {
releaseImage = config.DesiredUpdate.Payload
}
payload, err := loadUpdatePayload(payloadDir, releaseImage)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cvo/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "fmt"
// ImageForShortName returns the image using the updatepayload embedded in
// the Operator.
func ImageForShortName(name string) (string, error) {
up, err := loadUpdatePayload(defaultUpdatePayloadDir)
up, err := loadUpdatePayload(defaultUpdatePayloadDir, "")
if err != nil {
return "", fmt.Errorf("error loading update payload from %q: %v", defaultUpdatePayloadDir, err)
}
Expand Down
81 changes: 81 additions & 0 deletions pkg/cvo/render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package cvo

import (
"bytes"
"fmt"
"io/ioutil"
"path/filepath"
"text/template"

"github.com/openshift/cluster-version-operator/lib/resourcebuilder"

"github.com/golang/glog"
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
"k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors"

osv1 "github.com/openshift/cluster-version-operator/pkg/apis/operatorstatus.openshift.io/v1"
)

// Render renders all the manifests from updatepayload to outputDir.
// Render skips Jobs, OperatorStatus.
func Render(outputDir, releaseImage string) error {
up, err := loadUpdatePayload(defaultUpdatePayloadDir, releaseImage)
if err != nil {
return fmt.Errorf("error loading update payload from %q: %v", defaultUpdatePayloadDir, err)
}

var errs []error
skipGVKs := []schema.GroupVersionKind{
batchv1.SchemeGroupVersion.WithKind("Job"), batchv1beta1.SchemeGroupVersion.WithKind("Job"),
osv1.SchemeGroupVersion.WithKind("OperatorStatus"),
}
for idx, manifest := range up.manifests {
mname := fmt.Sprintf("(%s) %s/%s", manifest.GVK, manifest.Object().GetNamespace(), manifest.Object().GetName())
skip := false
for _, gvk := range skipGVKs {
if gvk == manifest.GVK {
skip = true
}
}
if skip {
glog.Infof("skipping Manifest %s", mname)
continue
}

if !resourcebuilder.Mapper.Exists(manifest.GVK) {
return fmt.Errorf("error: Unknown GVK: %v; Operator will not be able to manage this Manifest %s", manifest.GVK, mname)
}

path := filepath.Join(outputDir, fmt.Sprintf("%03d-manifest.json", idx))
if err := ioutil.WriteFile(path, manifest.Raw, 0644); err != nil {
errs = append(errs, err)
}
}

agg := utilerrors.NewAggregate(errs)
if agg != nil {
return fmt.Errorf("error rendering from UpdatePayload: %v", agg.Error())
}
return nil
}

type manifestRenderConfig struct {
ReleaseImage string
}

// renderManifest Executes go text template from `manifestBytes` with `config`.
func renderManifest(config manifestRenderConfig, manifestBytes []byte) ([]byte, error) {
tmpl, err := template.New("manifest").Parse(string(manifestBytes))
if err != nil {
return nil, fmt.Errorf("failed to parse manifest: %v", err)
}

buf := new(bytes.Buffer)
if err := tmpl.Execute(buf, config); err != nil {
return nil, fmt.Errorf("failed to execute template: %v", err)
}

return buf.Bytes(), nil
}
12 changes: 11 additions & 1 deletion pkg/cvo/updatepayload.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const (
imageReferencesFile = "image-references"
)

func loadUpdatePayload(dir string) (*updatePayload, error) {
func loadUpdatePayload(dir, releaseImage string) (*updatePayload, error) {
glog.V(4).Info("Loading updatepayload from %q", dir)
if err := validateUpdatePayload(dir); err != nil {
return nil, err
Expand Down Expand Up @@ -75,6 +75,16 @@ func loadUpdatePayload(dir string) (*updatePayload, error) {
return nil, err
}

mrc := manifestRenderConfig{ReleaseImage: releaseImage}
for idx := range manifests {
mname := fmt.Sprintf("(%s) %s/%s", manifests[idx].GVK.String(), manifests[idx].Object().GetNamespace(), manifests[idx].Object().GetName())
rendered, err := renderManifest(mrc, manifests[idx].Raw)
if err != nil {
return nil, fmt.Errorf("error when rendering Manifest %s: %v", mname, err)
}
manifests[idx].Raw = rendered
}

return &updatePayload{
imageRef: imageRef,
manifests: manifests,
Expand Down