Skip to content

Commit

Permalink
feature: enrich kubefile (#1980)
Browse files Browse the repository at this point in the history
Signed-off-by: yuxing.lyx <[email protected]>

Signed-off-by: yuxing.lyx <[email protected]>
  • Loading branch information
starnop authored Jan 16, 2023
1 parent b74d867 commit 91ba640
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 34 deletions.
53 changes: 38 additions & 15 deletions build/kubefile/command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,28 @@

package command

import (
"fmt"
)

const (

// the following commands are kubefile-spec commands
// we recommender users to use these commands to pack their
// demands for applications.
App = "app"
Launch = "launch"
Cmds = "cmds"
Cmd = "cmd" // Deprecated
App = "app"
Launch = "launch"
Cmds = "cmds"
CNI = "cni"
CSI = "csi"
KUBEVERSION = "kubeversion"

Label = "label"
Maintainer = "maintainer"

// Deprecated
Cmd = "cmd"

// the following commands are the intenal implementations for kube commands
Add = "add"
Arg = "arg"
Expand All @@ -35,17 +44,31 @@ const (
Run = "run"
)

const (
LabelSupportedKubeVersionAlpha = "app.alpha.sealer.io/supported-kube-version"
LabelSupportedKubeCNIAlpha = "cluster.alpha.sealer.io/kube-cni"
LabelSupportedKubeCSIAlpha = "cluster.alpha.sealer.io/kube-csi"
)

var (
LabelKubeCNIPrefix = fmt.Sprintf("%s-", LabelSupportedKubeCNIAlpha)
LabelKubeCSIPrefix = fmt.Sprintf("%s-", LabelSupportedKubeCSIAlpha)
)

// SupportedCommands is list of all Kubefile commands
var SupportedCommands = map[string]struct{}{
Add: {},
Arg: {},
Copy: {},
From: {},
Label: {},
Maintainer: {},
Run: {},
App: {},
Launch: {},
Cmds: {},
Cmd: {},
Add: {},
Arg: {},
Copy: {},
From: {},
Label: {},
Maintainer: {},
Run: {},
App: {},
Launch: {},
Cmds: {},
Cmd: {},
CNI: {},
CSI: {},
KUBEVERSION: {},
}
15 changes: 8 additions & 7 deletions build/kubefile/parser/app_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ import (

"github.com/sealerio/sealer/build/kubefile/command"
v1 "github.com/sealerio/sealer/pkg/define/application/v1"
"github.com/sealerio/sealer/pkg/define/application/version"
)

func (kp *KubefileParser) processApp(node *Node, result *KubefileResult) error {
func (kp *KubefileParser) processApp(node *Node, result *KubefileResult) (version.VersionedApplication, error) {
var (
appName = ""
localFiles = []string{}
Expand All @@ -50,12 +51,12 @@ func (kp *KubefileParser) processApp(node *Node, result *KubefileResult) error {
case isRemote(val):
remoteFiles = append(remoteFiles, val)
default:
return errors.New("source schema should be specified with https:// http:// local:// in APP")
return nil, errors.New("source schema should be specified with https:// http:// local:// in APP")
}
}

if appName == "" {
return errors.New("app name should be specified in the app cmd")
return nil, errors.New("app name should be specified in the app cmd")
}

// TODO clean the app directory first before putting files into it.
Expand All @@ -71,12 +72,12 @@ func (kp *KubefileParser) processApp(node *Node, result *KubefileResult) error {
if len(remoteFiles) > 0 {
remoteCxtAbs, err := os.MkdirTemp(kp.buildContext, "sealer-remote-files")
if err != nil {
return errors.Errorf("failed to create remote context: %s", err)
return nil, errors.Errorf("failed to create remote context: %s", err)
}

files, err := downloadRemoteFiles(remoteCxtAbs, remoteFiles)
if err != nil {
return err
return nil, err
}

remoteCxtBase := filepath.Base(remoteCxtAbs)
Expand All @@ -97,7 +98,7 @@ func (kp *KubefileParser) processApp(node *Node, result *KubefileResult) error {
result.legacyContext.apps2Files[appName] = append([]string{}, filesToCopy...)
appType, launchFiles, err := getApplicationType(filesToCopy)
if err != nil {
return fmt.Errorf("error in judging the application type: %v", err)
return nil, fmt.Errorf("error in judging the application type: %v", err)
}

v1App := v1.NewV1Application(
Expand All @@ -106,7 +107,7 @@ func (kp *KubefileParser) processApp(node *Node, result *KubefileResult) error {
launchFiles,
).(*v1.Application)
result.Applications[v1App.Name()] = v1App
return nil
return v1App, nil
}

func downloadRemoteFiles(shadowDir string, files []string) ([]string, error) {
Expand Down
37 changes: 36 additions & 1 deletion build/kubefile/parser/kubefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"io"
"os"
"path/filepath"
"strconv"
"strings"

"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -161,7 +162,14 @@ func (kp *KubefileParser) processOnCmd(result *KubefileResult, node *Node) error
result.Dockerfile = mergeLines(result.Dockerfile, node.Original)
return nil
case command.App:
return kp.processApp(node, result)
_, err := kp.processApp(node, result)
return err
case command.CNI:
return kp.processCNI(node, result)
case command.CSI:
return kp.processCSI(node, result)
case command.KUBEVERSION:
return kp.processKubeVersion(node, result)
case command.Launch:
return kp.processLaunch(node, result)
case command.Cmds:
Expand All @@ -173,6 +181,33 @@ func (kp *KubefileParser) processOnCmd(result *KubefileResult, node *Node) error
}
}

func (kp *KubefileParser) processCNI(node *Node, result *KubefileResult) error {
app, err := kp.processApp(node, result)
if err != nil {
return err
}
dockerFileInstruction := fmt.Sprintf(`LABEL %s%s="true"`, command.LabelKubeCNIPrefix, app.Name())
result.Dockerfile = mergeLines(result.Dockerfile, dockerFileInstruction)
return nil
}

func (kp *KubefileParser) processCSI(node *Node, result *KubefileResult) error {
app, err := kp.processApp(node, result)
if err != nil {
return err
}
dockerFileInstruction := fmt.Sprintf(`LABEL %s%s="true"`, command.LabelKubeCSIPrefix, app.Name())
result.Dockerfile = mergeLines(result.Dockerfile, dockerFileInstruction)
return nil
}

func (kp *KubefileParser) processKubeVersion(node *Node, result *KubefileResult) error {
kubeVersionValue := node.Next.Value
dockerFileInstruction := fmt.Sprintf(`LABEL %s=%s`, command.LabelSupportedKubeVersionAlpha, strconv.Quote(kubeVersionValue))
result.Dockerfile = mergeLines(result.Dockerfile, dockerFileInstruction)
return nil
}

func (kp *KubefileParser) processCmd(node *Node, result *KubefileResult) error {
original := node.Original
cmd := strings.Split(original, "CMD ")
Expand Down
25 changes: 14 additions & 11 deletions build/kubefile/parser/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,17 +205,20 @@ func init() {
// functions. Errors are propagated up by Parse() and the resulting AST can
// be incorporated directly into the existing AST as a next.
dispatch = map[string]func(string, *Directive) (*Node, map[string]bool, error){
command.Add: parseMaybeJSONToList,
command.Arg: parseNameOrNameVal,
command.Copy: parseMaybeJSONToList,
command.From: parseStringsWhitespaceDelimited,
command.Label: parseLabel,
command.Maintainer: parseString,
command.Run: parseMaybeJSON,
command.App: parseMaybeJSONToList,
command.Launch: parseMaybeJSONToList,
command.Cmds: parseMaybeJSONToList,
command.Cmd: parseMaybeJSONToList,
command.Add: parseMaybeJSONToList,
command.Arg: parseNameOrNameVal,
command.Copy: parseMaybeJSONToList,
command.From: parseStringsWhitespaceDelimited,
command.Label: parseLabel,
command.Maintainer: parseString,
command.Run: parseMaybeJSON,
command.App: parseMaybeJSONToList,
command.KUBEVERSION: parseString,
command.CNI: parseMaybeJSONToList,
command.CSI: parseMaybeJSONToList,
command.Launch: parseMaybeJSONToList,
command.Cmds: parseMaybeJSONToList,
command.Cmd: parseMaybeJSONToList,
}
}

Expand Down
6 changes: 6 additions & 0 deletions pkg/define/image/v1/sealer_image_extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ type ImageExtension struct {
// applications in the sealer image
Applications []version.VersionedApplication `json:"applications,omitempty"`

// Labels are metadata to the sealer image
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`

// launch spec will declare
Launch Launch `json:"launch,omitempty"`
}
Expand All @@ -108,6 +111,8 @@ type v1ImageExtension struct {
SchemaVersion string `json:"schemaVersion"`
// sealer image type, like AppImage
Type string `json:"type,omitempty"`
// Labels are metadata to the sealer image
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
// applications in the sealer image
Applications []application_v1.Application `json:"applications,omitempty"`
// launch spec will declare
Expand All @@ -123,6 +128,7 @@ func (ie *ImageExtension) UnmarshalJSON(data []byte) error {

(*ie).BuildClient = v1Ex.BuildClient
(*ie).SchemaVersion = v1Ex.SchemaVersion
(*ie).Labels = v1Ex.Labels
(*ie).Type = v1Ex.Type
(*ie).Applications = make([]version.VersionedApplication, len(v1Ex.Applications))
for i, app := range v1Ex.Applications {
Expand Down
38 changes: 38 additions & 0 deletions pkg/imageengine/buildah/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ import (
"fmt"
"os"
"regexp"
"strings"
"text/template"

"github.com/sealerio/sealer/build/kubefile/command"
image_v1 "github.com/sealerio/sealer/pkg/define/image/v1"
"github.com/sealerio/sealer/pkg/define/options"

Expand Down Expand Up @@ -69,6 +71,10 @@ func (engine *Engine) Inspect(opts *options.InspectOptions) error {
if err != nil {
return errors.Wrapf(err, "failed to get %s in image %s", image_v1.SealerImageExtension, opts.ImageNameOrID)
}
imageExtension.Labels = handleImageLabelOutput(builderInfo.OCIv1.Config.Labels)
// NOTE: avoid duplicate content output
builderInfo.OCIv1.Config.Labels = nil

containerImageList, err := GetContainerImagesFromAnnotations(builderInfo.ImageAnnotations)
if err != nil {
return errors.Wrapf(err, "failed to get %s in image %s", image_v1.SealerImageContainerImageList, opts.ImageNameOrID)
Expand Down Expand Up @@ -110,3 +116,35 @@ func (engine *Engine) Inspect(opts *options.InspectOptions) error {
}
return enc.Encode(result)
}

func handleImageLabelOutput(labels map[string]string) map[string]string {
if len(labels) == 0 {
return labels
}

var result = make(map[string]string)
var supportedCNI []string
var supportedCSI []string
for k, v := range labels {
if strings.HasPrefix(k, command.LabelKubeCNIPrefix) {
supportedCNI = append(supportedCNI, strings.TrimPrefix(k, command.LabelKubeCNIPrefix))
continue
}
if strings.HasPrefix(k, command.LabelKubeCSIPrefix) {
supportedCSI = append(supportedCSI, strings.TrimPrefix(k, command.LabelKubeCSIPrefix))
continue
}
result[k] = v
}

if len(supportedCNI) != 0 {
supportedCNIJSON, _ := json.Marshal(supportedCNI)
result[command.LabelSupportedKubeCNIAlpha] = string(supportedCNIJSON)
}
if len(supportedCSI) != 0 {
supportedCSIJSON, _ := json.Marshal(supportedCSI)
result[command.LabelSupportedKubeCSIAlpha] = string(supportedCSIJSON)
}

return result
}
65 changes: 65 additions & 0 deletions pkg/imageengine/buildah/inspect_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright © 2023 Alibaba Group Holding Ltd.
//
// Licensed 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 buildah

import (
"fmt"
"reflect"
"testing"

"github.com/sealerio/sealer/build/kubefile/command"
)

func Test_handleLabelOutput(t *testing.T) {
type args struct {
labels map[string]string
}
tests := []struct {
name string
args args
want map[string]string
}{
{
name: "",
args: args{
labels: map[string]string{
fmt.Sprintf("%s%s", command.LabelKubeCNIPrefix, "calico"): "true",
fmt.Sprintf("%s%s", command.LabelKubeCNIPrefix, "flannel"): "true",
fmt.Sprintf("%s%s", command.LabelKubeCSIPrefix, "alibaba-csi"): "true",
"key1": "value1",
},
},
want: map[string]string{
command.LabelSupportedKubeCNIAlpha: `["calico","flannel"]`,
command.LabelSupportedKubeCSIAlpha: `["alibaba-csi"]`,
"key1": "value1",
},
},
{
name: "",
args: args{
labels: map[string]string{},
},
want: map[string]string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := handleImageLabelOutput(tt.args.labels); !reflect.DeepEqual(got, tt.want) {
t.Errorf("handleLabelOutput() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit 91ba640

Please sign in to comment.