CiscoCloud / distributive

Unit testing for the cloud
Apache License 2.0
147 stars 22 forks source link

Merge distributive and goss #113

Open eirslett opened 8 years ago

eirslett commented 8 years ago

https://github.com/aelsabbahy/goss

Looks like goss does (almost) exactly the same thing as distributive. Would it be possible to merge the two projects? Instead of reimplementing every feature/reinventing the wheel, they could combine efforts and build more features.

@aelsabbahy

langston-barrett commented 8 years ago

Technically, this probably wouldn't be too complex. Distributive has a lot more aspects that it can examine, but is separated into packages that could just as easily be used as part of goss. What do you think @aelsabbahy?

aelsabbahy commented 8 years ago

Just found out about Distributive this morning and https://github.com/milosgajdos83/servpeek by @milosgajdos83 a few days earlier.

Perhaps we can all agree to a shared interface for probing system resources and have that be maintained as a separate package/repo, much like specinfra is a separate gem from serverspec? I haven't looked at the Distributive code yet, but would be interesting to see what checks are there and how each of us approached the problem.

stevendborrelli commented 8 years ago

I like the idea of a shared interface library.

Maybe create a new top-level project that provides a standard way of checking resources on servers that is easy to embed in other go programs.

langston-barrett commented 8 years ago

@aelsabbahy Here's Distributive's interface:

// Check is a unified interface for health checks, it defines only the minimal
// necessary behaviors, while allowing each check to parse and store type-safe
// parameters.
type Check interface {
    // ID is a uniquely identifying check name, like "running" or "temp"
    ID() string
    // New both validates the JSON-provided parameters (list of strings),
    // and parses and stores them in an internal, typed field for later access.
    New(parameters []string) (Check, error)
    // Status returns the status of the check at the instant it is called.
    //
    // msg is a descriptive, human-readable description of the status.
    //
    // code is exit code defining whether or not this check is passing.  0 is
    // considered passing, 1 is failing, with other values reserved for later
    // use.
    Status() (code int, msg string, err error)
}

This is less than ideal, because each check has to parse its arguments from a string if they are actually of a different type (e.g. Port parses its argument to a uint16). The big question for our interface is probably how to maintain both a generic interface and type safety.

aelsabbahy commented 8 years ago

Disributive and goss are almost exactly the same Tried out at Distributive briefly this morning (but haven't looked at code yet) and it has more in common with goss than I originally thought:

Common interface Regarding common interface, I was thinking it would be one level lower than checks (goss use-case is more than just checks). Was thinking something along the lines of how puppet/chef providers work. Checks can be built on top of that, but the library itself should be more generic so it can be leveraged in other ways. For example, package can be something like (full-disclosure, this is very similar to goss's package interface):

// Any package would have to implement this interface
type Package interface {
    // Name of the package
    Name() string
    // Whether the package is installed
    Installed() (bool, error)
    // Versions of the package that are installed
    Versions() ([]string, error)
}

At that point, one can build DebPackage, RpmPackage, GemPackage, or even NullPackage. And can leverage the information in any checking system they want.

From goss's perspective, here are the important requirements from a probing library:

langston-barrett commented 8 years ago

@aelsabbahy Agreed on all points. As a starting point, Distributive has subpackages memstatus, fsstatus, netstatus, systemdstatus, usrstatus, and dockerstatus. I'll email you and we can start a planning document.

aelsabbahy commented 8 years ago

Awesome! :+1:

langston-barrett commented 8 years ago

We're beginning collaboration on this document. Anybody can comment here (or on the doc) with input!

eirslett commented 8 years ago

From the doc:

The only dependencies currently are package commands and service command which are guaranteed to be there.

In an OS with systemd (CentOS, future versions of Debian...) you don't have "service", you have "systemctl". There's no point in abstracting away the difference between them though, so it's maybe best to have one sysvinit package and one systemd package?

eirslett commented 8 years ago
// Any package would have to implement this interface
type Package interface {
    // Name of the package
    Name() string
    // Whether the package is installed
    Installed() (bool, error)
    // Versions of the package that are installed
    Versions() ([]string, error)
}

This looks like a "fact" to me (similar to puppet/ansible facts). One type of check could be to evaluate the facts' values against some criteria. (But that can't model things like health checks based on http calls)

I'm sure this kind of library could be used from consul as well (@armon ?), I believe consul implements almost the same thing for HTTP checks today.

aelsabbahy commented 8 years ago

@eirslett

so it's maybe best to have one sysvinit package and one systemd package?

Agree, with a small modification s/package/systemd struct that implements Service interface/.

To clarify: service is the only external command being called explicitly in goss because dbus is used directly for systemctl, the intent of that sentence was to just highlight the importance limiting the reliance on external tools/commands as much as possible. Apologies if that wasn't clear. Ex:

Interface:

Implementations:

This looks like a "fact" to me (similar to puppet/ansible facts).

Looks more like a provider to me, take a look at the puppet provider for file. The owner, group, mode getters look like the above pseudo-code. Puppet providers also have setters, but I think that's out of scope for this library.

For this library to be compatible with goss I'm going to have to insist that it's not based on/limited to checks, but rather focused on the lower level of probing the system for state. Checks can be built upon that, but shouldn't be in the same library.

One type of check could be to evaluate the facts' values against some criteria. (But that can't model things like health checks based on http calls)

Not sure I follow.. couldn't a health check simply leverage the provider?

func HealthCheckUsingUrlProvider(url string) bool {
    // URLProvider being an example provider from the library in our discussion
    u := URLProvider.New(url)

    if u.Code() == "200" {
      return true
    }
    return false
}
eirslett commented 8 years ago

Yes; a health check should be able to use a provider like that.

eirslett commented 8 years ago

@siddharthist You've already done a great job on the distributive project, and it helps us a lot at FINN!

But how would you feel about merging every feature that distributive has, into goss (given it fits into goss' design?), and then adapt goss for the mantl project going forward? It doesn't look like there are any directly conflicting interests between the two projects; no use cases that distributive solves, but goss, on the other hand, is explicitly meant not to solve?

langston-barrett commented 8 years ago

@eirslett That sounds just fine to me! I think maintaining a separate library would still be of interest for other projects. @stevendborrelli What do you think?

langston-barrett commented 8 years ago

I'm in the process of beginning to use goss as a library for distributive where its resources align. I'll keep track of progress here: