Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.0.8 Release #1

Merged
merged 6 commits into from
Dec 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ jobs:
runs-on: ubuntu-latest
steps:

- name: Set up Go 1.13
- name: Set up Go 1.14
uses: actions/setup-go@v1
with:
go-version: 1.13
go-version: 1.14
id: go

- name: Check out code into the Go module directory
Expand Down
32 changes: 30 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This resource assumes that kubernetes deployment is executed by plain manifests(

In addition to deploy(`kubectl apply`) operation, it also supports delete(`kubectl delete`) and undo(`kubectl rollout undo`) operations.

This resource has been tested on 1.14 and 1.17 (probably works on 1.15 and 1.16).
This resource has been tested on kubernetes v1.20.1 and concourse v6.7.2.

## Source Configuration

Expand Down Expand Up @@ -43,6 +43,8 @@ Deploys the watched resources to kubernetes using plain manifests or Kustomize o
* `kustomize` - true if deploying by kustomize. Default to `false`.
* `status_check_timeout` - The time(seconds) to wait for deployment to complete. Default to 5 minutes.
* `command_timeout` - The time(seconds) to wait for kubectl apply or delete. Default to unlimited(0).
* `diff` - true if using `kubectl diff`. Default to `false`.
* `server_dry_run` - true if using `kubectl apply --dry-run=server`. Default to `false`.
* `delete` - true if using `kubectl delete` operation. Default to `false`.
* `undo` - true if using `kubectl rollout undo` operation(target resources are `watchedResources`). Default to `false`.

Expand All @@ -58,7 +60,7 @@ resource_types:
type: docker-image
source:
repository: kudohn/concourse-k8s-resource
tag: 0.0.7
tag: 0.0.8
```

### `resources`
Expand Down Expand Up @@ -161,6 +163,32 @@ jobs:
undo: true
```

#### Deploy resources with server-dry-run

```yaml
jobs:
- name: deploy-app
plan:
- get: repo
- put: k8s
params:
paths:
- repo/test/kustomize/overlays/prod
server_dry_run: true
```
#### Diff Resources manifests

```yaml
jobs:
- name: deploy-app
plan:
- get: repo
- put: k8s
params:
paths:
- repo/test/kustomize/overlays/prod
diff: true
```
#### Delete Resources

```yaml
Expand Down
22 changes: 15 additions & 7 deletions cmd/out/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func main() {
if err := kubectl.RunCommand(factory, commandConfig); err != nil {
log.Fatalln("cannot run kubectl command", err)
}
if !request.Params.Delete {
if requireStatusCheck(request.Params) {
time.Sleep(5 * time.Second)
log.Println("check status for", request.Source.WatchResources)
if ok := k8s.CheckResourceStatus(clientset, request.Source.Namespace, request.Source.WatchResources, request.Params.StatusCheckTimeout); !ok {
Expand All @@ -66,24 +66,25 @@ func main() {
}
}

func requireStatusCheck(params models.OutParams) bool {
return !params.Delete && !params.ServerDryRun && !params.Diff
}

func createResponse(request models.OutRequest, clientset kubernetes.Interface) *models.OutResponse {

if request.Params.Delete {
// resources is deleted, so just return empty response
return &models.OutResponse{
Version: models.Version{},
Metadata: nil,
}
return emptyResponse()
}

// apply or undo
version, err := k8s.GetCurrentVersion(&request.Source, clientset)
if err != nil {
log.Fatalln(err)
return emptyResponse()
}
metadatas, err := k8s.GenerateMetadatas(&request.Source, clientset)
if err != nil {
log.Fatalln(err)
return emptyResponse()
}

response := models.OutResponse{
Expand All @@ -93,6 +94,13 @@ func createResponse(request models.OutRequest, clientset kubernetes.Interface) *
return &response
}

func emptyResponse() *models.OutResponse {
return &models.OutResponse{
Version: models.Version{},
Metadata: nil,
}
}

func toDiscoveryInterface(obj interface{}) discovery.DiscoveryInterface {
if discoveryIf, ok := obj.(discovery.DiscoveryInterface); ok {
return discoveryIf
Expand Down
18 changes: 9 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
module github.com/mamezou-tech/concourse-k8s-resource

go 1.13
go 1.14

require (
github.com/aws/aws-lambda-go v1.13.3 // indirect
github.com/spf13/cobra v0.0.5
github.com/emicklei/go-restful v2.9.5+incompatible
github.com/spf13/cobra v1.1.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.4.0
k8s.io/api v0.17.0
k8s.io/apimachinery v0.17.0
k8s.io/cli-runtime v0.17.0
k8s.io/client-go v0.17.0
k8s.io/kubectl v0.17.0
github.com/stretchr/testify v1.6.1
k8s.io/api v0.20.1
k8s.io/apimachinery v0.20.1
k8s.io/cli-runtime v0.20.1
k8s.io/client-go v0.20.1
k8s.io/kubectl v0.20.1
)
536 changes: 420 additions & 116 deletions go.sum

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions pkg/k8s/kubectl/apply_command_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ func (*applyCommandFactory) create(config *CommandConfig) (commands []*Command,
setFlag(command, "record", "true")
setFlag(command, "timeout", strconv.Itoa(int(config.Params.CommandTimeout))+"s")
setManifestPath(command, config.Params)
if config.Params.ServerDryRun {
setFlag(command, "dry-run", "server")
}

commands = append(commands, &Command{command, []string{}})
return
Expand Down
45 changes: 45 additions & 0 deletions pkg/k8s/kubectl/diff_command_factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package kubectl

import (
"github.com/emicklei/go-restful/log"
"github.com/spf13/cobra"
"k8s.io/client-go/util/exec"
"k8s.io/kubectl/pkg/cmd/apply"
"k8s.io/kubectl/pkg/cmd/diff"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
)

type diffCommandFactory struct{}

var _ CommandFactory = &diffCommandFactory{}

func (*diffCommandFactory) create(config *CommandConfig) (commands []*Command, err error) {
factory := createKubectlFactory(config)

options := diff.NewDiffOptions(config.Streams)
command := &cobra.Command{
Use: "diff",
DisableFlagsInUseLine: true,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckDiffErr(options.Complete(factory, cmd))
if err := options.Run(); err != nil {
// exit code == 1 -> there is difference(not error!)
if ee, ok := err.(exec.ExitError); ok && ee.ExitStatus() == 1 {
log.Printf("found difference!")
return
}
// exit with error code(>2)
log.Printf("ERR! %+v", err)
cmdutil.CheckDiffErr(err)
}
},
}
cmdutil.AddFilenameOptionFlags(command, &options.FilenameOptions, "contains the configuration to diff")
cmdutil.AddServerSideApplyFlags(command)
cmdutil.AddFieldManagerFlagVar(command, &options.FieldManager, apply.FieldManagerClientSideApply)

setManifestPath(command, config.Params)

commands = append(commands, &Command{command, []string{}})
return
}
2 changes: 2 additions & 0 deletions pkg/k8s/kubectl/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ func NewCommandFactory(params *models.OutParams) CommandFactory {
return &undoCommandFactory{}
case params.Delete:
return &deleteCommandFactory{}
case params.Diff:
return &diffCommandFactory{}
default:
return &applyCommandFactory{}
}
Expand Down
7 changes: 4 additions & 3 deletions pkg/k8s/kubectl/undo_command_factory.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kubectl

import (
"context"
"fmt"
"github.com/mamezou-tech/concourse-k8s-resource/pkg/k8s"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -24,7 +25,7 @@ func (*undoCommandFactory) create(config *CommandConfig) (commands []*Command, e
var args []string
switch {
case k8s.IsDeployment(resource.Kind):
d, err := config.Clientset.AppsV1().Deployments(config.Namespace).Get(resource.Name, metav1.GetOptions{})
d, err := config.Clientset.AppsV1().Deployments(config.Namespace).Get(context.TODO(), resource.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
Expand All @@ -40,11 +41,11 @@ func (*undoCommandFactory) create(config *CommandConfig) (commands []*Command, e
args = []string{fmt.Sprintf("%s/%s", "deployment", resource.Name)}

case k8s.IsStatefulSet(resource.Kind):
sts, err := config.Clientset.AppsV1().StatefulSets(config.Namespace).Get(resource.Name, metav1.GetOptions{})
sts, err := config.Clientset.AppsV1().StatefulSets(config.Namespace).Get(context.TODO(), resource.Name, metav1.GetOptions{})
if err != nil {
return nil, err
}
rev, err := config.Clientset.AppsV1().ControllerRevisions(config.Namespace).Get(sts.Status.CurrentRevision, metav1.GetOptions{})
rev, err := config.Clientset.AppsV1().ControllerRevisions(config.Namespace).Get(context.TODO(), sts.Status.CurrentRevision, metav1.GetOptions{})
if err != nil {
return nil, err
}
Expand Down
7 changes: 4 additions & 3 deletions pkg/k8s/metadata_reader.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package k8s

import (
"context"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
Expand All @@ -19,7 +20,7 @@ type DeploymentReader struct {
var _ MetadataReader = &DeploymentReader{}

func NewDeploymentReader(clientset kubernetes.Interface, namespace string, name string) (*DeploymentReader, error) {
d, err := clientset.AppsV1().Deployments(namespace).Get(name, metav1.GetOptions{})
d, err := clientset.AppsV1().Deployments(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return nil, err
}
Expand All @@ -42,11 +43,11 @@ type StatefulSetReader struct {
var _ MetadataReader = &StatefulSetReader{}

func NewStatefulSetReader(clientset kubernetes.Interface, namespace string, name string) (*StatefulSetReader, error) {
sts, err := clientset.AppsV1().StatefulSets(namespace).Get(name, metav1.GetOptions{})
sts, err := clientset.AppsV1().StatefulSets(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return nil, err
}
rev, err := clientset.AppsV1().ControllerRevisions(namespace).Get(sts.Status.CurrentRevision, metav1.GetOptions{})
rev, err := clientset.AppsV1().ControllerRevisions(namespace).Get(context.TODO(), sts.Status.CurrentRevision, metav1.GetOptions{})
if err != nil {
return nil, err
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/k8s/status_checker.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package k8s

import (
"context"
"fmt"
"github.com/mamezou-tech/concourse-k8s-resource/pkg/models"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -77,7 +78,7 @@ func (c *statusChecker) check() error {
var current int32
switch {
case IsDeployment(c.resource.Kind):
d, err := c.clientset.AppsV1().Deployments(c.namespace).Get(c.resource.Name, metav1.GetOptions{})
d, err := c.clientset.AppsV1().Deployments(c.namespace).Get(context.TODO(), c.resource.Name, metav1.GetOptions{})
if err != nil {
return err
}
Expand All @@ -97,7 +98,7 @@ func (c *statusChecker) check() error {
return nil
}
case IsStatefulSet(c.resource.Kind):
sts, err := c.clientset.AppsV1().StatefulSets(c.namespace).Get(c.resource.Name, metav1.GetOptions{})
sts, err := c.clientset.AppsV1().StatefulSets(c.namespace).Get(context.TODO(), c.resource.Name, metav1.GetOptions{})
if err != nil {
return err
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/k8s/status_checker_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package k8s

import (
"context"
"github.com/mamezou-tech/concourse-k8s-resource/pkg/models"
"github.com/stretchr/testify/assert"
appv1 "k8s.io/api/apps/v1"
Expand Down Expand Up @@ -68,9 +69,9 @@ func TestCheckResourceStatus(t *testing.T) {
time.AfterFunc(1*time.Second, func() {
t.Log("ready for pod...")
app1rs.Status.ReadyReplicas = 3
clientset.AppsV1().ReplicaSets("test").Update(&app1rs)
clientset.AppsV1().ReplicaSets("test").Update(context.TODO(), &app1rs, metav1.UpdateOptions{})
app2.Status.ReadyReplicas = 2
clientset.AppsV1().StatefulSets("test").Update(&app2)
clientset.AppsV1().StatefulSets("test").Update(context.TODO(), &app2, metav1.UpdateOptions{})
})

ok := CheckResourceStatus(clientset, "test", resources, 5)
Expand Down
6 changes: 5 additions & 1 deletion pkg/models/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,14 @@ type OutParams struct {
StatusCheckTimeout int32 `json:"status_check_timeout"`
// if true, delete resources
Delete bool `json:"delete"`
// if true, rollback previous deployment
// if true, rollback to previous deployment
Undo bool `json:"undo"`
// kubectl timeout seconds
CommandTimeout int32 `json:"command_timeout"`
// if true, execute as dry-run=server
ServerDryRun bool `json:"server_dry_run"`
// if true, run diff command instead of apply
Diff bool `json:"diff"`
}

// concourse metadata
Expand Down
10 changes: 8 additions & 2 deletions test/_internal.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
kubectl create ns concourse
helm repo add concourse https://concourse-charts.storage.googleapis.com/
helm upgrade --install concourse concourse/concourse --namespace concourse \
--set persistence.worker.storageClass=openebs-cstor-sparse \
--set postgresql.persistence.storageClass=openebs-cstor-sparse
--set persistence.worker.storageClass=openebs-hostpath \
--set postgresql.persistence.storageClass=openebs-hostpath

export POD_NAME=$(kubectl get pods --namespace concourse -l "app=concourse-web" -o jsonpath="{.items[0].metadata.name}")
kubectl port-forward --namespace concourse $POD_NAME 8080:8080
Expand Down Expand Up @@ -39,4 +39,10 @@ cat test/json/out_request_undo_kustomize.json | go run cmd/out/main.go
## delete
cat test/json/out_request_delete_plain.json | go run cmd/out/main.go
cat test/json/out_request_delete_kustomize.json | go run cmd/out/main.go
## dry-run
cat test/json/out_request_dryrun_plain.json | go run cmd/out/main.go
cat test/json/out_request_dryrun_kustomize.json | go run cmd/out/main.go
## diff
cat test/json/out_request_diff_plain.json | go run cmd/out/main.go
cat test/json/out_request_diff_kustomize.json | go run cmd/out/main.go
```
Loading