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

Caddyfile snippets #1921

Merged
merged 13 commits into from
Nov 13, 2017
Merged

Caddyfile snippets #1921

merged 13 commits into from
Nov 13, 2017

Conversation

captncraig
Copy link

1. What does this change do, exactly?

Allows macros of the form:

macro foo {
  content....
}

or

(foo) {
  content...
}

We had some discussion about particular syntax, so I implemented both. I don't really care which anymore at this point. Just need to choose one.

Can be used in any server with import foo, it just imports the saved macro rather than read a file.

2. Please link to the relevant issues.

https://caddy.community/t/caddyfile-macros-or-inline-includes/2852/8

3. Which documentation changes (if any) need to be made because of this PR?

Needs to be documented in caddyfile docs

4. Checklist

  • I have written tests and verified that they fail without my change
  • I have squashed any insignificant commits
  • This change has comments for package types, values, functions, and non-obvious lines of code
  • I am willing to help maintain this change if there are issues with it later

@mholt
Copy link
Member

mholt commented Oct 14, 2017

Cool, thanks!

I think the name "macro" leaves a bad taste in my mouth after some bad experiences with C. Also, it feels a little weird to call a declarative unit a "macro" when it's not procedural or operative.

That said, I do like the symbolic notation a little better than another keyword exception.

I like the parentheses, personally. Or I still like the idea of single quotes: 'foo' to declare a literal block named foo.

Are there other preferences for this syntax? And what should such a block be called? Just a 'block literal' or a 'reusable block'? Once we decide which syntax to use, I'll begin a review. :)

@miekg
Copy link
Collaborator

miekg commented Oct 14, 2017

Oh, I like the 'foo' syntax as well (and dropping "macro" I assume?)

@mholt
Copy link
Member

mholt commented Oct 14, 2017

Yeah, I'd like to drop 'macro' in favor of some other syntax. Either way, I think beginners will have to learn what the block is/means, whether it's the word macro or a single-quoted word.

@francislavoie
Copy link
Member

francislavoie commented Oct 14, 2017

