Skip to content

Commit ad53177

Browse files
committed
feat: support image policy
Signed-off-by: HIHIA <[email protected]>
1 parent 91ba640 commit ad53177

File tree

5,894 files changed

+1610113
-131787
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

5,894 files changed

+1610113
-131787
lines changed

Diff for: cmd/sealer/cmd/cluster/run.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ func installApplication(appImageName string, envs []string, app *v2.Application,
392392
return loadToRegistry(infraDriver, distributor)
393393
}
394394

395-
installer := clusterruntime.NewAppInstaller(infraDriver, distributor, extension)
395+
installer := clusterruntime.NewAppInstaller(infraDriver, distributor, extension, imageEngine)
396396
err = installer.Install(infraDriver.GetHostIPListByRole(common.MASTER)[0], v2App.GetImageLaunchCmds())
397397
if err != nil {
398398
return err

Diff for: common/common.go

+66
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,72 @@ const (
127127
WINDOWS = "windows"
128128
)
129129

130+
const (
131+
ImagePolicyLabelKey = "app.alpha.sealer.io/image-policy-plugin"
132+
ImagePolicyPluginKyverno = "kyverno"
133+
ImagePolicyTemplateYamlName = "image-policy-template.yaml"
134+
ImagePolicyTemplate = `
135+
apiVersion : kyverno.io/v1
136+
kind: ClusterPolicy
137+
metadata:
138+
name: [[.name]]-redirect-registry
139+
namespace: kube-system
140+
spec:
141+
background: false
142+
rules:
143+
- name: prepend-registry-containers
144+
match:
145+
resources:
146+
kinds:
147+
- Pod
148+
preconditions:
149+
all:
150+
- key: "{{request.operation}}"
151+
operator: In
152+
value:
153+
- CREATE
154+
- UPDATE
155+
mutate:
156+
foreach:
157+
- list: "request.object.spec.containers"
158+
preconditions:
159+
all:
160+
- key: "{{element.image}}"
161+
operator: AnyIn
162+
value: [[.imageList]]
163+
patchStrategicMerge:
164+
spec:
165+
containers:
166+
- name: "{{ element.name }}"
167+
image: "[[.registry]]/{{ images.containers.{{element.name}}.path}}:{{images.containers.{{element.name}}.tag}}"
168+
- name: prepend-registry-initcontainers
169+
match:
170+
resources:
171+
kinds:
172+
- Pod
173+
preconditions:
174+
all:
175+
- key: "{{request.operation}}"
176+
operator: In
177+
value:
178+
- CREATE
179+
- UPDATE
180+
mutate:
181+
foreach:
182+
- list: "request.object.spec.initContainers"
183+
preconditions:
184+
all:
185+
- key: "{{element.image}}"
186+
operator: AnyIn
187+
value: [[.imageList]]
188+
patchStrategicMerge:
189+
spec:
190+
initContainers:
191+
- name: "{{ element.name }}"
192+
image: "[[.registry]]/{{ images.initContainers.{{element.name}}.path}}:{{images.initContainers.{{element.name}}.tag}}"
193+
`
194+
)
195+
130196
func GetSealerWorkDir() string {
131197
return filepath.Join(GetHomeDir(), ".sealer")
132198
}

Diff for: go.mod

+232-63
Large diffs are not rendered by default.

Diff for: go.sum

+2,147-81
Large diffs are not rendered by default.

Diff for: pkg/cluster-runtime/apps.go

+32-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ import (
2626
"github.com/sealerio/sealer/common"
2727
containerruntime "github.com/sealerio/sealer/pkg/container-runtime"
2828
v12 "github.com/sealerio/sealer/pkg/define/image/v1"
29+
"github.com/sealerio/sealer/pkg/define/options"
2930
"github.com/sealerio/sealer/pkg/imagedistributor"
31+
"github.com/sealerio/sealer/pkg/imageengine"
32+
"github.com/sealerio/sealer/pkg/imagepolicy/kyverno"
3033
"github.com/sealerio/sealer/pkg/infradriver"
3134
"github.com/sealerio/sealer/pkg/registry"
3235
"github.com/sirupsen/logrus"
@@ -36,13 +39,15 @@ type AppInstaller struct {
3639
infraDriver infradriver.InfraDriver
3740
distributor imagedistributor.Distributor
3841
extension v12.ImageExtension
42+
imageEngine imageengine.Interface
3943
}
4044

41-
func NewAppInstaller(infraDriver infradriver.InfraDriver, distributor imagedistributor.Distributor, extension v12.ImageExtension) AppInstaller {
45+
func NewAppInstaller(infraDriver infradriver.InfraDriver, distributor imagedistributor.Distributor, extension v12.ImageExtension, imageEngine imageengine.Interface) AppInstaller {
4246
return AppInstaller{
4347
infraDriver: infraDriver,
4448
distributor: distributor,
4549
extension: extension,
50+
imageEngine: imageEngine,
4651
}
4752
}
4853

@@ -90,6 +95,20 @@ func (i AppInstaller) Launch(master0 net.IP, launchCmds []string) error {
9095
ex = shell.NewLex('\\')
9196
)
9297

98+
var imagePolicyEngine *kyverno.Engine
99+
clusterImageName := i.infraDriver.GetClusterImageName()
100+
extension, err := i.imageEngine.GetSealerImageExtension(&options.GetImageAnnoOptions{ImageNameOrID: clusterImageName})
101+
if err != nil {
102+
return err
103+
}
104+
val, ok := extension.Labels[common.ImagePolicyLabelKey]
105+
if ok && val == common.ImagePolicyPluginKyverno {
106+
imagePolicyEngine, err = kyverno.NewKyvernoImagePolicyEngine()
107+
if err != nil {
108+
return err
109+
}
110+
}
111+
93112
for _, value := range launchCmds {
94113
if value == "" {
95114
continue
@@ -102,6 +121,18 @@ func (i AppInstaller) Launch(master0 net.IP, launchCmds []string) error {
102121
if err = i.infraDriver.CmdAsync(master0, fmt.Sprintf(common.CdAndExecCmd, rootfsPath, cmdline)); err != nil {
103122
return err
104123
}
124+
125+
if imagePolicyEngine != nil {
126+
ok, err := imagePolicyEngine.IsImagePolicyApp(cmdline)
127+
if err != nil {
128+
return err
129+
}
130+
if ok {
131+
if err := imagePolicyEngine.CreateImagePolicyRule(i.infraDriver, i.imageEngine, cmdline); err != nil {
132+
return err
133+
}
134+
}
135+
}
105136
}
106137

107138
return i.save(common.GetDefaultApplicationFile())

Diff for: pkg/cluster-runtime/installer.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ func (i *Installer) Install() error {
202202
return err
203203
}
204204

205-
appInstaller := NewAppInstaller(i.infraDriver, i.Distributor, extension)
205+
appInstaller := NewAppInstaller(i.infraDriver, i.Distributor, extension, i.ImageEngine)
206206

207207
v2App, err := application.NewV2Application(v2.ConstructApplication(i.Application, cmds, appNames), extension)
208208
if err != nil {

Diff for: pkg/imageengine/buildah/sealer_image_extension.go

+10
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ func GetImageExtensionFromAnnotations(annotations map[string]string) (image_v1.I
5151
return extension, nil
5252
}
5353

54+
func (engine *Engine) GetSealerContainerImageList(opts *options.GetImageAnnoOptions) ([]*image_v1.ContainerImage, error) {
55+
annotation, err := engine.GetImageAnnotation(opts)
56+
result, err := GetContainerImagesFromAnnotations(annotation)
57+
if err != nil {
58+
return []*image_v1.ContainerImage{}, errors.Wrapf(err, "failed to get %s in image %s", image_v1.SealerImageContainerImageList, opts.ImageNameOrID)
59+
}
60+
61+
return result, nil
62+
}
63+
5464
func GetContainerImagesFromAnnotations(annotations map[string]string) ([]*image_v1.ContainerImage, error) {
5565
var containerImageList []*image_v1.ContainerImage
5666
annotationStr := annotations[image_v1.SealerImageContainerImageList]

Diff for: pkg/imageengine/interface.go

+2
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ type Interface interface {
6161

6262
GetSealerImageExtension(opts *options.GetImageAnnoOptions) (v1.ImageExtension, error)
6363

64+
GetSealerContainerImageList(opts *options.GetImageAnnoOptions) ([]*v1.ContainerImage, error)
65+
6466
LookupManifest(name string) (*libimage.ManifestList, error)
6567

6668
CreateManifest(name string, opts *options.ManifestCreateOpts) error

Diff for: pkg/imagepolicy/common.go

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright © 2023 Alibaba Group Holding Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package imagepolicy
16+
17+
import "github.com/sealerio/sealer/pkg/imagepolicy/kyverno"
18+
19+
func NewImagePolicyEngine() (Interface, error) {
20+
return kyverno.NewKyvernoImagePolicyEngine()
21+
}

Diff for: pkg/imagepolicy/interface.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright © 2023 Alibaba Group Holding Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package imagepolicy
16+
17+
import (
18+
"github.com/sealerio/sealer/pkg/imageengine"
19+
"github.com/sealerio/sealer/pkg/infradriver"
20+
)
21+
22+
type Interface interface {
23+
IsImagePolicyApp(appName string) (bool, error)
24+
25+
CreateImagePolicyRule(infraDriver infradriver.InfraDriver, imageEngine imageengine.Interface, appName string) error
26+
}

Diff for: pkg/imagepolicy/kyverno/engine.go

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright © 2023 Alibaba Group Holding Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package kyverno
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"io/ioutil"
21+
"os"
22+
"path/filepath"
23+
"strings"
24+
25+
apierrors "k8s.io/apimachinery/pkg/api/errors"
26+
"k8s.io/apimachinery/pkg/runtime/serializer"
27+
runtimeClient "sigs.k8s.io/controller-runtime/pkg/client"
28+
29+
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
30+
"github.com/sealerio/sealer/common"
31+
imagecommon "github.com/sealerio/sealer/pkg/define/options"
32+
"github.com/sealerio/sealer/pkg/imageengine"
33+
"github.com/sealerio/sealer/pkg/infradriver"
34+
"github.com/sealerio/sealer/pkg/rootfs"
35+
"github.com/sealerio/sealer/pkg/runtime"
36+
k "github.com/sealerio/sealer/pkg/runtime/kubernetes"
37+
apimachineryRuntime "k8s.io/apimachinery/pkg/runtime"
38+
"k8s.io/client-go/kubernetes/scheme"
39+
)
40+
41+
type Engine struct {
42+
driver runtime.Driver
43+
}
44+
45+
func NewKyvernoImagePolicyEngine() (*Engine, error) {
46+
driver, err := k.NewKubeDriver(k.AdminKubeConfPath)
47+
if err != nil {
48+
return nil, err
49+
}
50+
51+
return &Engine{
52+
driver: driver,
53+
}, nil
54+
}
55+
56+
// TODO: make it configurable
57+
func (engine *Engine) IsImagePolicyApp(appName string) (bool, error) {
58+
return appName == "kyverno", nil
59+
}
60+
61+
func (engine *Engine) CreateImagePolicyRule(infraDriver infradriver.InfraDriver, imageEngine imageengine.Interface, appName string) error {
62+
imagePolicyTemplateStr := common.ImagePolicyTemplate
63+
templatePath := filepath.Clean(filepath.Join(infraDriver.GetClusterRootfsPath(), rootfs.GlobalManager.App().Root(), appName, common.ImagePolicyTemplateYamlName))
64+
if _, err := os.Stat(templatePath); err != nil {
65+
if !os.IsNotExist(err) {
66+
return err
67+
}
68+
} else {
69+
yamlFile, err := ioutil.ReadFile(templatePath)
70+
if err != nil {
71+
return err
72+
}
73+
imagePolicyTemplateStr = string(yamlFile)
74+
}
75+
76+
clusterImageName := infraDriver.GetClusterImageName()
77+
imageList, err := imageEngine.GetSealerContainerImageList(&imagecommon.GetImageAnnoOptions{ImageNameOrID: clusterImageName})
78+
clusterPolicy := &kyvernov1.ClusterPolicy{}
79+
80+
var imageListArray []string
81+
for _, containerImage := range imageList {
82+
imageListArray = append(imageListArray, "\""+containerImage.Image+"\"")
83+
}
84+
85+
ctx := map[string]string{
86+
"name": clusterImageName,
87+
"imageList": "[" + strings.Join(imageListArray, ",") + "]",
88+
"registry": infraDriver.GetClusterRegistry().LocalRegistry.Domain,
89+
}
90+
imagePolicyTemplate, err := SubsituteTemplate(imagePolicyTemplateStr, ctx)
91+
if err != nil {
92+
return err
93+
}
94+
95+
if err := kyvernov1.AddToScheme(scheme.Scheme); err != nil {
96+
return err
97+
}
98+
decoder := serializer.NewCodecFactory(scheme.Scheme).UniversalDecoder()
99+
if err := apimachineryRuntime.DecodeInto(decoder, []byte(imagePolicyTemplate), clusterPolicy); err != nil {
100+
return err
101+
}
102+
103+
if err := engine.driver.Create(context.Background(), clusterPolicy, &runtimeClient.CreateOptions{}); err != nil {
104+
if !apierrors.IsAlreadyExists(err) {
105+
return fmt.Errorf("unable to create image policy: %v", err)
106+
}
107+
108+
if err := engine.driver.Update(context.Background(), clusterPolicy, &runtimeClient.UpdateOptions{}); err != nil {
109+
return fmt.Errorf("unable to update image policy: %v", err)
110+
}
111+
}
112+
return nil
113+
}

Diff for: pkg/imagepolicy/kyverno/util.go

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright © 2023 Alibaba Group Holding Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package kyverno
16+
17+
import (
18+
"bytes"
19+
"text/template"
20+
)
21+
22+
// SubsituteTemplate fills out the templates based on the context
23+
func SubsituteTemplate(tmpl string, context interface{}) (string, error) {
24+
// Use [[]] as identifier to avoid conflicts
25+
t, tmplPrsErr := template.New("test").Delims("[[", "]]").Option("missingkey=zero").Parse(tmpl)
26+
if tmplPrsErr != nil {
27+
return "", tmplPrsErr
28+
}
29+
writer := bytes.NewBuffer([]byte{})
30+
if err := t.Execute(writer, context); nil != err {
31+
return "", err
32+
}
33+
34+
return writer.String(), nil
35+
}

0 commit comments

Comments
 (0)