My wish for 0.9: go gettable custom builds

I’m liking what I see with 0.9 so far. Plugins addable via simple import is a good step. The problem that I see is still that a custom build requires you clone caddy and run caddyext in specific ways or change the source by hand. Not a huge fan of that strategy for reproducibility reasons.

What I would really like to do is make my own repo for my build so I can simply go get github.com/myname/mycustomcaddy, and have a full binary ready to go. The mechanism for this is simple, simply define my own main:

package main
import(
  "github.com/mholt/caddy"
  "pluginA"
  "pluginB"
  "pluginN"
)

func main(){
   caddy.Run() //doesn't exist currently
}

The problem I still have is that I cannot import caddy’s main package. But that really is what I would like to do. The solution would be for caddy to move everything main does now into an importable package. I would propose github.com/mholt/caddy has a Main() function and github.com/mholt/caddy/cmd/caddy has a one line main that calls it.

Another thing this would enable is a cmd/caddywitheverything type package in the main repo that has all known plugins enabled via imports as well.

I have brought this up before with mixed response. Maybe discourse is a better venue for discussion on this anyway.

4 Likes

I’m not opposed to this for the 0.9 release. Let me see what I can do… will update you in a few days.

Surprise! :balloon: This didn’t take me as long as I thought. Check it out: https://github.com/mholt/caddy/blob/0.9-wip-readonly/caddy/main.go

All the current Caddy 0.9 progress has been pushed to that branch. (The branch will be deleted before a new one is created which will be able to merge.)

Let me know how that works for you.

1 Like

+1000. Thanks!

Question - don’t you still have to add the name of the custom plugin to the directives array in httpserver/plugin.go? When directives is left unchanged, the following error occurs:

2016/08/22 16:37:25 Caddyfile:3 - Parse error: Unknown directive 'myplugin'

Anyway around this? I want to add a new (internal, not widely useful) plugin without modifying any caddy source.

The server type has to know which order to execute the directives in, so when you add a new directive, it has to be in the right place in the list. I don’t know a way around that. :confused:

We’re so close to having a clean way of extending Caddy. It feels messy to directly modify the source for this one thing.

Maybe it is as simple as adding an optional field or two to caddy.Plugin:

caddy.RegisterPlugin("myauth", caddy.Plugin{
	ServerType: "http",
	Action:      setup,
	InsertAfter: "basicauth", // add InsertBefore to struct as well
})

If neither InsertAfter or InsertBefore is set, or if the directive is already in the array, do nothing to maintain backwards compatibility. Otherwise insert away into the directive’s array.

// https://github.com/golang/go/wiki/SliceTricks

if isStringInList(directives, directive) {
    // do nothing
} else if plugin.InsertAfter != "" && isStringInList(directives, plugin.InsertAfter) {
    // insert it after
} else if plugin.InsertBefore != "" && isStringInList(directives, plugin.InsertBefore) {
    // insert it before
}

Sound OK ?

This has been proposed before. And I’ve looked into it before myself. The problem is that this is relative ordering, not absolute, so correctness is not guaranteed and now your builds are non-deterministic. Yikes.

I have still considered making it possible to dynamically add a directive to that list by exporting a function in the httpserver package, say, RegisterDirective() or something but… this would have to be restricted to development or local builds only where you know what you’re doing. I doubt we could ever rely on relative ordering to power the Caddy plugin ecosystem.

Sure, I now understand the underlying issue. And for me it is indeed a local build issue - I just want to add my little plugin (basicauth with an external configuration file and REST API password matcher) but don’t want to have to modify caddy source. Oh well, I only have to add 1 string to 1 file.

Thank you.

@Steve I’ve committed an experimental function for use only in development: https://github.com/mholt/caddy/commit/5a691fbaf5a19af8280092d77bd794c39e813a3d - mind you, this function throws fatal errors (kills program execution) if there’s a problem and always prints to stdout to remind you it’s for dev purposes only.

The idea is that you can just call the function from your plugin’s init() when you’re developing, and remove it when you’re ready to ship. This way, you can develop your plugin without having to dirty your copy of the Caddy source. (Remember that in order for it to work in production, you’ll still have to add the directive to that list manually. Requires a pull request.) (Also, you still have to add the import for right now, but we’re working on a tool to automate that for you.)