Skip to content
This repository has been archived by the owner on Mar 24, 2023. It is now read-only.

Ship init supports arbitrary upstreams #399

Merged
merged 1 commit into from
Aug 20, 2018
Merged

Ship init supports arbitrary upstreams #399

merged 1 commit into from
Aug 20, 2018

Conversation

dexhorthy
Copy link
Member

@dexhorthy dexhorthy commented Aug 20, 2018

What I Did

Updated how we resolve releases so that in addition to helm charts,
ship can pull an arbitrary directory of k8s manifests.

We now clone the repo into .ship, and then inspect if there's
a Chart.yaml. If there is, we moved the cloned repo path into ./chart,
and generate the values-kustomize lifecycle. If there's no Chart.yaml,
we move it straight to base, and use a minimal lifecycle to drop the user
straight into "kustomize".

This is mostly a POC, and could probably use some tweaks on the
UX side, esp around UI messages and their pacing/verbosity.

It could probably also use some unit testing love, but this has good
integration test coverage for now.

Resolves #268

How I Did it

We've add a new workflow for resolving releases, to try and consolidate
some of the various levels of abstraction in the specs.Resolver and in
kustomize.go. In order:

  • fetch upstream from github or using helm fetch
  • determine application type based on the upstream we fetched. For now, type is one of replicated.app, helm or k8s, but i think we wanna add knative soon
  • Write out upstream and content sha to state and to ShipAppMetadata
  • If helm, add helm chart metadata to ShipAppMetadata as well
  • If helm+fetch, write helm fetch parameters like --chart-version to state.json
  • generate a release based on application type + discovered metadata

There is a lot of stuff moving around in this PR with respect to "how
do we get the release we hand off to daemon/lifecycle", it may be easier to
read the just the new code first, before diving into the diff.

