Merge pull request #908 from GLYASAI/V5.2

Bug Fix: env rendering bug
This commit is contained in:
barnettZQG 2020-12-09 05:38:35 -06:00 committed by GitHub
commit c90b485a12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1251 additions and 33 deletions

View File

@ -265,6 +265,7 @@ func (e *exectorManager) exec(task *pb.TaskMessage) error {
}
}()
if err := worker.Run(time.Minute * 10); err != nil {
logrus.Errorf("task type: %s; body: %s; run task: %+v", task.TaskType, task.TaskBody, err)
MetricErrorTaskNum++
worker.ErrorCallBack(err)
}

View File

@ -19,6 +19,7 @@
package exector
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
@ -32,6 +33,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/client"
ramv1alpha1 "github.com/goodrain/rainbond-oam/pkg/ram/v1alpha1"
"github.com/goodrain/rainbond/builder"
"github.com/goodrain/rainbond/builder/sources"
"github.com/goodrain/rainbond/db"
@ -246,6 +248,21 @@ func (i *ExportApp) parseApps() ([]gjson.Result, error) {
return arr, nil
}
// parse metadata into ram
func (i *ExportApp) parseApp() (*ramv1alpha1.RainbondApplicationConfig, error) {
data, err := ioutil.ReadFile(fmt.Sprintf("%s/metadata.json", i.SourceDir))
if err != nil {
return nil, errors.Wrapf(err, "open metadata.json")
}
var ram ramv1alpha1.RainbondApplicationConfig
if err := json.Unmarshal(data, &ram); err != nil {
return nil, errors.Wrapf(err, "unmarshal data into ram")
}
return &ram, nil
}
//exportImage export image of app
func (i *ExportApp) exportImage(serviceDir string, app gjson.Result) error {
os.MkdirAll(serviceDir, 0755)
@ -518,7 +535,7 @@ func (i *ExportApp) exportRunnerImage() error {
//DockerComposeYaml docker compose struct
type DockerComposeYaml struct {
Version string `yaml:"version"`
Volumes map[string]Volume `yaml:"volumes,omitempty"`
Volumes map[string]GlobalVolume `yaml:"volumes,omitempty"`
Services map[string]*Service `yaml:"services,omitempty"`
}
@ -541,8 +558,8 @@ type Service struct {
} `yaml:"logging,omitempty"`
}
// Volume is the volume for docker compose.
type Volume struct {
// GlobalVolume is the volume for docker compose.
type GlobalVolume struct {
External bool `yaml:"external"`
}
@ -555,66 +572,47 @@ func (i *ExportApp) buildDockerComposeYaml() error {
y := &DockerComposeYaml{
Version: "2.1",
Volumes: make(map[string]Volume, 5),
Volumes: make(map[string]GlobalVolume, 5),
Services: make(map[string]*Service, 5),
}
i.Logger.Info("开始生成YAML文件", map[string]string{"step": "build-yaml", "status": "failure"})
logrus.Debug("Build docker compose yaml file in directory: ", i.SourceDir)
ram, err := i.parseApp()
if err != nil {
return err
}
dockerCompose := newDockerCompose(ram)
for _, app := range apps {
shareImage := app.Get("share_image").String()
appName := app.Get("service_cname").String()
appName = composeName(appName)
volumes := make([]string, 0, 3)
shareUUID := app.Get("service_share_uuid").String()
appName := dockerCompose.GetServiceName(shareUUID)
// 如果该组件是镜像方式部署,需要做两件事
// 1. 在.volumes中创建一个volume
// 2. 在.services.volumes中做映射
for _, item := range app.Get("service_volume_map_list").Array() {
volumeName := item.Get("volume_name").String()
volumeName = buildToLinuxFileName(volumeName)
volumePath := item.Get("volume_path").String()
if item.Get("volume_type").String() == "config-file" {
mountPath := path.Join(appName, volumePath)
logrus.Debugf("appName: %s; volumePath: %s; mount path: %s", appName, volumePath, mountPath)
volume := fmt.Sprintf("./%s:%s", mountPath, volumePath)
volumes = append(volumes, volume)
} else {
y.Volumes[volumeName] = Volume{}
volumes = append(volumes, fmt.Sprintf("%s:%s", volumeName, volumePath))
}
}
volumes := dockerCompose.GetServiceVolumes(shareUUID)
// environment variables
envs := make(map[string]string, 10)
if len(app.Get("service_env_map_list").Array()) > 0 {
if len(app.Get("port_map_list").Array()) > 0 {
// The first port here maybe not as the same as the first one original
port := app.Get("port_map_list").Array()[0]
envs["PORT"] = port.Get("container_port").String()
}
envs["MEMORY_SIZE"] = envutil.GetMemoryType(int(app.Get("memory").Int()))
configs := make(map[string]string)
for _, item := range app.Get("service_env_map_list").Array() {
key := item.Get("attr_name").String()
value := item.Get("attr_value").String()
configs[key] = value
envs[key] = value
if envs[key] == "**None**" {
envs[key] = util.NewUUID()[:8]
}
}
for key, value := range envs {
// env rendering
envs[key] = util.ParseVariable(value, configs)
}
for _, item := range app.Get("service_connect_info_map_list").Array() {
key := item.Get("attr_name").String()
value := item.Get("attr_value").String()
envs[key] = value
}
var depServices []string
// 如果该app依赖了另了个app-b则把app-b中所有公开环境变量注入到该app
for _, item := range app.Get("dep_service_map_list").Array() {
@ -629,6 +627,11 @@ func (i *ExportApp) buildDockerComposeYaml() error {
}
}
for key, value := range envs {
// env rendering
envs[key] = util.ParseVariable(value, envs)
}
service := &Service{
Image: sources.GenSaveImageName(shareImage),
ContainerName: appName,
@ -648,6 +651,7 @@ func (i *ExportApp) buildDockerComposeYaml() error {
y.Services[appName] = service
}
y.Volumes = dockerCompose.GetGlobalVolumes()
content, err := yaml.Marshal(y)
if err != nil {
i.Logger.Error(fmt.Sprintf("生成YAML文件失败%v", err), map[string]string{"step": "build-yaml", "status": "failure"})
@ -764,3 +768,131 @@ func buildToLinuxFileName(fileName string) string {
return fileName
}
type dockerCompose struct {
ram *ramv1alpha1.RainbondApplicationConfig
globalVolumes []string
serviceVolumes map[string][]string
serviceNames map[string]string
}
func newDockerCompose(ram *ramv1alpha1.RainbondApplicationConfig) *dockerCompose {
dc := &dockerCompose{
ram: ram,
}
dc.build()
return dc
}
func (d *dockerCompose) build() {
// Important! serviceNames is always first
d.serviceNames = d.buildServiceNames()
d.serviceVolumes, d.globalVolumes = d.buildVolumes()
}
func (d *dockerCompose) buildServiceNames() map[string]string {
names := make(map[string]string)
set := make(map[string]struct{})
for _, cpt := range d.ram.Components {
name := composeName(cpt.ServiceCname)
// make sure every name is unique
if _, exists := set[name]; exists {
name += "-" + util.NewUUID()[0:4]
}
set[name] = struct{}{}
names[cpt.ServiceShareID] = name
}
return names
}
// build service volumes and global volumes
func (d *dockerCompose) buildVolumes() (map[string][]string, []string) {
logrus.Debugf("start building volumes for %s", d.ram.AppName)
// all volumes from components
allVolumes := make(map[string]ramv1alpha1.ComponentVolumeList)
for _, cpt := range d.ram.Components {
allVolumes[cpt.ServiceShareID] = cpt.ServiceVolumeMapList
}
var composeVolumes []string
componentVolumes := make(map[string][]string)
for _, cpt := range d.ram.Components {
serviceName := d.GetServiceName(cpt.ServiceShareID)
var volumes []string
// own volumes
for _, vol := range cpt.ServiceVolumeMapList {
vol, composeVolume := d.buildVolume(serviceName, &vol)
volumes = append(volumes, vol)
if composeVolume != "" {
composeVolumes = append(composeVolumes, composeVolume)
}
}
// dependent volumes
for _, dvol := range cpt.MntReleationList {
vol := findDepVolume(allVolumes, dvol.ShareServiceUUID, dvol.VolumeName)
if vol == nil {
logrus.Warningf("[dockerCompose] [buildVolumes] dependent volume(%s/%s) not found", dvol.ShareServiceUUID, dvol.VolumeName)
continue
}
volume, composeVolume := d.buildVolume(serviceName, vol)
volumes = append(volumes, volume)
if composeVolume != "" {
composeVolumes = append(composeVolumes, composeVolume)
}
}
componentVolumes[cpt.ServiceShareID] = volumes
}
return componentVolumes, composeVolumes
}
func (d *dockerCompose) buildVolume(serviceName string, volume *ramv1alpha1.ComponentVolume) (string, string) {
volumePath := volume.VolumeMountPath
if volume.VolumeType == "config-file" {
mountPath := path.Join(serviceName, volume.VolumeMountPath)
mountPath = buildToLinuxFileName(mountPath)
return fmt.Sprintf("./%s:%s", mountPath, volumePath), ""
}
// make sure every volumeName is unique
volumeName := serviceName + "_" + volume.VolumeName
return fmt.Sprintf("%s:%s", volumeName, volumePath), volumeName
}
// GetServiceVolumes -
func (d *dockerCompose) GetServiceVolumes(shareServiceUUID string) []string {
return d.serviceVolumes[shareServiceUUID]
}
// GetGlobalVolumes -
func (d *dockerCompose) GetGlobalVolumes() map[string]GlobalVolume {
globalVolumes := make(map[string]GlobalVolume)
for _, vol := range d.globalVolumes {
globalVolumes[vol] = GlobalVolume{
External: false,
}
}
return globalVolumes
}
// GetServiceName -
func (d *dockerCompose) GetServiceName(shareServiceUUID string) string {
return d.serviceNames[shareServiceUUID]
}
func findDepVolume(allVolumes map[string]ramv1alpha1.ComponentVolumeList, key, volumeName string) *ramv1alpha1.ComponentVolume {
vols := allVolumes[key]
// find related volume
var volume *ramv1alpha1.ComponentVolume
for _, vol := range vols {
if vol.VolumeName == volumeName {
volume = &vol
break
}
}
return volume
}

View File

@ -0,0 +1,276 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2020-2020 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package oam
import (
"fmt"
"strings"
v1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
v1alpha1 "github.com/goodrain/rainbond-oam/pkg/ram/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
type containerWorkloadBuilder struct {
com v1alpha1.Component
plugins []v1alpha1.Plugin
output []v1alpha2.DataOutput
}
func (c *containerWorkloadBuilder) Build() runtime.RawExtension {
oamOS := v1alpha2.OperatingSystemLinux
oamCPU := v1alpha2.CPUArchitectureAMD64
var cw = &v1alpha2.ContainerizedWorkload{
ObjectMeta: metav1.ObjectMeta{
Name: c.com.ServiceCname,
Labels: map[string]string{},
Annotations: map[string]string{},
},
Spec: v1alpha2.ContainerizedWorkloadSpec{
OperatingSystem: &oamOS,
CPUArchitecture: &oamCPU,
Containers: c.buildContainers(),
},
}
return runtime.RawExtension{Object: cw}
}
func (c *containerWorkloadBuilder) Kind() string {
return "ContainerWorkload"
}
func (c *containerWorkloadBuilder) Output() []v1alpha2.DataOutput {
return c.output
}
func (c *containerWorkloadBuilder) buildContainers() []v1alpha2.Container {
com := c.com
var containers []v1alpha2.Container
mainContainer := v1alpha2.Container{
Name: com.ServiceName,
Image: com.Image,
Resources: &v1alpha2.ContainerResources{
Memory: v1alpha2.MemoryResources{
Required: NewMemoryQuantity(com.Memory),
},
CPU: v1alpha2.CPUResources{
Required: NewCPUQuantity(com.CPU),
},
Volumes: c.buildVolumes(com.ServiceVolumeMapList, com.MntReleationList),
},
Command: strings.Split(com.Cmd, " "),
Environment: c.buildEnv(com.Envs, com.ServiceConnectInfoMapList, true),
ConfigFiles: c.buildConfigFile(com.ServiceVolumeMapList),
Ports: c.buildPorts(com.Ports),
LivenessProbe: c.buildLivenessProbe(com.Probes),
ReadinessProbe: c.buildReadinessProbe(com.Probes),
ImagePullSecret: c.buildImagePullSecret(com.AppImage),
}
containers = append(containers, mainContainer)
//plugin container
for _, pluginConfig := range com.ServicePluginConfigs {
plugin := c.getPlugin(pluginConfig.PluginKey)
if plugin != nil {
containers = append(containers, c.buildPluginContainer(*plugin, pluginConfig, com))
}
}
return containers
}
//TODO: share volume
func (c *containerWorkloadBuilder) buildVolumes(volumes v1alpha1.ComponentVolumeList, shareVolume []v1alpha1.ComponentShareVolume) (re []v1alpha2.VolumeResource) {
for _, volume := range volumes {
if volume.VolumeType == v1alpha1.ConfigFileVolumeType {
continue
}
vr := v1alpha2.VolumeResource{
Name: volume.VolumeName,
MountPath: volume.VolumeMountPath,
AccessMode: NewVolumeAccess(volume.AccessMode),
SharingPolicy: NewSharingPolicy(volume.SharingPolicy),
Disk: &v1alpha2.DiskResource{},
}
if volume.VolumeCapacity > 0 {
vr.Disk.Required = NewDiskQuantity(volume.VolumeCapacity)
}
re = append(re, vr)
}
return
}
//TODO: share config file
func (c *containerWorkloadBuilder) buildConfigFile(volumes v1alpha1.ComponentVolumeList) (re []v1alpha2.ContainerConfigFile) {
for _, volume := range volumes {
if volume.VolumeType != v1alpha1.ConfigFileVolumeType {
continue
}
vr := v1alpha2.ContainerConfigFile{
Path: volume.VolumeMountPath,
Value: &volume.FileConent,
}
re = append(re, vr)
}
return
}
func (c *containerWorkloadBuilder) buildEnv(envs, connect []v1alpha1.ComponentEnv, insetOutput bool) (re []v1alpha2.ContainerEnvVar) {
for _, env := range envs {
re = append(re, v1alpha2.ContainerEnvVar{
Name: env.AttrName,
Value: &env.AttrValue,
})
}
for _, out := range connect {
re = append(re, v1alpha2.ContainerEnvVar{
Name: out.AttrName,
Value: &out.AttrValue,
})
if insetOutput {
c.output = append(c.output, v1alpha2.DataOutput{
Name: out.AttrName,
FieldPath: fmt.Sprintf("spec.container[0].env[%d].value", len(re)-1),
})
}
}
return
}
//TODO: create service
func (c *containerWorkloadBuilder) buildPorts(ports []v1alpha1.ComponentPort) (re []v1alpha2.ContainerPort) {
for _, p := range ports {
re = append(re, v1alpha2.ContainerPort{
Name: strings.ToLower(p.PortAlias),
Port: int32(p.ContainerPort),
Protocol: NewTransportProtocol(p.Protocol),
})
}
return
}
//TODO: build secret
func (c *containerWorkloadBuilder) buildImagePullSecret(info v1alpha1.ImageInfo) *string {
var secret string
return &secret
}
func (c *containerWorkloadBuilder) buildLivenessProbe(probes []v1alpha1.ComponentProbe) *v1alpha2.ContainerHealthProbe {
for _, probe := range probes {
if probe.Mode == "livebess" {
return createProbe(probe)
}
}
return nil
}
func (c *containerWorkloadBuilder) buildReadinessProbe(probes []v1alpha1.ComponentProbe) *v1alpha2.ContainerHealthProbe {
for _, probe := range probes {
if probe.Mode == "readiness" {
return createProbe(probe)
}
}
return nil
}
func (c *containerWorkloadBuilder) buildPluginContainer(plugin v1alpha1.Plugin, pluginConfig v1alpha1.ComponentPluginConfig, com v1alpha1.Component) v1alpha2.Container {
return v1alpha2.Container{
Name: plugin.PluginName,
Image: plugin.Image,
Resources: &v1alpha2.ContainerResources{
Memory: v1alpha2.MemoryResources{
Required: NewMemoryQuantity(pluginConfig.MemoryRequired),
},
CPU: v1alpha2.CPUResources{
Required: NewCPUQuantity(pluginConfig.CPURequired),
},
Volumes: c.buildVolumes(com.ServiceVolumeMapList, com.MntReleationList),
},
Command: strings.Split(com.Cmd, " "),
Environment: c.buildEnv(c.com.Envs, c.com.ServiceConnectInfoMapList, false),
ConfigFiles: c.buildConfigFile(c.com.ServiceVolumeMapList),
ImagePullSecret: c.buildImagePullSecret(plugin.PluginImage),
}
}
func createProbe(probe v1alpha1.ComponentProbe) *v1alpha2.ContainerHealthProbe {
return &v1alpha2.ContainerHealthProbe{
Exec: func() *v1alpha2.ExecProbe {
if probe.Cmd != "" {
return &v1alpha2.ExecProbe{
Command: strings.Split(probe.Cmd, " "),
}
}
return nil
}(),
HTTPGet: func() *v1alpha2.HTTPGetProbe {
if probe.Scheme == "http" {
return &v1alpha2.HTTPGetProbe{
Path: probe.Path,
Port: int32(probe.Port),
HTTPHeaders: func() []v1alpha2.HTTPHeader {
if probe.HTTPHeader == "" {
return nil
}
hds := strings.Split(probe.HTTPHeader, ",")
var headers []v1alpha2.HTTPHeader
for _, hd := range hds {
kv := strings.Split(hd, "=")
if len(kv) == 1 {
header := v1alpha2.HTTPHeader{
Name: kv[0],
Value: "",
}
headers = append(headers, header)
} else if len(kv) == 2 {
header := v1alpha2.HTTPHeader{
Name: kv[0],
Value: kv[1],
}
headers = append(headers, header)
}
}
return headers
}(),
}
}
return nil
}(),
TCPSocket: func() *v1alpha2.TCPSocketProbe {
if probe.Scheme == "tcp" {
return &v1alpha2.TCPSocketProbe{
Port: int32(probe.Port),
}
}
return nil
}(),
InitialDelaySeconds: Int32(probe.InitialDelaySecond),
PeriodSeconds: Int32(probe.PeriodSecond),
TimeoutSeconds: Int32(probe.TimeoutSecond),
SuccessThreshold: Int32(probe.SuccessThreshold),
FailureThreshold: Int32(probe.FailureThreshold),
}
}
func (c *containerWorkloadBuilder) getPlugin(key string) *v1alpha1.Plugin {
for _, p := range c.plugins {
if p.PluginKey == key {
return &p
}
}
return nil
}

View File

@ -0,0 +1,137 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2020-2020 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package oam
import (
"github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
"github.com/goodrain/rainbond-oam/pkg/ram/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
type builder struct {
oamApp *v1alpha2.ApplicationConfiguration
ram v1alpha1.RainbondApplicationConfig
}
//Builder oam application model builder
type Builder interface {
// build oam application
Build() *v1alpha2.ApplicationConfiguration
}
//WorkloadBuilder workload builder
type WorkloadBuilder interface {
Build() runtime.RawExtension
Output() []v1alpha2.DataOutput
Kind() string
}
//NewBuilder new oam model builder
func NewBuilder(ram v1alpha1.RainbondApplicationConfig) Builder {
var oam v1alpha2.ApplicationConfiguration
return &builder{
oamApp: &oam,
ram: ram,
}
}
//NewWorkloadBuilder new workload builder
func NewWorkloadBuilder(com v1alpha1.Component, plugins []v1alpha1.Plugin) WorkloadBuilder {
switch com.DeployType {
case v1alpha1.StateMultipleDeployType, v1alpha1.StateSingletonDeployType:
return &statefulWorkloadBuilder{
com: com,
plugins: plugins,
}
case v1alpha1.StatelessMultipleDeployType, v1alpha1.StatelessSingletionDeployType:
return &containerWorkloadBuilder{
com: com,
plugins: plugins,
}
default:
return &containerWorkloadBuilder{
com: com,
plugins: plugins,
}
}
}
func (b *builder) Build() *v1alpha2.ApplicationConfiguration {
return b.oamApp
}
func (b *builder) buildApplication() {
b.oamApp.Name = b.ram.AppName
}
func (b *builder) buildComponent() {
var components []v1alpha2.Component
var configurationComponents []v1alpha2.ApplicationConfigurationComponent
for i := range b.ram.Components {
rcom := b.ram.Components[i]
builder := NewWorkloadBuilder(*rcom, b.ram.Plugins)
cw := builder.Build()
output := builder.Output()
component := v1alpha2.Component{
ObjectMeta: metav1.ObjectMeta{
Name: rcom.ServiceCname,
Labels: map[string]string{},
Annotations: map[string]string{},
},
Spec: v1alpha2.ComponentSpec{
Workload: cw,
},
}
components = append(components, component)
var acc = v1alpha2.ApplicationConfigurationComponent{
ComponentName: component.GetName(),
DataOutputs: output,
}
// Handle dependencies between components
for _, dep := range rcom.DepServiceMapList {
for _, env := range b.getDepComponentConnectionInfo(dep.DepServiceKey) {
acc.DataInputs = append(acc.DataInputs, v1alpha2.DataInput{
ValueFrom: v1alpha2.DataInputValueFrom{
DataOutputName: env.AttrName,
},
//TODO:
ToFieldPaths: func() []string {
return []string{""}
}(),
})
}
}
configurationComponents = append(configurationComponents, acc)
}
b.oamApp.Spec.Components = configurationComponents
}
func (b *builder) getDepComponentConnectionInfo(componentKey string) []v1alpha1.ComponentEnv {
for _, com := range b.ram.Components {
if com.ServiceKey == componentKey {
return com.ServiceConnectInfoMapList
}
}
return nil
}
func (b *builder) buildTrait() {
}

View File

@ -0,0 +1,96 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2020-2020 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package oam
import (
v1alpha2 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
"github.com/goodrain/rainbond-oam/pkg/ram/v1alpha1"
apps "k8s.io/api/apps/v1"
core "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)
type statefulWorkloadBuilder struct {
com v1alpha1.Component
plugins []v1alpha1.Plugin
output []v1alpha2.DataOutput
}
func (s *statefulWorkloadBuilder) Build() runtime.RawExtension {
var statefulset = &apps.StatefulSet{
ObjectMeta: metav1.ObjectMeta{
Name: s.com.ServiceCname,
Labels: map[string]string{},
Annotations: map[string]string{},
},
Spec: apps.StatefulSetSpec{
Replicas: Int32(s.com.ExtendMethodRule.MinNode),
Template: s.buildPodTemplate(),
ServiceName: "",
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"name": s.com.ServiceName,
},
},
UpdateStrategy: apps.StatefulSetUpdateStrategy{
Type: apps.RollingUpdateStatefulSetStrategyType,
},
},
}
return runtime.RawExtension{Object: statefulset}
}
func (s *statefulWorkloadBuilder) buildPodTemplate() core.PodTemplateSpec {
var podT = core.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Name: s.com.ServiceCname,
Labels: map[string]string{},
Annotations: map[string]string{},
},
Spec: core.PodSpec{
Volumes: s.buildVolume(),
Containers: s.buildPodContainer(),
InitContainers: s.buildPodInitContainer(),
RestartPolicy: core.RestartPolicyAlways,
ImagePullSecrets: []core.LocalObjectReference{},
},
}
return podT
}
func (s *statefulWorkloadBuilder) buildVolume() []core.Volume {
return nil
}
func (s *statefulWorkloadBuilder) buildPodContainer() []core.Container {
return nil
}
func (s *statefulWorkloadBuilder) buildPodInitContainer() []core.Container {
return nil
}
func (s *statefulWorkloadBuilder) Kind() string {
return "StatefulsetWorkload"
}
func (s *statefulWorkloadBuilder) Output() []v1alpha2.DataOutput {
return s.output
}

112
vendor/github.com/goodrain/rainbond-oam/pkg/oam/util.go generated vendored Normal file
View File

@ -0,0 +1,112 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2020-2020 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package oam
import (
"fmt"
"strings"
"github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
"github.com/goodrain/rainbond-oam/pkg/ram/v1alpha1"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/resource"
)
//NewMemoryQuantity new memory quantity
func NewMemoryQuantity(memory int) resource.Quantity {
rq, err := resource.ParseQuantity(fmt.Sprintf("%dMi", memory))
if err != nil {
logrus.Warningf("parse memory quantity failure %s", err.Error())
}
return rq
}
//NewCPUQuantity new cpu quantity
func NewCPUQuantity(cpu int) resource.Quantity {
rq, err := resource.ParseQuantity(fmt.Sprintf("%d", cpu))
if err != nil {
logrus.Warningf("parse cpu quantity failure %s", err.Error())
}
return rq
}
//NewDiskQuantity new disk quantity
func NewDiskQuantity(disk int) resource.Quantity {
rq, err := resource.ParseQuantity(fmt.Sprintf("%dGi", disk))
if err != nil {
logrus.Warningf("parse cpu quantity failure %s", err.Error())
}
return rq
}
//NewVolumeAccess new volume access
func NewVolumeAccess(va v1alpha1.AccessMode) *v1alpha2.VolumeAccessMode {
var ro = v1alpha2.VolumeAccessModeRO
var rw = v1alpha2.VolumeAccessModeRW
switch va {
case v1alpha1.ROXAccessMode:
return &ro
case v1alpha1.RWOAccessMode:
return &rw
case v1alpha1.RWXAccessMode:
return &rw
default:
return &rw
}
}
//NewSharingPolicy new sharing policy
func NewSharingPolicy(sp string) *v1alpha2.VolumeSharingPolicy {
var share = v1alpha2.VolumeSharingPolicyShared
var exclusive = v1alpha2.VolumeSharingPolicyExclusive
switch sp {
case "Shared":
return &share
case "Exclusive":
return &exclusive
default:
return &share
}
}
//NewTransportProtocol -
func NewTransportProtocol(protocol string) *v1alpha2.TransportProtocol {
var udp = v1alpha2.TransportProtocolUDP
var tcp = v1alpha2.TransportProtocolTCP
switch strings.ToLower(protocol) {
case "udp":
return &udp
default:
return &tcp
}
}
//Uint32 -
func Uint32(s int) *uint32 {
var ss = uint32(s)
return &ss
}
//Int32 -
func Int32(s int) *int32 {
var ss = int32(s)
return &ss
}

View File

@ -0,0 +1,409 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2020-2020 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package v1alpha1
import (
"encoding/json"
"fmt"
"github.com/goodrain/rainbond-oam/pkg/util"
)
//RainbondApplicationConfig store app version templete
type RainbondApplicationConfig struct {
AppKeyID string `json:"group_key"`
AppName string `json:"group_name"`
AppVersion string `json:"group_version"`
TempleteVersion string `json:"template_version"`
Components []*Component `json:"apps"`
Plugins []Plugin `json:"plugins,omitempty"`
AppConfigGroups []AppConfigGroup `json:"app_config_groups"`
}
//HandleNullValue handle null value
func (s *RainbondApplicationConfig) HandleNullValue() {
if s.TempleteVersion == "" {
s.TempleteVersion = "v2"
}
if s.Plugins == nil {
s.Plugins = []Plugin{}
}
for i := range s.Components {
s.Components[i].HandleNullValue()
}
for i := range s.Plugins {
s.Plugins[i].HandleNullValue()
}
}
//Validation validation app templete
func (s *RainbondApplicationConfig) Validation() error {
if len(s.Components) == 0 {
return fmt.Errorf("no app in templete")
}
for _, app := range s.Components {
if err := app.Validation(); err != nil {
return err
}
}
for _, plugin := range s.Plugins {
if err := plugin.Validation(); err != nil {
return err
}
}
return nil
}
//JSON return json string
func (s *RainbondApplicationConfig) JSON() string {
body, _ := json.Marshal(s)
return string(body)
}
//DeployType deploy type
// TODO update it stateless_multiple, stateless_singleton
type DeployType string
//StatelessSingletionDeployType stateless
var StatelessSingletionDeployType DeployType = "stateless_singleton"
//StatelessMultipleDeployType -
var StatelessMultipleDeployType DeployType = "stateless_multiple"
//StateMultipleDeployType -
var StateMultipleDeployType DeployType = "state_multiple"
//StateSingletonDeployType state
var StateSingletonDeployType DeployType = "state_singleton"
//ServiceType 服务类型
type ServiceType string
//ApplicationServiceType 普通应用
var ApplicationServiceType = "application"
//HelmChartServiceType helm应用
var HelmChartServiceType = "helm-chart"
//ComponentVolumeList volume list
type ComponentVolumeList []ComponentVolume
//Add add volume
func (s *ComponentVolumeList) Add(volume ComponentVolume) {
for _, v := range *s {
if v.VolumeName == volume.VolumeName {
if v.VolumeMountPath == volume.VolumeMountPath {
return
}
volume.VolumeName = volume.VolumeName + util.NewUUID()[24:]
}
}
*s = append(*s, volume)
}
//Component component model
type Component struct {
// container limit memory, unit MB
Memory int `json:"memory"`
CPU int `json:"cpu"`
Probes []ComponentProbe `json:"probes"`
AppImage ImageInfo `json:"service_image"`
ComponentID string `json:"service_id"`
DeployType DeployType `json:"extend_method"`
ServiceKey string `json:"service_key"`
ServiceShareID string `json:"service_share_uuid,omitempty"`
ShareType string `json:"share_type,omitempty"`
MntReleationList []ComponentShareVolume `json:"mnt_relation_list"`
ServiceSource string `json:"service_source"`
DepServiceMapList []ComponentDep `json:"dep_service_map_list"`
ServiceConnectInfoMapList []ComponentEnv `json:"service_connect_info_map_list"`
ServiceVolumeMapList ComponentVolumeList `json:"service_volume_map_list"`
Version string `json:"version"`
Ports []ComponentPort `json:"port_map_list"`
ServiceName string `json:"service_name"`
Category string `json:"category"`
Envs []ComponentEnv `json:"service_env_map_list"`
ServiceAlias string `json:"service_alias"`
DeployVersion string `json:"deploy_version"`
ExtendMethodRule ComponentExtendMethodRule `json:"extend_method_map"`
ServiceType string `json:"service_type"`
ServiceCname string `json:"service_cname"`
ShareImage string `json:"share_image"`
Image string `json:"image"`
Cmd string `json:"cmd"`
Language string `json:"language"`
ServicePluginConfigs []ComponentPluginConfig `json:"service_related_plugin_config,omitempty"`
ComponentMonitor []ComponentMonitor `json:"component_monitor"`
}
//HandleNullValue 处理null值
func (s *Component) HandleNullValue() {
if s.ServicePluginConfigs == nil {
s.ServicePluginConfigs = []ComponentPluginConfig{}
}
if s.Envs == nil {
s.Envs = []ComponentEnv{}
}
if s.Ports == nil {
s.Ports = []ComponentPort{}
}
if s.ServiceVolumeMapList == nil {
s.ServiceVolumeMapList = []ComponentVolume{}
}
if s.ServiceConnectInfoMapList == nil {
s.ServiceConnectInfoMapList = []ComponentEnv{}
}
if s.DepServiceMapList == nil {
s.DepServiceMapList = []ComponentDep{}
}
if s.MntReleationList == nil {
s.MntReleationList = []ComponentShareVolume{}
}
if s.Probes == nil {
s.Probes = []ComponentProbe{}
}
}
//Validation -
func (s *Component) Validation() error {
return nil
}
//ComponentProbe probe
type ComponentProbe struct {
ID int `json:"ID" bson:"ID"`
InitialDelaySecond int `json:"initial_delay_second" bson:"initial_delay_second"`
FailureThreshold int `json:"failure_threshold" bson:"failure_threshold"`
ServiceID string `json:"service_id" bson:"service_id"`
HTTPHeader string `json:"http_header" bson:"http_header"`
Cmd string `json:"cmd" bson:"cmd"`
ProbeID string `json:"probe_id" bson:"probe_id"`
Scheme string `json:"scheme" bson:"scheme"`
SuccessThreshold int `json:"success_threshold" bson:"success_threshold"`
TimeoutSecond int `json:"timeout_second" bson:"timeout_second"`
IsUsed bool `json:"is_used" bson:"is_used"`
PeriodSecond int `json:"period_second" bson:"period_second"`
Port int `json:"port" bson:"port"`
Mode string `json:"mode" bson:"mode"`
Path string `json:"path" bson:"path"`
}
//Validation probe validation
func (s *ComponentProbe) Validation() error {
if s.Port == 0 && s.Cmd == "" {
return fmt.Errorf("probe endpoint port is 0")
}
return nil
}
//ImageInfo -
type ImageInfo struct {
HubPassword string `json:"hub_password" bson:"hub_password"`
Namespace string `json:"namespace" bson:"namespace"`
HubURL string `json:"hub_url" bson:"hub_url"`
HubUser string `json:"hub_user" bson:"hub_user"`
IsTrust bool `json:"is_trust" bson:"is_trust"`
}
//ComponentPort port
type ComponentPort struct {
PortAlias string `json:"port_alias" bson:"port_alias"`
Protocol string `json:"protocol" bson:"protocol"`
TenantID string `json:"tenant_id" bson:"tenant_id"`
ContainerPort int `json:"container_port" bson:"container_port"`
IsOuter bool `json:"is_outer_service" bson:"is_outer_service"`
IsInner bool `json:"is_inner_service" bson:"is_inner_service"`
}
//ComponentEnv env
type ComponentEnv struct {
AttrName string `json:"attr_name" bson:"attr_name"`
Name string `json:"name" bson:"name"`
IsChange bool `json:"is_change" bson:"is_change"`
AttrValue string `json:"attr_value" bson:"attr_value"`
}
//ComponentExtendMethodRule -
//服务伸缩规则,目前仅包含手动伸缩的规则
type ComponentExtendMethodRule struct {
MinNode int `json:"min_node" bson:"min_node"`
StepMemory int `json:"step_memory" bson:"step_memory"`
IsRestart int `json:"is_restart" bson:"is_restart"`
StepNode int `json:"step_node" bson:"step_node"`
MaxMemory int `json:"max_memory" bson:"max_memory"`
MaxNode int `json:"max_node" bson:"max_node"`
MinMemory int `json:"min_memory" bson:"min_memory"`
}
//DefaultExtendMethodRule default Scaling rules
func DefaultExtendMethodRule() ComponentExtendMethodRule {
return ComponentExtendMethodRule{
MinNode: 1,
MinMemory: 64,
MaxMemory: 1024 * 64,
MaxNode: 1024,
StepMemory: 64,
StepNode: 1,
}
}
//Plugin templete plugin model
type Plugin struct {
ID int `json:"ID" bson:"id"`
Origin string `json:"origin" bson:"origin"`
CodeRepo string `json:"code_repo" bson:"code_repo"`
PluginAlias string `json:"plugin_alias" bson:"plugin_alias"`
PluginName string `json:"plugin_name" bson:"plugin_name"`
CreateTime string `json:"create_time" bson:"create_time"`
ShareImage string `json:"share_image" bson:"share_image"`
ConfigGroups []PluginConfigGroup `json:"config_groups,omitempty" bson:"config_groups"`
PluginKey string `json:"plugin_key" bson:"plugin_key"`
BuildSource string `json:"build_source" bson:"build_source"`
Desc string `json:"desc" bson:"desc"`
PluginID string `json:"plugin_id" bson:"plugin_id"`
Category string `json:"category" bson:"category"`
OriginShareID string `json:"origin_share_id" bson:"origin_share_id"`
Image string `json:"image" bson:"image"`
PluginImage ImageInfo `json:"plugin_image" bson:"plugin_image"`
BuildVersion string `json:"build_version" bson:"build_version"`
}
//Validation validation app templete
func (s *Plugin) Validation() error {
return nil
}
//HandleNullValue 处理null值数据
func (s *Plugin) HandleNullValue() {
if s.ConfigGroups == nil {
s.ConfigGroups = []PluginConfigGroup{}
}
}
//PluginConfigGroup 插件配置定义
type PluginConfigGroup struct {
ID int `json:"ID" bson:"id"`
ConfigName string `json:"config_name" bson:"config_name"`
Options []PluginConfigGroupOption `json:"options,omitempty" bson:"options"`
BuildVersion string `json:"build_version" bson:"build_version"`
PluginID string `json:"plugin_id" bson:"plugin_id"`
Injection string `json:"injection" bson:"injection"`
ServiceMetaType string `json:"service_meta_type" bson:"service_meta_type"`
}
//PluginConfigGroupOption 插件配置项定义
type PluginConfigGroupOption struct {
ID int `json:"ID" bson:"id"`
AttrValue string `json:"attr_alt_value" bson:"attr_alt_value"`
AttrType string `json:"attr_type" bson:"attr_type"`
IsChange bool `json:"is_change" bson:"is_change"`
BuildVersion string `json:"build_version" bson:"build_version"`
PluginID string `json:"plugin_id" bson:"plugin_id"`
ServiceMetaType string `json:"service_meta_type" bson:"service_meta_type"`
AttrDefaultValue string `json:"attr_default_value" bson:"attr_default_value"`
AttrName string `json:"attr_name" bson:"attr_name"`
AttrInfo string `json:"attr_info" bson:"attr_info"`
Protocol string `json:"protocol" bson:"protocol"`
}
//ComponentShareVolume 共享其他服务存储信息
type ComponentShareVolume struct {
VolumeName string `json:"mnt_name" bson:"mnt_name"`
VolumeMountDir string `json:"mnt_dir" bson:"mnt_dir"`
ShareServiceUUID string `json:"service_share_uuid" bson:"service_share_uuid"`
}
//ComponentDep 服务依赖关系数据
type ComponentDep struct {
DepServiceKey string `json:"dep_service_key" bson:"dep_service_key"`
}
//VolumeType volume type
type VolumeType string
//ShareFileVolumeType 共享文件存储
var ShareFileVolumeType VolumeType = "share-file"
//LocalVolumeType 本地文件存储
var LocalVolumeType VolumeType = "local"
//MemoryFSVolumeType 内存文件存储
var MemoryFSVolumeType VolumeType = "memoryfs"
//ConfigFileVolumeType configuration file volume type
var ConfigFileVolumeType VolumeType = "config-file"
func (vt VolumeType) String() string {
return string(vt)
}
// AccessMode volume access mode
type AccessMode string
// RWOAccessMode write and read only one node
var RWOAccessMode AccessMode = "RWO"
// RWXAccessMode write and read multi node
var RWXAccessMode AccessMode = "RWX"
// ROXAccessMode only read
var ROXAccessMode AccessMode = "ROX"
//ComponentVolume volume config
type ComponentVolume struct {
VolumeName string `json:"volume_name"`
FileConent string `json:"file_content"`
VolumeMountPath string `json:"volume_path"`
VolumeType VolumeType `json:"volume_type"`
VolumeCapacity int `json:"volume_capacity"`
AccessMode AccessMode `json:"access_mode"`
SharingPolicy string `json:"sharing_policy"`
}
//ComponentPluginConfig 服务插件配置数据
type ComponentPluginConfig struct {
CreateTime string `json:"create_time"`
PluginStatus bool `json:"plugin_status"`
ServiceID string `json:"service_id"`
PluginID string `json:"plugin_id"`
ServiceMetaType string `json:"service_meta_type"`
MemoryRequired int `json:"memory_required"`
CPURequired int `json:"cpu_required"`
Attr []map[string]interface{} `json:"attr"`
//插件类型
PluginKey string `json:"plugin_key"`
BuildVersion string `json:"build_version"`
}
//ComponentMonitor component monitor plugin
type ComponentMonitor struct {
Name string `json:"name"`
ServiceShowName string `json:"service_show_name"`
Port int `json:"port"`
Path string `json:"path"`
Interval string `json:"interval"`
}
//AppConfigGroup app config groups
type AppConfigGroup struct {
Name string `json:"name"`
InjectionType string `json:"injection_type"`
ConfigItems map[string]string `json:"config_items"`
ComponentIDs []string `json:"component_ids"`
}

View File

@ -0,0 +1,30 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2020-2020 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package util
import (
"strings"
"github.com/google/uuid"
)
//NewUUID new uuid string
func NewUUID() string {
return strings.Replace(uuid.New().String(), "-", "", -1)
}

View File

@ -0,0 +1,25 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2020-2020 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package util
import "testing"
func TestNewUUID(t *testing.T) {
t.Log(NewUUID())
}