Skip to content

Commit

Permalink
Merge pull request #82 from containscafeine/add_label_support
Browse files Browse the repository at this point in the history
implement service level labels
  • Loading branch information
kadel authored May 17, 2017
2 parents ff9d664 + 8fb1788 commit 35a7ac7
Show file tree
Hide file tree
Showing 9 changed files with 372 additions and 17 deletions.
11 changes: 11 additions & 0 deletions docs/file-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ version: "0.1-dev"
services:
- name: foobar
replicas: 3
labels:
foo_label: bar_label
containers:
- image: foo/bar:tag
env:
Expand Down Expand Up @@ -57,6 +59,8 @@ Each service has name and list of the containers, while `replicas` can be option
# <ServiceSpec>
name: foo
replicas: 4
labels:
foo_label: bar_label
containers:
- <ContainerSpec>
emptyDirVolumes:
Expand All @@ -77,6 +81,13 @@ Name of the service.

Number of desired pods of this particluar service. This is an optional field. The valid value can only be a positive number.

#### labels
| type | required |
|---------|----------|
| map with string keys and string values | no |

Desired labels to be applied to the resulting Kubernetes objects from the service.

### containers
| type | required |
|-----------------------------------------|----------|
Expand Down
37 changes: 37 additions & 0 deletions examples/wordpress/storage_labels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
version: '0.1-dev'

services:
- name: database
labels:
app: db
containers:
- image: mariadb:10
env:
- MYSQL_ROOT_PASSWORD=rootpasswd
- MYSQL_DATABASE=wordpress
- MYSQL_USER=wordpress
- MYSQL_PASSWORD=wordpress
ports:
- port: 3306
mounts:
- volumeName: database
mountPath: /var/lib/mysql

- name: web
labels:
app: web
containers:
- image: wordpress:4
env:
- WORDPRESS_DB_HOST=database:3306
- WORDPRESS_DB_PASSWORD=wordpress
- WORDPRESS_DB_USER=wordpress
- WORDPRESS_DB_NAME=wordpress
ports:
- port: 80
type: external

volumes:
- name: database
size: 100Mi
accessMode: ReadWriteOnce
17 changes: 16 additions & 1 deletion pkg/encoding/v1/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,19 @@ func (raw *EnvVariable) UnmarshalYAML(unmarshal func(interface{}) error) error {
return nil
}

type Labels object.Labels

func (lb *Labels) UnmarshalYAML(unmarshal func(interface{}) error) error {
labelMap := make(map[string]string)
if err := unmarshal(&labelMap); err != nil {
return err
}

*lb = Labels(labelMap)

return nil
}

type ImageRef string

// FIXME: implement ImageRef unmarshalling
Expand Down Expand Up @@ -264,6 +277,7 @@ type Service struct {
Containers []Container `yaml:"containers"`
Replicas *int32 `yaml:"replicas,omitempty"`
EmptyDirVolumes []EmptyDirVolume `yaml:"emptyDirVolumes,omitempty"`
Labels Labels `yaml:"labels,omitempty"`
}

func (s *Service) UnmarshalYAML(unmarshal func(interface{}) error) error {
Expand Down Expand Up @@ -389,7 +403,8 @@ func (d *Decoder) Decode(data []byte) (*object.OpenCompose, error) {
// convert services
for _, s := range v1.Services {
os := object.Service{
Name: string(s.Name),
Name: string(s.Name),
Labels: object.Labels(s.Labels),
}

os.Replicas = s.Replicas
Expand Down
87 changes: 83 additions & 4 deletions pkg/encoding/v1/encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,10 +336,7 @@ readOnly: true
&Mount{
VolumeName: "test-volume",
ReadOnly: goutil.BoolAddr(true),
},
},
}

}}}
for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
var mount Mount
Expand Down Expand Up @@ -402,6 +399,52 @@ excess: field
}
}

func TestLabels_UnmarshalYAML(t *testing.T) {
tests := []struct {
name string
Succeed bool
RawLabels string
Labels *Labels
}{
{
"Providing valid label strings",
true,
`
key1: value1
key2: value2
key3:
key4: value4
`,
&Labels{
"key1": "value1",
"key2": "value2",
"key3": "",
"key4": "value4",
},
},
}
for _, tt := range tests {
var labels Labels
err := yaml.Unmarshal([]byte(tt.RawLabels), &labels)
if err != nil {
if tt.Succeed {
t.Errorf("Failed to unmarshal %#v; error %#v", tt.RawLabels, err)
}
continue
}

if !tt.Succeed {
t.Errorf("Expected %#v to fail!", tt.RawLabels)
continue
}

if !reflect.DeepEqual(labels, *tt.Labels) {
t.Errorf("Expected %#v, got %#v", *tt.Labels, labels)
continue
}
}
}

func TestService_UnmarshalYAML(t *testing.T) {
tests := []struct {
Name string
Expand Down Expand Up @@ -979,6 +1022,42 @@ volumes:
`,
nil,
},
{
true,
`
version: 0.1-dev
services:
- name: helloworld
replicas: 2
containers:
- image: tomaskral/nonroot-nginx
labels:
key1: value1
key2: value2
key3:
key4: value4
`,
&object.OpenCompose{
Version: Version,
Services: []object.Service{
{
Name: "helloworld",
Replicas: goutil.Int32Addr(2),
Containers: []object.Container{
{
Image: "tomaskral/nonroot-nginx",
},
},
Labels: object.Labels{
"key1": "value1",
"key2": "value2",
"key3": "",
"key4": "value4",
},
},
},
},
},
}

for _, tt := range tests {
Expand Down
11 changes: 11 additions & 0 deletions pkg/object/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type Mount struct {
ReadOnly bool
}

type Labels map[string]string

type Container struct {
Image string
Environment []EnvVariable
Expand All @@ -58,6 +60,7 @@ type Service struct {
Containers []Container
Replicas *int32
EmptyDirVolumes []EmptyDirVolume
Labels Labels
}

type Volume struct {
Expand Down Expand Up @@ -163,6 +166,14 @@ func (s *Service) validate() error {
}
}

// validate label values
for _, v := range s.Labels {
errString := validation.IsValidLabelValue(v)
if errString != nil {
return fmt.Errorf("Invalid label value: %v", errString)
}
}

return nil
}

Expand Down
44 changes: 44 additions & 0 deletions pkg/object/object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,50 @@ func TestOpenCompose_Validate(t *testing.T) {
},
},
},

{
"Valid labels",
true,
&OpenCompose{
Version: Version,
Services: []Service{
{
Name: name,
Containers: []Container{
{
Image: image,
},
},
Labels: Labels{
"key1": "value1",
"key2": "value2",
},
},
},
},
},

{
"Invalid label values",
false,
&OpenCompose{
Version: Version,
Services: []Service{
{
Name: name,
Containers: []Container{
{
Image: image,
},
},
Labels: Labels{
"key1": "garbage^value",
"key2": "value2",
},
},
},
},
},
}

for _, test := range tests {
Expand Down
48 changes: 36 additions & 12 deletions pkg/transform/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

"github.com/redhat-developer/opencompose/pkg/object"
"github.com/redhat-developer/opencompose/pkg/util"
_ "k8s.io/client-go/pkg/api/install"
"k8s.io/client-go/pkg/api/resource"
api_v1 "k8s.io/client-go/pkg/api/v1"
Expand All @@ -20,12 +21,18 @@ func (t *Transformer) CreateServices(o *object.Service) ([]runtime.Object, error
result := []runtime.Object{}

Service := func() *api_v1.Service {
serviceLabels := map[string]string(o.Labels)
return &api_v1.Service{
ObjectMeta: api_v1.ObjectMeta{
Name: o.Name,
Labels: map[string]string{
"service": o.Name,
},
Labels: *util.MergeMaps(
// The map containing `"service": o.Name` should always be
// passed later to avoid being overridden by util.MergeMaps()
&serviceLabels,
&map[string]string{
"service": o.Name,
},
),
},
Spec: api_v1.ServiceSpec{
Selector: map[string]string{
Expand Down Expand Up @@ -81,13 +88,19 @@ func (t *Transformer) CreateServices(o *object.Service) ([]runtime.Object, error
// Create k8s ingresses for OpenCompose service
func (t *Transformer) CreateIngresses(o *object.Service) ([]runtime.Object, error) {
result := []runtime.Object{}
serviceLabels := map[string]string(o.Labels)

i := &ext_v1beta1.Ingress{
ObjectMeta: api_v1.ObjectMeta{
Name: o.Name,
Labels: map[string]string{
"service": o.Name,
},
Labels: *util.MergeMaps(
// The map containing `"service": o.Name` should always be
// passed later to avoid being overridden by util.MergeMaps()
&serviceLabels,
&map[string]string{
"service": o.Name,
},
),
},
}

Expand Down Expand Up @@ -142,13 +155,19 @@ func (t *Transformer) CreateIngresses(o *object.Service) ([]runtime.Object, erro
// Create k8s deployments for OpenCompose service
func (t *Transformer) CreateDeployments(s *object.Service) ([]runtime.Object, error) {
result := []runtime.Object{}
serviceLabels := map[string]string(s.Labels)

d := &ext_v1beta1.Deployment{
ObjectMeta: api_v1.ObjectMeta{
Name: s.Name,
Labels: map[string]string{
"service": s.Name,
},
Labels: *util.MergeMaps(
// The map containing `"service": s.Name` should always be
// passed later to avoid being overridden by util.MergeMaps()
&serviceLabels,
&map[string]string{
"service": s.Name,
},
),
},
Spec: ext_v1beta1.DeploymentSpec{
Strategy: ext_v1beta1.DeploymentStrategy{
Expand All @@ -159,9 +178,14 @@ func (t *Transformer) CreateDeployments(s *object.Service) ([]runtime.Object, er
},
Template: api_v1.PodTemplateSpec{
ObjectMeta: api_v1.ObjectMeta{
Labels: map[string]string{
"service": s.Name,
},
Labels: *util.MergeMaps(
// The map containing `"service": s.Name` should always be
// passed later to avoid being overridden by util.MergeMaps()
&serviceLabels,
&map[string]string{
"service": s.Name,
},
),
},
Spec: api_v1.PodSpec{},
},
Expand Down
Loading

0 comments on commit 35a7ac7

Please sign in to comment.