Changes in order of appearance:

  • refactor replicated.app spec fetching into specs/replicatedapp
  • refactor github client into specs/githubclient
  • new specs/apptype package for determining app type
  • move init_chart integration tests to init, which just allows for passing in arbitrary args like --chart-version etc.
  • rename chartURL to upstream everywhere
  • rename HelmChartMetadata to ShipAppMetadata, the this info is
    pretty generic and can apply to non-helm charts
  • Create a .ship/tmp for temporary workspace stuff, to be cleaned up
    when ship terminates. This lets components write to that dir, without
    having to worry about how/when to do a deferred cleanup.
    • Cobra commands do an os.MkdirAll on .ship/tmp before ship starts
    • Cobra commands do an os.RemoveAll on .ship/tmp as ship shuts down
    • On Shutdown, ship does an os.RemoveAll on .ship/tmp (this handles the case where cobra doesn't get to clean up, because ship did an os.Exit)
  • Rename some constants to be more generic for non-helm apps
  • use BackupIfPresent before removing any directories, move the
    function into a utililty package, add a terminal warning when backing up
    directories.
  • Rework specs.Resolver for the new workflow above
  • expand determineApplicationType to handle raw k8s case, as well as the old helm fetch case
  • add methods to migrate state.ChartURL to state.Upstream cleanly.
  • helm asset uses .ship/tmp/.helm as HELM_HOME

Other fun stuff:

  • integration tests pretty-print json before diffing
  • state.Manager saves pretty-printed state json
  • Increase timeout from 60ms to 120ms on a flaky test
  • More fixes for weird tmpdir stuff on osx

How to verify it

Check out integration tests, but you can now do ship init for repo paths that
have no helm chart, just k8s yaml. For example, you can try out my fork of
sysdig cloud's kubernetes installer
:

ship init github.com/dexhorthy/sysdigcloud-kubernetes/sysdigcloud

Description for the Changelog

Support fetching/kustomizing an arbitary directory of Kubernetes manifests with ship init.

Special thanks to @jadengore who helped out on debugging and fixing a few things here,
and whose commits got obliterated in what turned out to be a pretty messy rebase after
the changes for helm fetch.

@dexhorthy dexhorthy requested review from ebramanti and laverya August 20, 2018 13:39
@@ -156,7 +156,7 @@ func TestV2CompleteStep(t *testing.T) {
POST: "/api/v1/navcycle/step/make-the-things",
// need to wait until the async task completes before we check all the expected mock calls,
// otherwise the state won't have been saved yet
WaitForCleanup: func() <-chan time.Time { return time.After(60 * time.Millisecond) },
WaitForCleanup: func() <-chan time.Time { return time.After(120 * time.Millisecond) },
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change is unrelated, just trying to get this test to flake less


// ShipPathInternal is the default folder path of Ship configuration
var ShipPathInternalTmp = path.Join(".ship", "tmp")
var ShipPathInternalTmpHelmHome = path.Join(ShipPathInternalTmp, ".helm")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed some of these paths, and modified them to use path.Join instead of /

"github.com/go-kit/kit/log/level"
"github.com/pkg/errors"
"github.com/spf13/afero"
"github.com/replicatedhq/ship/pkg/util"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved backup logic into util package

archiver.EXPECT().Open(&matchers.StartsWith{Value: "/tmp/dockerlayer"}, &matchers.StartsWith{Value: "/tmp/dockerlayer"}).Return(nil)
archiver.EXPECT().Open(&matchers.StartsWith{Value: "/tmp/dockerlayer"}, asset.Dest).Return(nil)
archiver.EXPECT().Open(&matchers.Contains{Value: "/dockerlayer"}, &matchers.Contains{Value: "/dockerlayer"}).Return(nil)
archiver.EXPECT().Open(&matchers.Contains{Value: "/dockerlayer"}, asset.Dest).Return(nil)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix for tmpdir stuff

@@ -6,15 +6,15 @@ import (

// Commands are Helm commands that are available to the Ship binary.
type Commands interface {
Init() error
Init(home string) error
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to make this .ship/tmp/.helm, I know it probably collides with #396 , happy to let that go in and then resolve conflicts

@@ -98,7 +98,8 @@ func (f *LocalTemplater) Template(
templateArgs = append(templateArgs, args...)

debug.Log("event", "helm.init")
if err := f.Commands.Init(); err != nil {
// todo fix
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

waiting on #396

}, nil
}

func (s *Ship) Shutdown(cancelFunc context.CancelFunc) {
s.FS.RemoveAll(constants.ShipPathInternalTmp)
Copy link
Member Author

@dexhorthy dexhorthy Aug 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This removes the tmp folder in the case that we're gonna os.Exit(1) due to failure, because in that case cobra wont get a change to clean it up

@@ -98,7 +98,8 @@ func (f *LocalTemplater) Template(
templateArgs = append(templateArgs, args...)

debug.Log("event", "helm.init")
if err := f.Commands.Init(); err != nil {
// todo fix
if err := f.Commands.Init(".ship/tmp/.helm"); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this refer to the constant?

@@ -134,7 +134,7 @@ func TestForkTemplater(t *testing.T) {
},
optionAndValuesArgs...,
)
mockCommands.EXPECT().Init().Return(nil)
mockCommands.EXPECT().Init(".ship/tmp/.helm").Return(nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here - IMO this should refer to the helm tempdir constant, not to a literal string

"path/filepath"
"os"

"fmt"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 stdlib imports
4 groups
lol

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"goimports is good"

}

// "sure"
isGithub := strings.HasPrefix(strings.TrimLeft(upstream, "htps:/"), "github.com/")
Copy link
Member

@laverya laverya Aug 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure this should be https
Not htps
Possibly also http

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this could use a test or three

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

htps:/ is a "cutset", I agree this method could use a test, will probably add on in a follow up, since this is getting huge. https://play.golang.org/p/DeCvtN2dM7N

Copy link
Member

@laverya laverya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You couldn't have made this PR any bigger?
Anyways, I like the structure change
LGTM barring comments

What I Did
------------

Updated how we resolve releases so that in addition to helm charts,
ship can pull an arbitrary directory of k8s manifests.

We now clone the repo into .ship, and then inspect if there's
a `Chart.yaml`. If there is, we moved the cloned repo path into `./chart`,
and generate the values-kustomize lifecycle. If there's no `Chart.yaml`,
we move it straight to `base`, and use a minimal lifecycle to drop the user
straight into "kustomize".

This is mostly a POC, and could probably use some tweaks on the
UX side, esp around UI messages and their pacing/verbosity.

It could probably also use some unit testing love, but this has good
integration test coverage for now.

How I Did it
------------

We've add a new workflow for resolving releases, to try and consolidate
some of the various levels of abstraction in the `specs.Resolver` and in
`kustomize.go`. In order:

- fetch upstream from github or using `helm fetch`
- determine application type based on the upstream we fetched. For now, type is one of `replicated.app`, `helm` or `k8s`, but i think we wanna add knative soon
- Write out upstream and content sha to state and to ShipAppMetadata
- If helm, add helm chart metadata to ShipAppMetadata as well
- If helm+fetch, write helm fetch parameters like `--chart-version` to state.json
- generate a release based on application type + discovered metadata

There is a *lot* of stuff moving around in this PR with respect to "how
do we get the release we hand off to daemon/lifecycle", it may be easier to
read the flow of the new code, rather than trying to grok the diff.

Changes in order of appearance:

- refactor replicated.app spec fetching into specs/replicatedapp
- refactor github client into specs/githubclient
- new specs/apptype package for determining app type
- move `init_chart` integration tests to `init`, which just allows for passing in arbitrary args like `--chart-version` etc.
- rename `chartURL` to upstream everywhere
- rename `HelmChartMetadata` to `ShipAppMetadata`, the this info is
  pretty generic and can apply to non-helm charts
- Create a `.ship/tmp` for temporary workspace stuff, to be cleaned up
when ship terminates. This lets components write to that dir, without
having to worry about how/when to do a deferred cleanup.
  - Cobra commands do an os.MkdirAll on `.ship/tmp` before ship starts
  - Cobra commands do an os.RemoveAll on `.ship/tmp` as ship shuts down
  - On Shutdown, ship does an `os.RemoveAll` on `.ship/tmp` (this handles the case where cobra doesn't get to clean up, because ship did an os.Exit)
- Rename some constants to be more generic for non-helm apps
- use `BackupIfPresent` before removing any directories, move the
function into a utililty package, add a terminal warning when backing up
directories.
- Rework specs.Resolver for the new workflow above
- expand determineApplicationType to handle raw k8s case, as well as the old `helm fetch` case
- add methods to migrate `state.ChartURL` to `state.Upstream` cleanly.
- `helm` asset uses `.ship/tmp/.helm` as HELM_HOME

Other fun stuff:

- integration tests pretty-print json before diffing
- `state.Manager` saves pretty-printed state json
- Increase timeout from 60ms to 120ms on a flaky test
- More fixes for weird tmpdir stuff on osx

How to verify it
------------

Check out integration tests, but you can now do `ship init` for repo paths that
have no helm chart, just k8s yaml. For example, you can try out [my fork of
sysdig cloud's kubernetes installer](https://github.com/dexhorthy/sysdigcloud-kubernetes/tree/master/sysdigcloud):

```
ship init github.com/dexhorthy/sysdigcloud-kubernetes/sysdigcloud
```

Description for the Changelog
------------

Support fetching/kustomizing an arbitary directory of Kubernetes manifests with `ship init`.

![](https://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/FrotamericaShipWreck.jpg/2560px-FrotamericaShipWreck.jpg)

Special thanks to @jadengore who helped out on debugging and fixing a few things here,
and whose commits got obliterated in what turned out to be a pretty messy rebase after
the changes for `helm fetch`.
@dexhorthy dexhorthy merged commit 93d7148 into replicatedhq:master Aug 20, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ship init command should work for plain kubernetes yaml also
2 participants