Skip to content

Commit c4d81e5

Browse files
authored
create work command (#134)
Signed-off-by: Jian Qiu <[email protected]>
1 parent 6a2b682 commit c4d81e5

File tree

4 files changed

+213
-0
lines changed

4 files changed

+213
-0
lines changed

pkg/cmd/create/cmd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"github.com/spf13/cobra"
66
"k8s.io/cli-runtime/pkg/genericclioptions"
77
"open-cluster-management.io/clusteradm/pkg/cmd/create/clusterset"
8+
"open-cluster-management.io/clusteradm/pkg/cmd/create/work"
89
genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions"
910
)
1011

@@ -16,6 +17,7 @@ func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, stream
1617
}
1718

1819
cmd.AddCommand(clusterset.NewCmd(clusteradmFlags, streams))
20+
cmd.AddCommand(work.NewCmd(clusteradmFlags, streams))
1921

2022
return cmd
2123
}

pkg/cmd/create/work/cmd.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright Contributors to the Open Cluster Management project
2+
package work
3+
4+
import (
5+
"fmt"
6+
7+
genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions"
8+
clusteradmhelpers "open-cluster-management.io/clusteradm/pkg/helpers"
9+
10+
"github.com/spf13/cobra"
11+
"k8s.io/cli-runtime/pkg/genericclioptions"
12+
)
13+
14+
var example = `
15+
# create a work.
16+
%[1]s create work work-example -f xxx.yaml --cluster cluster1
17+
`
18+
19+
// NewCmd...
20+
func NewCmd(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *cobra.Command {
21+
o := newOptions(clusteradmFlags, streams)
22+
23+
cmd := &cobra.Command{
24+
Use: "work",
25+
Short: "create a work",
26+
Example: fmt.Sprintf(example, clusteradmhelpers.GetExampleHeader()),
27+
SilenceUsage: true,
28+
PreRunE: func(c *cobra.Command, args []string) error {
29+
clusteradmhelpers.DryRunMessage(clusteradmFlags.DryRun)
30+
31+
return nil
32+
},
33+
RunE: func(c *cobra.Command, args []string) error {
34+
if err := o.complete(c, args); err != nil {
35+
return err
36+
}
37+
if err := o.validate(); err != nil {
38+
return err
39+
}
40+
if err := o.run(); err != nil {
41+
return err
42+
}
43+
44+
return nil
45+
},
46+
}
47+
48+
cmd.Flags().StringVar(&o.Cluster, "cluster", "", "Names of the managed cluster to apply work")
49+
cmd.Flags().BoolVar(&o.Overwrite, "overwrite", false, "Overwrite the existing work if it exists already")
50+
o.FileNameFlags.AddFlags(cmd.Flags())
51+
52+
return cmd
53+
}

pkg/cmd/create/work/exec.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// Copyright Contributors to the Open Cluster Management project
2+
package work
3+
4+
import (
5+
"context"
6+
"fmt"
7+
8+
"github.com/spf13/cobra"
9+
"k8s.io/apimachinery/pkg/api/errors"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"k8s.io/apimachinery/pkg/runtime"
12+
"k8s.io/cli-runtime/pkg/resource"
13+
workclientset "open-cluster-management.io/api/client/work/clientset/versioned"
14+
workapiv1 "open-cluster-management.io/api/work/v1"
15+
)
16+
17+
func (o *Options) complete(cmd *cobra.Command, args []string) (err error) {
18+
if len(args) == 0 {
19+
return fmt.Errorf("work name must be specified")
20+
}
21+
22+
if len(args) > 1 {
23+
return fmt.Errorf("only one work name can be specified")
24+
}
25+
26+
o.Workname = args[0]
27+
28+
return nil
29+
}
30+
31+
func (o *Options) validate() (err error) {
32+
if len(o.Cluster) == 0 {
33+
return fmt.Errorf("the name of the cluster must be specified")
34+
}
35+
if len(*o.FileNameFlags.Filenames) == 0 {
36+
return fmt.Errorf("manifest files must be specified")
37+
}
38+
return nil
39+
}
40+
41+
func (o *Options) run() (err error) {
42+
restConfig, err := o.ClusteradmFlags.KubectlFactory.ToRESTConfig()
43+
if err != nil {
44+
return err
45+
}
46+
workClient, err := workclientset.NewForConfig(restConfig)
47+
if err != nil {
48+
return err
49+
}
50+
51+
manifests, err := o.readManifests()
52+
if err != nil {
53+
return err
54+
}
55+
56+
err = o.applyWork(workClient, manifests)
57+
if err != nil {
58+
return err
59+
}
60+
61+
fmt.Fprintf(o.Streams.Out, "work %s in cluster %s is created\n", o.Workname, o.Cluster)
62+
return
63+
}
64+
65+
func (o *Options) readManifests() ([]workapiv1.Manifest, error) {
66+
opt := o.FileNameFlags.ToOptions()
67+
builder := resource.NewLocalBuilder().
68+
Unstructured().
69+
FilenameParam(false, &opt).
70+
Flatten().
71+
ContinueOnError()
72+
result := builder.Do()
73+
74+
if err := result.Err(); err != nil {
75+
return nil, err
76+
}
77+
78+
manifests := []workapiv1.Manifest{}
79+
80+
items, err := result.Infos()
81+
if err != nil {
82+
return nil, err
83+
}
84+
for _, item := range items {
85+
manifests = append(manifests, workapiv1.Manifest{RawExtension: runtime.RawExtension{Object: item.Object}})
86+
}
87+
88+
return manifests, nil
89+
}
90+
91+
func (o *Options) applyWork(workClient workclientset.Interface, manifests []workapiv1.Manifest) error {
92+
work, err := workClient.WorkV1().ManifestWorks(o.Cluster).Get(context.TODO(), o.Workname, metav1.GetOptions{})
93+
94+
switch {
95+
case errors.IsNotFound(err):
96+
work = &workapiv1.ManifestWork{
97+
ObjectMeta: metav1.ObjectMeta{
98+
Name: o.Workname,
99+
Namespace: o.Cluster,
100+
},
101+
Spec: workapiv1.ManifestWorkSpec{
102+
Workload: workapiv1.ManifestsTemplate{
103+
Manifests: manifests,
104+
},
105+
},
106+
}
107+
_, createErr := workClient.WorkV1().ManifestWorks(o.Cluster).Create(context.TODO(), work, metav1.CreateOptions{})
108+
return createErr
109+
case err != nil:
110+
return err
111+
}
112+
113+
if !o.Overwrite {
114+
return fmt.Errorf("work %s in cluster %s already exists", o.Workname, o.Cluster)
115+
}
116+
117+
work.Spec.Workload.Manifests = manifests
118+
_, err = workClient.WorkV1().ManifestWorks(o.Cluster).Update(context.TODO(), work, metav1.UpdateOptions{})
119+
return err
120+
}

pkg/cmd/create/work/options.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright Contributors to the Open Cluster Management project
2+
package work
3+
4+
import (
5+
"k8s.io/cli-runtime/pkg/genericclioptions"
6+
genericclioptionsclusteradm "open-cluster-management.io/clusteradm/pkg/genericclioptions"
7+
)
8+
9+
type Options struct {
10+
//ClusteradmFlags: The generic optiosn from the clusteradm cli-runtime.
11+
ClusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags
12+
13+
Streams genericclioptions.IOStreams
14+
15+
Cluster string
16+
17+
Workname string
18+
19+
FileNameFlags genericclioptions.FileNameFlags
20+
21+
Overwrite bool
22+
}
23+
24+
func newOptions(clusteradmFlags *genericclioptionsclusteradm.ClusteradmFlags, streams genericclioptions.IOStreams) *Options {
25+
return &Options{
26+
ClusteradmFlags: clusteradmFlags,
27+
Streams: streams,
28+
Cluster: "",
29+
FileNameFlags: genericclioptions.FileNameFlags{
30+
Filenames: &[]string{},
31+
Recursive: boolPtr(true),
32+
},
33+
}
34+
}
35+
36+
func boolPtr(val bool) *bool {
37+
return &val
38+
}

0 commit comments

Comments
 (0)