Macro with args, syntax idea

I’m new to Caddy, so there are likely some big holes in my thinking here, but I wanted to see if there was any interest in re-sparking the conversation around adding syntax support for macros with arguments as mentioned in topic #2852 (or if there is currently an ongoing discussion about something along these lines that I missed, please point me in the right direction).

I understand one of the goals of the Caddyfile syntax is to minimize repetition and I think something along the lines of what I’m proposing below could take that goal to the next level.

My Caddyfile on my dev machine has 15 vhosts all setup identically except for the host and the proxy port. Each vhost is defined as shown below, causing a lot of repetition and making my Caddyfile longer than my old comparable nginx config. As far as I can tell, snippets cannot solve this problem.

project-one.tld {
    proxy / localhost:24611 {
        transparent
    }
    tls self_signed
}
# ... +14 more of these

So what if instead I was able to define a macro for my common vhost setup like this:

# define macro
def @vhost(host, proxy_port) {
    [host] {
        proxy / localhost:[proxy_port] {
            transparent
        }
        [...] # defines where the body of the macro should be inserted when called
    }
}

# call macro like a directive with or without a body

@vhost project-one.tld 24611

@vhost project-2.tld 24612 {
    tls self_signed
}

It ends up feeling like a directive when you use it, but the @ saves you from namespace conflicts and let’s you know to look for that definition above (or from an above import).

Thoughts? As I said at the top of this message, I’m sure there are some pretty big holes in my thinking here, but I just wanted to get the conversation going again

Hi Joseph, welcome!

Not really – the goal is to be simple and readable, not necessarily to reduce repetition. Repetition, though not ideal when things change, is at least plain and simple. We really don’t want to make the Caddyfile programmable… instead, opt for config file generation.

Have you looked at Caddyfile snippets? They seem to be closest to what you’re asking for.

I’d still probably opt to use the import directive and perhaps feed it parameters via subdirective block, if I were implementing something like this.

That said, you’re essentially making functions in the Caddyfile. It’s a big leap for complexity. This kind of task is quite well achieved by templating config generation.

Thanks for the quick reply :slight_smile: I was basing my assumption of a goal of minimal repetition off of your blog post here where you said:

A few distinct features of the Caddyfile are: very low duplication, almost no punctuation, irrelevant extraneous whitespace… The Caddyfile is designed to reduce identical, duplicate lines and reduce the amount of typing overall.

If that’s not one of the main goals with the syntax then I see where you’re coming from.

I have seen snippets, and as I understand it they wouldn’t solve my problem. My current caddy file looks like this:

project1.tld {
    proxy / localhost:24611 {
        transparent
    }
    tls self_signed
}
project2.tld {
    proxy / localhost:24610 {
        transparent
    }
    tls self_signed
}
project3.tld {
    proxy / localhost:24615 {
        transparent
    }
    tls self_signed
}
project4.tld {
    proxy / localhost:24616 {
        transparent
    }
    tls self_signed
}
project5.tld {
    proxy / localhost:24618 {
        transparent
    }
    tls self_signed
}
project6.tld {
    proxy / localhost:24622 {
        transparent
    }
    tls self_signed
}
project7.tld {
    proxy / localhost:24625 {
        transparent
    }
    tls self_signed
}
project8.tld {
    proxy / localhost:24634 {
        transparent
    }
    tls self_signed
}
project9.tld {
    proxy / localhost:24639 {
        transparent
    }
    tls self_signed
}
project10.tld {
    proxy / localhost:24639 {
        transparent
    }
    tls self_signed
}
project11.tld {
    proxy / localhost:24642 {
        transparent
    }
    tls self_signed
}
project12.tld {
    proxy / localhost:24664 {
        transparent
    }
    tls self_signed
}
project13.tld {
    proxy / localhost:8001 {
        transparent
    }
    tls self_signed
}
project14.tld {
    proxy / localhost:3001 {
        transparent
    }
    tls self_signed
}
project15.tld {
    proxy / localhost:8000 {
        transparent
    }
    tls self_signed
}

If I understand right, using snippets would only increase the number of lines in my Caddyfile, though it would give me the benefit of only having to edit shared code in one place.

It would be amazing if it could look like this:

def @vhost(host, proxy_port) {
    [host] {
        proxy / localhost:[proxy_port] {
            transparent
        }
        tls self_signed
    }
}

@vhost project1.tld 24611
@vhost project2.tld 24610
@vhost project3.tld 24615
@vhost project4.tld 24616
@vhost project5.tld 24618
@vhost project6.tld 24622
@vhost project7.tld 24625
@vhost project8.tld 24634
@vhost project9.tld 24639
@vhost project10.tld 24639
@vhost project11.tld 24642
@vhost project12.tld 24664
@vhost project13.tld 8001
@vhost project14.tld 3001
@vhost project15.tld 8000

While it definitely is an increase in complexity, I don’t know if I would go as far as calling it a function, it’s more like a super simple template for concise config generation.

Is there a reason that config generation won’t meet your needs?

Yes, config generation would be one way to not have to repeat myself. But it seems more like an escape hatch that allows you to do all sorts of things at the cost of extra project complexity. It’s a tradeoff.

I totally understand if something like this is not a priority for the Caddy project, but I see it as something only slightly more complex than the snippets (there’s no logic in these macros, just simple string formatting), but much more useful.

Thanks for your time responding to this :slight_smile:

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.