How about define foo { or something? Personally I think using a keyword really helps make things clear. If we use somekind of syntactic symbol like single quotes, then it won't really fit with the rest of Caddyfile's design in terms of "simple words" for each directive. I personally like macro, but any synonym can do, really.

Other possibilities: var or let

@aaronellington
Copy link

I like the ‘(foo) {‘ syntax. Seems more clear that it is not a normal label.

@msfjarvis
Copy link

I'd love define foo {... or maybe var foo { so that you know that this following block is not your standard virtual host definition. Just my two cents. Great work @captncraig !

@mholt
Copy link
Member

mholt commented Oct 14, 2017

Again, the problem with using a keyword (and I sometimes regret using import as a special case already) is that it's indistinguishable from a hostname. Directives really do not belong outside server blocks.

@francislavoie
Copy link
Member

Do labels ever have spaces though? If you really needed a space in it, wouldn't you do %20 instead?

@mholt
Copy link
Member

mholt commented Oct 14, 2017

It depends on the server type - technically a label can have spaces, but usually you'd do that by enclosing it in quotes "like this" not with URL-encoding... I don't know of a server type that expects a single label with a space.

Multiple labels like this: site1.com site2.com { are a valid way to open a server block.

@elcore
Copy link
Collaborator

elcore commented Oct 14, 2017

I like (foo) and 'foo'

@tobya
Copy link
Collaborator

tobya commented Oct 15, 2017

I like the idea of using a special character such as

$foo
@foo
&foo

otherwise I prefer the use of (foo) to 'foo' since IMHO it is much more obvious that (foo) is not a host name. I think whatever we decide it should be something that it is obvious by looking at it cannot be a valid domain.

I also think we should explicitly not allow . (dot) in label names.

I think this is an excellent PR and support its merging.

@tobya
Copy link
Collaborator

tobya commented Oct 15, 2017

Personally I would love to be able to use this without the import keyword so it can be used like a placeholder.

This would enable me to define a log format for example

(StandardLog) {
        {path} {host} etc
   }

Then use this in the caddyfile where I want

Mydomain.com {
  Log / my file.log {(StandardLog)}
 }

Would make it a lot easier to manage large caddyfile s

@mholt
Copy link
Member

mholt commented Oct 15, 2017

@tobya In that case, I'd prefer the single quote syntax more than parentheses: {'StandardLog'}

But I'm not sure I like that more than just setting and using environment variables (which you can already do): {$StandardLog} - edit: although I dunno if the expansion is recursive... 🤔

@francislavoie
Copy link
Member

@mholt I do know env var expansion doesn't expand out to multiple arguments (see #1235)

@tobya
Copy link
Collaborator

tobya commented Oct 15, 2017

I must admit I was only sort of aware that I could use env variables like that in a caddyfile, however I would much rather have all my declarations in the caddyfile rather than being pulled in from outside.

I'm fine with whichever syntax the majority prefer.

Although this may need to be a separate feature request 😄 as I can see complications.

@captncraig
Copy link
Author

I removed the macro foo syntax, so now it only allows (foo) { }

@captncraig
Copy link
Author

@tobya, I kinda see where you are coming from there. import does currently preclude that because it has to be it's own line.

I don't see why your {{name}} syntax couldn't be added at a future time though. It is fairly orthogonal to this particular change.

Copy link
Collaborator

@tobya tobya left a comment

Choose a reason for hiding this comment

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

The block seems to only work if declared before its use. This may be intentional and may be desired?

p.definedMacros = map[string][]Token{}
}
if p.definedMacros[name] != nil {
p.Errf("redeclaration of previously declared macro %s", name)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This error doesn't seem to be triggered!

I declared two blocks directly after each other and both were parsed, but the second one applied with no error raised.

Copy link
Author

Choose a reason for hiding this comment

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

Ooops, I forgot to actually return the error. Good catch.

@captncraig
Copy link
Author

Yeah, macros only work if pre-declared. The alternative is to pre-scan for all macros and defer loading actual server blocks until we've collected everything. I'm not sure that would be so easy to implement though. I'm ok with this limitation for now.

@captncraig
Copy link
Author

@mholt, looks like the builds are all failing to find go vet. I don't feel like that is related to my change.

@francislavoie
Copy link
Member

@captncraig it's fixed upstream #1929

@mholt
Copy link
Member

mholt commented Oct 19, 2017

I guess gometalinter changed...

So what do we call these exactly? Not macros. Preset blocks? Block presets? Something like that, or something else?

@captncraig
Copy link
Author

I still like macros. I feel that best describes what it is. Yeah, it can be scary remembering C or Lisp or whatever, but it is also familiar to excel users or keyboard macros or things like that. Literally one word that represents a larger block.

Preset is my next favorite, but I don't feel it really captures what it is. I fear it may not be fully applicable in all foreseeable scenarios you may use this in.

Its not a default really, since it needs to be explicitly imported.

snippet may be workable, but I'm not sure.

command, shortcut, define, group, lookup, function, block, insertion. I dunno about any of those.

The only place the chosen term will show up is in the docs and in the code.

Macro still has my vote.

@pgaskin
Copy link

pgaskin commented Oct 20, 2017

@captncraig @mholt How about includes/include.

I prefer (in decreasing order): include, macro, snippet, preset, block.

@francislavoie
Copy link
Member

Considering we have import already, include doesn't fit here.

@tobya
Copy link
Collaborator

tobya commented Oct 20, 2017

I dont like macro.

I would suggest snippet or preferably caddyfile snippet

@captncraig
Copy link
Author

Snippet is growing on me. Since the name only really matters for the doc site. I think:

Reusable Snippets

Makes a tolerably good and descriptive heading.

@mholt
Copy link
Member

mholt commented Oct 20, 2017

Cool, 'snippet' it is then.

Might be worth renaming macro to snippet in the code and then I'm happy to merge this PR (subject to a final review of course)!

@mholt mholt changed the title Caddyfile macros Caddyfile snippets Oct 31, 2017
@mholt
Copy link
Member

mholt commented Oct 31, 2017

(To clarify, once the word "macro" has been replaced in the code with "snippet", I'll do a final review!)

Copy link
Member

@mholt mholt left a comment

Choose a reason for hiding this comment

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

Looking good! Thanks Craig. I just had one insignificant question and then it's on track for being merged.

keys := p.block.Keys
// A snippet block is a single key with parens. Nothing else qualifies.
if len(keys) == 1 && strings.HasPrefix(keys[0], "(") && strings.HasSuffix(keys[0], ")") {
return true, strings.TrimSuffix(keys[0][1:], ")")
Copy link
Member

Choose a reason for hiding this comment

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

Why TrimSuffix instead of just [1:len(keys[0])-1]?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we explicitly check here that the snippet name inside the parens () does not include a . (dot) this will avoid 1 possible route to confusion.

Copy link
Member

Choose a reason for hiding this comment

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

What is confusing about it?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I just think it would be good to avoid

(example.com) {
log example.log "{common}"
}

as a snippet

Copy link
Member

Choose a reason for hiding this comment

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

Hmm. Well, I'm curious to see if that will actually be a problem. How about we wait and find out before we add a restriction...

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I understand why it shouldn't be added as a restriction? It needs to be added from the beginning or not at all, otherwise you will have the potential to break installations. The only legitimate use I could see would be if someone wished to create a snippet

 (snippet for all subdomains of example.com) {
      tls off
   }

I wonder how likely that is. I would suggest it is easier to remove the restriction if people feel it gets in the way rather than add it in at a later point.

Copy link
Member

@mholt mholt Nov 7, 2017

Choose a reason for hiding this comment

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

Hmm, well, I see what you mean... (I think this change requires that the name in parens is only one word, and I assume your example is just an example in that sense) - what do you think @captncraig ?

Copy link
Member

@mholt mholt left a comment

Choose a reason for hiding this comment

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

LGTM

@mholt mholt added this to the 0.10.11 milestone Nov 7, 2017
@captncraig captncraig merged commit 1125a23 into master Nov 13, 2017
@tobya tobya deleted the macros branch January 29, 2018 21:04
@tobya tobya restored the macros branch January 29, 2018 21:04
@mholt mholt deleted the macros branch February 16, 2018 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants