Skip to content
This repository has been archived by the owner on Nov 15, 2022. It is now read-only.

Commit

Permalink
feat(spec): add support for extraResources
Browse files Browse the repository at this point in the history
With this root level field called `extraResources`, which is a list
of file names of Kubernetes artifacts that can be fed to Kubernetes
directly without kedge having to do any processing.

Anything that is not supported in kedge can be provided using this
field. Following is the example snippet of the app file that is
using `extraResources` field to deploy a `secret.yaml` and a
`cron-job.yaml` with the rest of kedge file. Here `secret.yaml` &
`cron-jobs.yaml` reside in the same directory.

e.g.

```
...
extraResources:
- secret.yaml
- cron-jobs.yaml
```
  • Loading branch information
surajssd committed Jul 31, 2017
1 parent ab990b5 commit 3c8b711
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 49 deletions.
25 changes: 25 additions & 0 deletions docs/file-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,31 @@ More info about Probe: https://kubernetes.io/docs/api-reference/v1.6/#probe-v1-c

The name of the Ingress.

## extraResources

```yaml
extraResources:
- <string>
- <string>
```

e.g.

```yaml
extraResources:
- ./kubernetes/cron-job.yaml
- secrets.yaml
```

This is list of files that are Kubernetes resources which can be passed to
Kubernetes directly. On these list of files Kedge won't do any processing, but
pass it to Kubernetes directly.

The file path are relative to the kedge application file.

This is one of the mechanism to extend kedge beyond it's capabilites to support
anything in the Kubernetes land.

## Complete example

```yaml
Expand Down
28 changes: 28 additions & 0 deletions examples/extraResources/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# extraResources

Kedge might not support all the things that Kubernetes has, but kedge would not
come in your way to define anything that Kubernetes understands.

For e.g. right now there is no way to define cron-jobs in kedge, but you can still
specify the Kubernetes cron-job file. In this field called `extraResources`.

See snippet from [app.yaml](app.yaml):

```yaml
extraResources:
- cronjob.yaml
```
So in this way you can specify list of files under root level field called
`extraResources`. These files has configuration that Kubernetes understands,
kedge won't do any processing on these files and feed them directly to
Kubernetes.

Also the file paths under `extraResources` should be relative to the kedge file
in which this config is specified.

This does not change anything with respect to deploying application.

```console
$ kedge create -f app.yaml
```
16 changes: 16 additions & 0 deletions examples/extraResources/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: web
containers:
- image: centos/httpd
volumeMounts:
- name: web
mountPath: /var/www/html/
services:
- name: web
type: NodePort
ports:
- port: 80
volumeClaims:
- name: web
size: 100Mi
extraResources:
- cronjob.yaml
26 changes: 26 additions & 0 deletions examples/extraResources/cronjob.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apiVersion: batch/v2alpha1
kind: CronJob
metadata:
name: web
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: web
image: busybox
args:
- /bin/sh
- -c
- date > /var/www/html/index.html; echo 'Hello from the Kubernetes cluster' >> /var/www/html/index.html
volumeMounts:
- name: web
mountPath: /var/www/html/
restartPolicy: OnFailure
volumes:
- name: web
persistentVolumeClaim:
claimName: web

47 changes: 36 additions & 11 deletions pkg/cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ package cmd

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"

"github.com/kedgeproject/kedge/pkg/encoding"
"github.com/kedgeproject/kedge/pkg/transform/kubernetes"

"github.com/ghodss/yaml"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/runtime"
)

func Generate(paths []string) error {
Expand All @@ -46,33 +47,57 @@ func Generate(paths []string) error {
return errors.Wrap(err, "unable to unmarshal data")
}

ros, err := kubernetes.Transform(app)
ros, extraResources, err := kubernetes.Transform(app)
if err != nil {
return errors.Wrap(err, "unable to transform data")
}

// write all the kubernetes objects that were generated
for _, runtimeObject := range ros {

data, err := yaml.Marshal(runtimeObject)
if err != nil {
return errors.Wrap(err, "failed to marshal object")
}

writeObject := func(o runtime.Object, data []byte) error {
_, err := fmt.Fprintln(os.Stdout, "---")
if err != nil {
return errors.Wrap(err, "could not print to STDOUT")
}

_, err = os.Stdout.Write(data)
return errors.Wrap(err, "could not write to STDOUT")
err = writeObject(data)
if err != nil {
return errors.Wrap(err, "failed to write object")
}
}

err = writeObject(runtimeObject, data)
for _, file := range extraResources {
// change the file name to absolute file name
// then read the file and then pass it to writeObject
file = findAbsPath(input.fileName, file)
data, err := ioutil.ReadFile(file)
if err != nil {
return errors.Wrap(err, "file reading failed")
}
err = writeObject(data)
if err != nil {
return errors.Wrap(err, "failed to write object")
}
}
}
return nil
}

func writeObject(data []byte) error {
_, err := fmt.Fprintln(os.Stdout, "---")
if err != nil {
return errors.Wrap(err, "could not print to STDOUT")
}

_, err = os.Stdout.Write(data)
return errors.Wrap(err, "could not write to STDOUT")
}

func findAbsPath(baseFilePath, path string) string {
// TODO: if the baseFilePath is empty then just take the
// pwd as basefilePath, here we will force user to
// use the kedge binary from the directory that has files
// otherwise there is no way of knowing where the files will be
// this condition will happen when we add support for reading from the stdin
return filepath.Join(filepath.Dir(baseFilePath), path)
}
62 changes: 42 additions & 20 deletions pkg/cmd/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,12 @@ func ExecuteKubectl(paths []string, args ...string) error {
}

for _, input := range inputs {

app, err := encoding.Decode(input.data)
if err != nil {
return errors.Wrap(err, "unable to unmarshal data")
}

ros, err := kubernetes.Transform(app)
ros, extraResources, err := kubernetes.Transform(app)
if err != nil {
return errors.Wrap(err, "unable to convert data")
}
Expand All @@ -57,34 +56,57 @@ func ExecuteKubectl(paths []string, args ...string) error {
if err != nil {
return errors.Wrap(err, "failed to marshal object")
}

// We need to add "-f -" at the end of the command passed to us to
// pass the files.
// e.g. If the command is "apply --namespace staging", then the
// pass the generated files.
// e.g. If the command and arguments are "apply --namespace staging", then the
// final command becomes "kubectl apply --namespace staging -f -"
cmd := exec.Command("kubectl", append(args, "-f", "-")...)

stdin, err := cmd.StdinPipe()
arguments := append(args, "-f", "-")
err = runKubectl(arguments, data)
if err != nil {
return errors.Wrap(err, "can't get stdinPipe for kubectl")
return errors.Wrap(err, "kubectl error")
}
}

go func() {
defer stdin.Close()
_, err := io.WriteString(stdin, string(data))
if err != nil {
fmt.Printf("can't write to stdin %v\n", err)
}
}()
for _, file := range extraResources {
// change the file name to absolute file name
file = findAbsPath(input.fileName, file)

out, err := cmd.CombinedOutput()
// We need to add "-f absolute-filename" at the end of the command passed to us to
// pass the generated files.
// e.g. If the command and arguments are "apply --namespace staging", then the
// final command becomes "kubectl apply --namespace staging -f absolute-filename"
arguments := append(args, "-f", file)
err = runKubectl(arguments, nil)
if err != nil {
fmt.Printf("%s", string(out))
return errors.Wrap(err, "failed to execute command")
return errors.Wrap(err, "kubectl error")
}
fmt.Printf("%s", string(out))
}
}

return nil
}

func runKubectl(args []string, data []byte) error {
cmd := exec.Command("kubectl", args...)

stdin, err := cmd.StdinPipe()
if err != nil {
return errors.Wrap(err, "can't get stdinPipe for kubectl")
}

go func() {
defer stdin.Close()
_, err := io.WriteString(stdin, string(data))
if err != nil {
fmt.Printf("can't write to stdin %v\n", err)
}
}()

out, err := cmd.CombinedOutput()
if err != nil {
fmt.Printf("%s", string(out))
return errors.Wrap(err, "failed to execute command")
}
fmt.Printf("%s", string(out))
return nil
}
6 changes: 4 additions & 2 deletions pkg/cmd/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import (
)

type inputData struct {
data []byte
fileName string
data []byte
}

func getApplicationsFromFiles(files []string) ([]inputData, error) {
Expand Down Expand Up @@ -51,7 +52,8 @@ func getApplicationsFromFiles(files []string) ([]inputData, error) {
// --- # avoids empty input here
if len(strings.TrimSpace(app)) > 0 {
appData = append(appData, inputData{
data: []byte(app),
fileName: file,
data: []byte(app),
})
}
}
Expand Down
1 change: 1 addition & 0 deletions pkg/spec/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type App struct {
ConfigMaps []ConfigMapMod `json:"configMaps,omitempty"`
Services []ServiceSpecMod `json:"services,omitempty"`
Ingresses []IngressSpecMod `json:"ingresses,omitempty"`
ExtraResources []string `json:"extraResources,omitempty"`
PodSpecMod `json:",inline"`
ext_v1beta1.DeploymentSpec `json:",inline"`
}
Loading

0 comments on commit 3c8b711

Please sign in to comment.