diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..01d20f30
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,8 @@
+.gitignore export-ignore
+.gitattributes export-ignore
+.gitkeep export-ignore
+docs/speeches export-ignore
+docs/video export-ignore
+docs/src/man7 export-ignore
+bin/cdist-build-helper export-ignore
+README-maintainers export-ignore
diff --git a/.gitignore b/.gitignore
index 55374d6d..85a8ccc7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,22 @@
# -vim
-.*.swp
+# Swap
+[._]*.s[a-v][a-z]
+[._]*.sw[a-p]
+[._]s[a-rt-v][a-z]
+[._]ss[a-gi-z]
+[._]sw[a-p]
+
+# Session
+Session.vim
+
+# Temporary
+.netrwhist
+*~
+*.tmp
+# Auto-generated tag files
+tags
+# Persistent undo
+[._]*.un~
# Ignore generated manpages
docs/src/.marker
@@ -7,6 +24,8 @@ docs/src/man1/*.1
docs/src/man7/*.7
docs/src/man7/cdist-type__*.rst
docs/src/cdist-reference.rst
+docs/src/cdist-types.rst
+docs/src/cdist.cfg.skeleton
# Ignore cdist cache for version control
/cache/
@@ -17,15 +36,17 @@ cdist/inventory/
# Python: cache, distutils, distribution in general
__pycache__/
*.pyc
-MANIFEST
+/MANIFEST
dist/
cdist/version.py
+cdist.egg-info/
# sphinx build dirs, cache
_build/
docs/dist
# Ignore temp files used for signing
+cdist-*.tar
cdist-*.tar.gz
cdist-*.tar.gz.asc
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 00000000..a4bc67aa
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,23 @@
+---
+image: code.ungleich.ch:5050/ungleich-public/cdist/cdist-ci:latest
+
+stages:
+ - test
+
+before_script:
+ - ./bin/cdist-build-helper version
+
+shellcheck:
+ stage: test
+ script:
+ - ./bin/cdist-build-helper shellcheck
+
+pycodestyle:
+ stage: test
+ script:
+ - ./bin/cdist-build-helper pycodestyle
+
+unit_tests:
+ stage: test
+ script:
+ - ./bin/cdist-build-helper test
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..14682ad6
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ cdist
+ Copyright (C) 2019 ungleich-public
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ cdist Copyright (C) 2019 ungleich-public
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/Makefile b/Makefile
index e33be3f2..3712511c 100644
--- a/Makefile
+++ b/Makefile
@@ -18,31 +18,30 @@
#
#
-helper=./bin/build-helper
+.PHONY: help
+help:
+ @echo "Please use \`make ' where is one of"
+ @echo "man build only man user documentation"
+ @echo "html build only html user documentation"
+ @echo "docs build both man and html user documentation"
+ @echo "dotman build man pages for types in your ~/.cdist directory"
+ @echo "speeches build speeches pdf files"
+ @echo "install install in the system site-packages directory"
+ @echo "install-user install in the user site-packages directory"
+ @echo "docs-clean clean documentation"
+ @echo "clean clean"
-DOCS_SRC_DIR=docs/src
-SPEECHDIR=docs/speeches
-TYPEDIR=cdist/conf/type
-
-WEBSRCDIR=docs/web
-
-WEBDIR=$$HOME/vcs/www.nico.schottelius.org
-WEBBLOG=$(WEBDIR)/blog
-WEBBASE=$(WEBDIR)/software/cdist
-WEBPAGE=$(WEBBASE).mdwn
-
-CHANGELOG_VERSION=$(shell $(helper) changelog-version)
-CHANGELOG_FILE=docs/changelog
-
-PYTHON_VERSION=cdist/version.py
+DOCS_SRC_DIR=./docs/src
+SPEECHDIR=./docs/speeches
+TYPEDIR=./cdist/conf/type
SPHINXM=make -C $(DOCS_SRC_DIR) man
SPHINXH=make -C $(DOCS_SRC_DIR) html
SPHINXC=make -C $(DOCS_SRC_DIR) clean
+
################################################################################
# Manpages
#
-MAN1DSTDIR=$(DOCS_SRC_DIR)/man1
MAN7DSTDIR=$(DOCS_SRC_DIR)/man7
# Manpages #1: Types
@@ -54,6 +53,7 @@ MANTYPES=$(subst /man.rst,.rst,$(MANTYPEPREFIX))
# Link manpage: do not create man.html but correct named file
$(MAN7DSTDIR)/cdist-type%.rst: $(TYPEDIR)/%/man.rst
+ mkdir -p $(MAN7DSTDIR)
ln -sf "../../../$^" $@
# Manpages #2: reference
@@ -63,11 +63,28 @@ DOCSREFSH=$(DOCS_SRC_DIR)/cdist-reference.rst.sh
$(DOCSREF): $(DOCSREFSH)
$(DOCSREFSH)
+# Html types list with references
+DOCSTYPESREF=$(MAN7DSTDIR)/cdist-types.rst
+DOCSTYPESREFSH=$(DOCS_SRC_DIR)/cdist-types.rst.sh
+
+$(DOCSTYPESREF): $(DOCSTYPESREFSH)
+ $(DOCSTYPESREFSH)
+
+DOCSCFGSKEL=./configuration/cdist.cfg.skeleton
+
+configskel: $(DOCSCFGSKEL)
+ cp -f "$(DOCSCFGSKEL)" "$(DOCS_SRC_DIR)/"
+
+version:
+ @[ -f "cdist/version.py" ] || { \
+ printf "Missing 'cdist/version.py', please generate it first.\n" && exit 1; \
+ }
+
# Manpages #3: generic part
-man: $(MANTYPES) $(DOCSREF) $(PYTHON_VERSION)
+man: version configskel $(MANTYPES) $(DOCSREF) $(DOCSTYPESREF)
$(SPHINXM)
-html: $(MANTYPES) $(DOCSREF) $(PYTHON_VERSION)
+html: version configskel $(MANTYPES) $(DOCSREF) $(DOCSTYPESREF)
$(SPHINXH)
docs: man html
@@ -75,24 +92,6 @@ docs: man html
docs-clean:
$(SPHINXC)
-# Manpages #5: release part
-MANWEBDIR=$(WEBBASE)/man/$(CHANGELOG_VERSION)
-HTMLBUILDDIR=docs/dist/html
-
-docs-dist: html
- rm -rf "${MANWEBDIR}"
- mkdir -p "${MANWEBDIR}"
- # mkdir -p "${MANWEBDIR}/man1" "${MANWEBDIR}/man7"
- # cp ${MAN1DSTDIR}/*.html ${MAN1DSTDIR}/*.css ${MANWEBDIR}/man1
- # cp ${MAN7DSTDIR}/*.html ${MAN7DSTDIR}/*.css ${MANWEBDIR}/man7
- cp -R ${HTMLBUILDDIR}/* ${MANWEBDIR}
- cd ${MANWEBDIR} && git add . && git commit -m "cdist manpages update: $(CHANGELOG_VERSION)" || true
-
-man-latest-link: web-pub
- # Fix ikiwiki, which does not like symlinks for pseudo security
- ssh staticweb.ungleich.ch \
- "cd /home/services/www/nico/nico.schottelius.org/www/software/cdist/man/ && rm -f latest && ln -sf "$(CHANGELOG_VERSION)" latest"
-
# Manpages: .cdist Types
DOT_CDIST_PATH=${HOME}/.cdist
DOTMAN7DSTDIR=$(MAN7DSTDIR)
@@ -105,8 +104,7 @@ DOTMANTYPES=$(subst /man.rst,.rst,$(DOTMANTYPEPREFIX))
$(DOTMAN7DSTDIR)/cdist-type%.rst: $(DOTTYPEDIR)/%/man.rst
ln -sf "$^" $@
-# Manpages #3: generic part
-dotman: $(DOTMANTYPES)
+dotman: version configskel $(DOTMANTYPES) $(DOCSREF) $(DOCSTYPESREF)
$(SPHINXM)
################################################################################
@@ -114,7 +112,6 @@ dotman: $(DOTMANTYPES)
#
SPEECHESOURCES=$(SPEECHDIR)/*.tex
SPEECHES=$(SPEECHESOURCES:.tex=.pdf)
-SPEECHESWEBDIR=$(WEBBASE)/speeches
# Create speeches and ensure Toc is up-to-date
$(SPEECHDIR)/%.pdf: $(SPEECHDIR)/%.tex
@@ -124,128 +121,28 @@ $(SPEECHDIR)/%.pdf: $(SPEECHDIR)/%.tex
speeches: $(SPEECHES)
-speeches-dist: speeches
- rm -rf "${SPEECHESWEBDIR}"
- mkdir -p "${SPEECHESWEBDIR}"
- cp ${SPEECHES} "${SPEECHESWEBDIR}"
- cd ${SPEECHESWEBDIR} && git add . && git commit -m "cdist speeches updated" || true
-
################################################################################
-# Website
+# Misc
#
-
-BLOGFILE=$(WEBBLOG)/cdist-$(CHANGELOG_VERSION)-released.mdwn
-
-$(BLOGFILE): $(CHANGELOG_FILE)
- $(helper) blog $(CHANGELOG_VERSION) $(BLOGFILE)
-
-web-blog: $(BLOGFILE)
-
-web-doc:
- # Go to top level, because of cdist.mdwn
- rsync -av "$(WEBSRCDIR)/" "${WEBBASE}/.."
- cd "${WEBBASE}/.." && git add cdist* && git commit -m "cdist doc update" cdist* || true
-
-web-dist: web-blog web-doc
-
-web-pub: web-dist docs-dist speeches-dist
- cd "${WEBDIR}" && make pub
-
-web-release-all: man-latest-link
-web-release-all-no-latest: web-pub
-
-################################################################################
-# Release: Mailinglist
-#
-ML_FILE=.lock-ml
-
-# Only send mail once - lock until new changelog things happened
-$(ML_FILE): $(CHANGELOG_FILE)
- $(helper) ml-release $(CHANGELOG_VERSION)
- touch $@
-
-ml-release: $(ML_FILE)
-
-
-################################################################################
-# pypi
-#
-PYPI_FILE=.pypi-release
-$(PYPI_FILE): man $(PYTHON_VERSION)
- python3 setup.py sdist upload
- touch $@
-
-pypi-release: $(PYPI_FILE)
-################################################################################
-# archlinux
-#
-ARCHLINUX_FILE=.lock-archlinux
-ARCHLINUXTAR=cdist-$(CHANGELOG_VERSION)-1.src.tar.gz
-
-$(ARCHLINUXTAR): PKGBUILD
- umask 022; mkaurball
-
-PKGBUILD: PKGBUILD.in $(PYTHON_VERSION)
- ./PKGBUILD.in $(CHANGELOG_VERSION)
-
-$(ARCHLINUX_FILE): $(ARCHLINUXTAR) $(PYTHON_VERSION)
- burp -c system $(ARCHLINUXTAR)
- touch $@
-
-archlinux-release: $(ARCHLINUX_FILE)
-
-################################################################################
-# Release
-#
-
-$(PYTHON_VERSION) version: .git/refs/heads/master
- $(helper) version
-
-# Code that is better handled in a shell script
-check-%:
- $(helper) $@
-
-release:
- $(helper) $@
-
-################################################################################
-# Cleanup
-#
-
-clean:
+clean: docs-clean
rm -f $(DOCS_SRC_DIR)/cdist-reference.rst
+ rm -f $(DOCS_SRC_DIR)/cdist-types.rst
+ rm -f $(DOCS_SRC_DIR)/cdist.cfg.skeleton
find "$(DOCS_SRC_DIR)" -mindepth 2 -type l \
| xargs rm -f
- make -C $(DOCS_SRC_DIR) clean
-
find * -name __pycache__ | xargs rm -rf
- # Archlinux
- rm -f cdist-*.pkg.tar.xz cdist-*.tar.gz
- rm -rf pkg/ src/
-
- rm -f MANIFEST PKGBUILD
- rm -rf dist/
-
- # Signed release
- rm -f cdist-*.tar.gz
- rm -f cdist-*.tar.gz.asc
-
-distclean: clean
- rm -f cdist/version.py
+ # distutils
+ rm -rf ./build
################################################################################
-# Misc
+# install
#
-# The pub is Nico's "push to all git remotes" way ("make pub")
-pub:
- git push --mirror
+install:
+ python3 setup.py install
-test:
- $(helper) $@
-
-pep8:
- $(helper) $@
+install-user:
+ python3 setup.py install --user
diff --git a/PKGBUILD.in b/PKGBUILD.in
index c967249d..c0188e68 100755
--- a/PKGBUILD.in
+++ b/PKGBUILD.in
@@ -9,7 +9,7 @@ pkgver=$version
pkgrel=1
pkgdesc='A Usable Configuration Management System"'
arch=('any')
-url='http://www.nico.schottelius.org/software/cdist/'
+url='https://www.cdi.st/'
license=('GPL3')
depends=('python>=3.2.0')
source=("http://pypi.python.org/packages/source/c/cdist/cdist-\${pkgver}.tar.gz")
diff --git a/README b/README
deleted file mode 100644
index a67e25e3..00000000
--- a/README
+++ /dev/null
@@ -1,6 +0,0 @@
-cdist
------
-
-cdist is a usable configuration management system.
-
-For the web documentation have a look at docs/web/.
diff --git a/README-maintainers b/README-maintainers
new file mode 100644
index 00000000..5766dd7d
--- /dev/null
+++ b/README-maintainers
@@ -0,0 +1,4 @@
+Maintainers should use ./bin/cdist-build-helper script.
+
+Makefile is intended for end users. It can be used for non-maintaining
+targets that can be run from pure source (without git repository).
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..de6901c7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+# cdist
+
+**cdist** is a usable configuration management system.
+
+It adheres to the [**KISS principle**](https://en.wikipedia.org/wiki/KISS_principle)
+and is being used in small up to enterprise grade environments.
+
+For more information have a look at [**homepage**](https://cdi.st)
+or at **``docs/src``** for manual in **reStructuredText** format.
+
+## Contributing
+
+Merge/Pull requests can be made in both
+[upstream **GitLab**](https://code.ungleich.ch/ungleich-public/cdist/merge_requests)
+(managed by [**ungleich**](https://ungleich.ch))
+and [**GitHub** project](https://github.com/ungleich/cdist/pulls).
+
+Issues can be made and other project management activites happen
+[**only in GitLab**](https://code.ungleich.ch/ungleich-public/cdist)
+(needs [**ungleich** account](https://account.ungleich.ch)).
+
+For community-maintained types there is
+[**cdist-contrib** project](https://code.ungleich.ch/ungleich-public/cdist-contrib).
+
+## Participating
+
+IRC: ``#cdist`` @ freenode
+
+Matrix: ``#cdist:ungleich.ch``
+
+Mattermost: https://chat.ungleich.ch/ungleich/channels/cdist
diff --git a/bin/build-helper b/bin/build-helper
deleted file mode 100755
index 46b139d1..00000000
--- a/bin/build-helper
+++ /dev/null
@@ -1,406 +0,0 @@
-#!/bin/sh
-#
-# 2011-2013 Nico Schottelius (nico-cdist at schottelius.org)
-#
-# This file is part of cdist.
-#
-# cdist is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# cdist is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with cdist. If not, see .
-#
-#
-# This file contains the heavy lifting found usually in the Makefile
-#
-
-basedir=${0%/*}/../
-# Change to checkout directory
-cd "$basedir"
-
-version=$(git describe)
-
-option=$1; shift
-
-case "$option" in
- changelog-changes)
- if [ "$#" -eq 1 ]; then
- start=$1
- else
- start="[[:digit:]]"
- fi
-
- end="[[:digit:]]"
-
- awk -F: "BEGIN { start=0 }
- {
- if(start == 0) {
- if (\$0 ~ /^$start/) {
- start = 1
- }
- } else {
- if (\$0 ~ /^$end/) {
- exit
- } else {
- print \$0
- }
- }
- }" "$basedir/docs/changelog"
- ;;
-
- changelog-version)
- # get version from changelog
- grep '^[[:digit:]]' "$basedir/docs/changelog" | head -n1 | sed 's/:.*//'
- ;;
-
- check-date)
- # verify date in changelog is today
- date_today="$(date +%Y-%m-%d)"
- date_changelog=$(grep '^[[:digit:]]' "$basedir/docs/changelog" | head -n1 | sed 's/.*: //')
-
- if [ "$date_today" != "$date_changelog" ]; then
- echo "Date in changelog is not today"
- echo "Changelog: $date_changelog"
- exit 1
- fi
- ;;
-
- check-unittest)
- "$0" test
- ;;
-
- blog)
- version=$1; shift
- blogfile=$1; shift
- dir=${blogfile%/*}
- file=${blogfile##*/}
-
-
- cat << eof > "$blogfile"
-[[!meta title="Cdist $version released"]]
-
-Here's a short overview about the changes found in version ${version}:
-
-eof
-
- $0 changelog-changes "$version" >> "$blogfile"
-
- cat << eof >> "$blogfile"
-For more information visit the [[cdist homepage|software/cdist]].
-
-[[!tag cdist config unix]]
-eof
- cd "$dir"
- git add "$file"
- # Allow git commit to fail if there are no changes
- git commit -m "cdist blog update: $version" "$blogfile" || true
- ;;
-
- ml-release)
- if [ $# -ne 1 ]; then
- echo "$0 ml-release version" >&2
- exit 1
- fi
-
- version=$1; shift
-
- to_a=cdist
- to_d=l.schottelius.org
- to=${to_a}@${to_d}
-
- from_a=nico-cdist
- from_d=schottelius.org
- from=${from_a}@${from_d}
-
- (
- cat << eof
-From: Nico -telmich- Schottelius <$from>
-To: cdist mailing list <$to>
-Subject: cdist $version released
-
-Hello .*,
-
-cdist $version has been released with the following changes:
-
-eof
-
- "$0" changelog-changes "$version"
- cat << eof
-
-Cheers,
-
-Nico
-
---
-Automatisation at its best level. With cdist.
-eof
- ) | /usr/sbin/sendmail -f "$from" "$to"
- ;;
-
-
- release-git-tag)
- target_version=$($0 changelog-version)
- if git rev-parse --verify refs/tags/$target_version 2>/dev/null; then
- echo "Tag for $target_version exists, aborting"
- exit 1
- fi
- printf "Enter tag description for ${target_version}: "
- read tagmessage
-
- # setup for signed tags:
- # gpg --fulL-gen-key
- # gpg --list-secret-keys --keyid-format LONG
- # git config --local user.signingkey
- # for exporting pub key:
- # gpg --armor --export > pubkey.asc
- # gpg --output pubkey.gpg --export
- # show tag with signature
- # git show
- # verify tag signature
- # git tag -v
- #
- # gpg verify signature
- # gpg --verify
- # gpg --no-default-keyring --keyring --verify
- # Ensure gpg-agent is running.
- export GPG_TTY=$(tty)
- gpg-agent
-
- git tag -s "$target_version" -m "$tagmessage"
- git push --tags
- ;;
-
- sign-git-release)
- if [ $# -lt 2 ]
- then
- printf "usage: $0 sign-git-release TAG TOKEN [ARCHIVE]\n"
- printf " if ARCHIVE is not specified then it is created\n"
- exit 1
- fi
- tag="$1"
- if ! git rev-parse -q --verify "${tag}" >/dev/null 2>&1
- then
- printf "Tag \"${tag}\" not found.\n"
- exit 1
- fi
- token="$2"
- if [ $# -gt 2 ]
- then
- archivename="$3"
- else
- archivename="cdist-${tag}.tar.gz"
- git archive --prefix="cdist-${tag}/" -o "${archivename}" "${tag}" \
- || exit 1
- fi
- gpg --armor --detach-sign "${archivename}" || exit 1
-
- # make github release
- curl -H "Authorization: token ${token}" \
- --request POST \
- --data "{ \"tag_name\":\"${tag}\", \
- \"target_commitish\":\"master\", \
- \"name\": \"${tag}\", \
- \"body\":\"${tag}\", \
- \"draft\":false, \
- \"prerelease\": false}" \
- "https://api.github.com/repos/ungleich/cdist/releases" || exit 1
-
- # get release ID
- repoid=$(curl "https://api.github.com/repos/ungleich/cdist/releases/tags/${tag}" \
- | python3 -c 'import json; import sys; print(json.loads(sys.stdin.read())["id"])') \
- || exit 1
-
- # upload archive and then signature
- curl -H "Authorization: token ${token}" \
- -H "Accept: application/vnd.github.manifold-preview" \
- -H "Content-Type: application/x-gtar" \
- --data-binary @${archivename} \
- "https://uploads.github.com/repos/ungleich/cdist/releases/${repoid}/assets?name=${archivename}" \
- || exit 1
- curl -H "Authorization: token ${token}" \
- -H "Accept: application/vnd.github.manifold-preview" \
- -H "Content-Type: application/pgp-signature" \
- --data-binary @${archivename}.asc \
- "https://uploads.github.com/repos/ungleich/cdist/releases/${repoid}/assets?name=${archivename}.asc" \
- || exit 1
-
- # remove generated files (archive and asc)
- if [ $# -eq 2]
- then
- rm -f "${archivename}"
- fi
- rm -f "${archivename}.asc"
- ;;
-
- release)
- set -e
- target_version=$($0 changelog-version)
- target_branch=$($0 version-branch)
-
- echo "Beginning release process for $target_version"
-
- # First check everything is sane
- "$0" check-date
- "$0" check-unittest
- "$0" check-pep8
-
- # Generate version file to be included in packaging
- "$0" target-version
-
- # Ensure the git status is clean, else abort
- if ! git diff-index --name-only --exit-code HEAD ; then
- echo "Unclean tree, see files above, aborting"
- exit 1
- fi
-
- # Ensure we are on the master branch
- masterbranch=yes
- if [ "$(git rev-parse --abbrev-ref HEAD)" != "master" ]; then
- echo "Releases are happening from the master branch, aborting"
-
- echo "Enter the magic word to release anyway"
- read magicword
-
- if [ "$magicword" = "iknowwhatido" ]; then
- masterbranch=no
- else
- exit 1
- fi
- fi
-
- if [ "$masterbranch" = yes ]; then
- # Ensure version branch exists
- if ! git rev-parse --verify refs/heads/$target_branch 2>/dev/null; then
- git branch "$target_branch"
- fi
-
- # Merge master branch into version branch
- git checkout "$target_branch"
- git merge master
- fi
-
- # Verify that after the merge everything works
- "$0" check-date
- "$0" check-unittest
-
- # Generate documentation (man and html)
- # First, clean old generated docs
- make docs-clean
- make docs
-
- # Generate speeches (indirect check if they build)
- make speeches
-
- #############################################################
- # Everything green, let's do the release
-
- # Tag the current commit
- "$0" release-git-tag
-
- # sign git tag
- printf "Enter github authentication token: "
- read token
- "$0" sign-git-release "${target_version}" "${token}"
-
- # Also merge back the version branch
- if [ "$masterbranch" = yes ]; then
- git checkout master
- git merge "$target_branch"
- fi
-
- # Publish git changes
- make pub
-
- # publish man, speeches, website
- if [ "$masterbranch" = yes ]; then
- make web-release-all
- else
- make web-release-all-no-latest
- fi
-
- # Ensure that pypi release has the right version
- "$0" version
-
- # Create and publish package for pypi
- make pypi-release
-
- # Archlinux release is based on pypi
- make archlinux-release
-
- # Announce change on ML
- make ml-release
-
- cat << eof
-Manual steps post release:
-
- - linkedin
- - hackernews
- - reddit
- - twitter
-
-eof
-
- ;;
-
- test)
- export PYTHONPATH="$(pwd -P)"
-
- if [ $# -lt 1 ]; then
- python3 -m cdist.test
- else
- python3 -m unittest "$@"
- fi
- ;;
-
- pep8)
- pep8 "${basedir}" "${basedir}/scripts/cdist" | less
- ;;
-
- check-pep8)
- "$0" pep8
- echo "Please review pep8 report."
- while true
- do
- echo "Continue (yes/no)?"
- any=
- read any
- case "$any" in
- yes)
- break
- ;;
- no)
- exit 1
- ;;
- *)
- echo "Please answer with 'yes' or 'no' explicitly."
- ;;
- esac
- done
- ;;
-
- version-branch)
- "$0" changelog-version | cut -d. -f '1,2'
- ;;
-
- version)
- echo "VERSION = \"$(git describe)\"" > cdist/version.py
- ;;
-
- target-version)
- target_version=$($0 changelog-version)
- echo "VERSION = \"${target_version}\"" > cdist/version.py
- ;;
-
- *)
- echo "Unknown helper target $@ - aborting"
- exit 1
- ;;
-
-esac
diff --git a/bin/build-helper.freebsd b/bin/build-helper.freebsd
deleted file mode 100755
index 183129db..00000000
--- a/bin/build-helper.freebsd
+++ /dev/null
@@ -1,468 +0,0 @@
-#!/bin/sh
-#
-# 2011-2013 Nico Schottelius (nico-cdist at schottelius.org)
-# 2016 Darko Poljak (darko.poljak at gmail.com)
-#
-# This file is part of cdist.
-#
-# cdist is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# cdist is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with cdist. If not, see .
-#
-#
-# This file contains the heavy lifting found usually in the Makefile
-#
-
-# vars for make
-helper=$0
-
-basedir=${0%/*}/../
-# run_as is used to check how the script is called (by $0 value)
-# currently supported sufixes for $0 are:
-# .freebsd - run as freebsd
-basename=${0##*/}
-run_as=${basename#*.}
-case "$run_as" in
- freebsd)
- to_a=cdist-configuration-management
- to_d=googlegroups.com
- from_a=darko.poljak
- from_d=gmail.com
- ml_name="Darko Poljak"
- ml_sig_name="Darko"
-
- # vars for make
- WEBDIR=../vcs/www.nico.schottelius.org
- ;;
- *)
- to_a=cdist
- to_d=l.schottelius.org
- from_a=nico-cdist
- from_d=schottelius.org
- ml_name="Nico -telmich- Schottelius"
- ml_sig_name="Nico"
-
- # vars for make
- WEBDIR=$$HOME/vcs/www.nico.schottelius.org
- ;;
-esac
-
-# Change to checkout directory
-cd "$basedir"
-
-version=$(git describe)
-
-option=$1; shift
-
-case "$option" in
- print-make-vars)
- printf "helper: ${helper}\n"
- printf "WEBDIR: ${WEBDIR}\n"
- ;;
- print-runas)
- printf "run_as: $run_as\n"
- ;;
- changelog-changes)
- if [ "$#" -eq 1 ]; then
- start=$1
- else
- start="[[:digit:]]"
- fi
-
- end="[[:digit:]]"
-
- awk -F: "BEGIN { start=0 }
- {
- if(start == 0) {
- if (\$0 ~ /^$start/) {
- start = 1
- }
- } else {
- if (\$0 ~ /^$end/) {
- exit
- } else {
- print \$0
- }
- }
- }" "$basedir/docs/changelog"
- ;;
-
- changelog-version)
- # get version from changelog
- grep '^[[:digit:]]' "$basedir/docs/changelog" | head -n1 | sed 's/:.*//'
- ;;
-
- check-date)
- # verify date in changelog is today
- date_today="$(date +%Y-%m-%d)"
- date_changelog=$(grep '^[[:digit:]]' "$basedir/docs/changelog" | head -n1 | sed 's/.*: //')
-
- if [ "$date_today" != "$date_changelog" ]; then
- echo "Date in changelog is not today"
- echo "Changelog: $date_changelog"
- exit 1
- fi
- ;;
-
- check-unittest)
- "$0" test
- ;;
-
- blog)
- version=$1; shift
- blogfile=$1; shift
- dir=${blogfile%/*}
- file=${blogfile##*/}
-
-
- cat << eof > "$blogfile"
-[[!meta title="Cdist $version released"]]
-
-Here's a short overview about the changes found in version ${version}:
-
-eof
-
- $0 changelog-changes "$version" >> "$blogfile"
-
- cat << eof >> "$blogfile"
-For more information visit the [[cdist homepage|software/cdist]].
-
-[[!tag cdist config unix]]
-eof
- cd "$dir"
- git add "$file"
- # Allow git commit to fail if there are no changes
- git commit -m "cdist blog update: $version" "$blogfile" || true
- ;;
-
- ml-release)
- if [ $# -ne 1 ]; then
- echo "$0 ml-release version" >&2
- exit 1
- fi
-
- version=$1; shift
-
- to=${to_a}@${to_d}
- from=${from_a}@${from_d}
-
- (
- cat << eof
-From: ${ml_name} <$from>
-To: cdist mailing list <$to>
-Subject: cdist $version released
-
-Hello .*,
-
-cdist $version has been released with the following changes:
-
-eof
-
- "$0" changelog-changes "$version"
- cat << eof
-
-Cheers,
-
-${ml_sig_name}
-
---
-Automatisation at its best level. With cdist.
-eof
- ) | /usr/sbin/sendmail -f "$from" "$to"
- ;;
-
- release-git-tag)
- target_version=$($0 changelog-version)
- if git rev-parse --verify refs/tags/$target_version 2>/dev/null; then
- echo "Tag for $target_version exists, aborting"
- exit 1
- fi
- printf "Enter tag description for ${target_version}: "
- read tagmessage
-
- # setup for signed tags:
- # gpg --fulL-gen-key
- # gpg --list-secret-keys --keyid-format LONG
- # git config --local user.signingkey
- # for exporting pub key:
- # gpg --armor --export > pubkey.asc
- # gpg --output pubkey.gpg --export
- # show tag with signature
- # git show
- # verify tag signature
- # git tag -v
- #
- # gpg verify signature
- # gpg --verify
- # gpg --no-default-keyring --keyring --verify
- # Ensure gpg-agent is running.
- export GPG_TTY=$(tty)
- gpg-agent
-
- git tag -s "$target_version" -m "$tagmessage"
- git push --tags
- ;;
-
- sign-git-release)
- if [ $# -lt 2 ]
- then
- printf "usage: $0 sign-git-release TAG TOKEN [ARCHIVE]\n"
- printf " if ARCHIVE is not specified then it is created\n"
- exit 1
- fi
- tag="$1"
- if ! git rev-parse -q --verify "${tag}" >/dev/null 2>&1
- then
- printf "Tag \"${tag}\" not found.\n"
- exit 1
- fi
- token="$2"
- if [ $# -gt 2 ]
- then
- archivename="$3"
- else
- archivename="cdist-${tag}.tar.gz"
- git archive --prefix="cdist-${tag}/" -o "${archivename}" "${tag}" \
- || exit 1
- fi
- gpg --armor --detach-sign "${archivename}" || exit 1
-
- # make github release
- curl -H "Authorization: token ${token}" \
- --request POST \
- --data "{ \"tag_name\":\"${tag}\", \
- \"target_commitish\":\"master\", \
- \"name\": \"${tag}\", \
- \"body\":\"${tag}\", \
- \"draft\":false, \
- \"prerelease\": false}" \
- "https://api.github.com/repos/ungleich/cdist/releases" || exit 1
-
- # get release ID
- repoid=$(curl "https://api.github.com/repos/ungleich/cdist/releases/tags/${tag}" \
- | python3 -c 'import json; import sys; print(json.loads(sys.stdin.read())["id"])') \
- || exit 1
-
- # upload archive and then signature
- curl -H "Authorization: token ${token}" \
- -H "Accept: application/vnd.github.manifold-preview" \
- -H "Content-Type: application/x-gtar" \
- --data-binary @${archivename} \
- "https://uploads.github.com/repos/ungleich/cdist/releases/${repoid}/assets?name=${archivename}" \
- || exit 1
- curl -H "Authorization: token ${token}" \
- -H "Accept: application/vnd.github.manifold-preview" \
- -H "Content-Type: application/pgp-signature" \
- --data-binary @${archivename}.asc \
- "https://uploads.github.com/repos/ungleich/cdist/releases/${repoid}/assets?name=${archivename}.asc" \
- || exit 1
-
- # remove generated files (archive and asc)
- if [ $# -eq 2]
- then
- rm -f "${archivename}"
- fi
- rm -f "${archivename}.asc"
- ;;
-
- release)
- set -e
- target_version=$($0 changelog-version)
- target_branch=$($0 version-branch)
-
- echo "Beginning release process for $target_version"
-
- # First check everything is sane
- "$0" check-date
- "$0" check-unittest
- "$0" check-pep8
-
- # Generate version file to be included in packaging
- "$0" target-version
-
- # Ensure the git status is clean, else abort
- if ! git diff-index --name-only --exit-code HEAD ; then
- echo "Unclean tree, see files above, aborting"
- exit 1
- fi
-
- # Ensure we are on the master branch
- masterbranch=yes
- if [ "$(git rev-parse --abbrev-ref HEAD)" != "master" ]; then
- echo "Releases are happening from the master branch, aborting"
-
- echo "Enter the magic word to release anyway"
- read magicword
-
- if [ "$magicword" = "iknowwhatido" ]; then
- masterbranch=no
- else
- exit 1
- fi
- fi
-
- if [ "$masterbranch" = yes ]; then
- # Ensure version branch exists
- if ! git rev-parse --verify refs/heads/$target_branch 2>/dev/null; then
- git branch "$target_branch"
- fi
-
- # Merge master branch into version branch
- git checkout "$target_branch"
- git merge master
- fi
-
- # Verify that after the merge everything works
- "$0" check-date
- "$0" check-unittest
-
- # Generate documentation (man and html)
- # First, clean old generated docs
- make helper=${helper} WEBDIR=${WEBDIR} docs-clean
- make helper=${helper} WEBDIR=${WEBDIR} docs
-
- # Generate speeches (indirect check if they build)
- make helper=${helper} WEBDIR=${WEBDIR} speeches
-
- #############################################################
- # Everything green, let's do the release
-
- # Tag the current commit
- "$0" release-git-tag
-
- # sign git tag
- printf "Enter github authentication token: "
- read token
- "$0" sign-git-release "${target_version}" "${token}"
-
- # Also merge back the version branch
- if [ "$masterbranch" = yes ]; then
- git checkout master
- git merge "$target_branch"
- fi
-
- # Publish git changes
- case "$run_as" in
- freebsd)
- # if we are not Nico :) then just push, no mirror
- git push
- # push also new branch and set up tracking
- git push -u origin "${target_branch}"
- ;;
- *)
- make helper=${helper} WEBDIR=${WEBDIR} pub
- ;;
- esac
-
- # publish man, speeches, website
- if [ "$masterbranch" = yes ]; then
- make helper=${helper} WEBDIR=${WEBDIR} web-release-all
- else
- make helper=${helper} WEBDIR=${WEBDIR} web-release-all-no-latest
- fi
-
- # Ensure that pypi release has the right version
- "$0" version
-
- # Create and publish package for pypi
- make helper=${helper} WEBDIR=${WEBDIR} pypi-release
-
- case "$run_as" in
- freebsd)
- ;;
- *)
- # Archlinux release is based on pypi
- make archlinux-release
- ;;
- esac
-
- # Announce change on ML
- make helper=${helper} WEBDIR=${WEBDIR} ml-release
-
- cat << eof
-Manual steps post release:
-
- - linkedin
- - hackernews
- - reddit
- - twitter
-
-eof
-
- case "$run_as" in
- freebsd)
- cat < cdist/version.py
- ;;
-
- target-version)
- target_version=$($0 changelog-version)
- echo "VERSION = \"${target_version}\"" > cdist/version.py
- ;;
-
- *)
- echo "Unknown helper target $@ - aborting"
- exit 1
- ;;
-
-esac
diff --git a/bin/cdist b/bin/cdist
index 645020a1..ddaffa7f 100755
--- a/bin/cdist
+++ b/bin/cdist
@@ -1,7 +1,8 @@
-#!/bin/sh
+#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
-# 2012 Nico Schottelius (nico-cdist at schottelius.org)
+# 2010-2016 Nico Schottelius (nico-cdist at schottelius.org)
+# 2016 Darko Poljak (darko.poljak at gmail.com)
#
# This file is part of cdist.
#
@@ -20,14 +21,81 @@
#
#
-# Wrapper for real script to allow execution from checkout
-dir=${0%/*}
+import logging
+import os
+import sys
-# Ensure version is present - the bundled/shipped version contains a static version,
-# the git version contains a dynamic version
-"$dir/build-helper" version
+# See if this file's parent is cdist module
+# and if so add it to module search path.
+cdist_dir = os.path.realpath(
+ os.path.join(
+ os.path.dirname(os.path.realpath(__file__)),
+ os.pardir))
+cdist_init_dir = os.path.join(cdist_dir, 'cdist', '__init__.py')
+if os.path.exists(cdist_init_dir):
+ sys.path.insert(0, cdist_dir)
-libdir=$(cd "${dir}/../" && pwd -P)
-export PYTHONPATH="${libdir}"
+import cdist # noqa 402
+import cdist.argparse # noqa 402
+import cdist.banner # noqa 402
+import cdist.config # noqa 402
+import cdist.install # noqa 402
+import cdist.shell # noqa 402
+import cdist.inventory # noqa 402
-"$dir/../scripts/cdist" "$@"
+
+def commandline():
+ """Parse command line"""
+
+ # preos subcommand hack
+ if len(sys.argv) > 1 and sys.argv[1] == 'preos':
+ return cdist.preos.PreOS.commandline(sys.argv[1:])
+ parser, cfg = cdist.argparse.parse_and_configure(sys.argv[1:])
+ args = cfg.get_args()
+
+ # Work around python 3.3 bug:
+ # http://bugs.python.org/issue16308
+ # http://bugs.python.org/issue9253
+
+ # FIXME: catching AttributeError also hides
+ # real problems.. try a different way
+
+ # FIXME: we always print main help, not
+ # the help of the actual parser being used!
+ try:
+ getattr(args, "func")
+ except AttributeError:
+ parser['main'].print_help()
+ sys.exit(0)
+
+ args.func(args)
+
+
+if __name__ == "__main__":
+ if sys.version < cdist.MIN_SUPPORTED_PYTHON_VERSION:
+ print('Python >= {} is required on the source host.'.format(
+ cdist.MIN_SUPPORTED_PYTHON_VERSIO), file=sys.stderr)
+ sys.exit(1)
+
+ exit_code = 0
+
+ try:
+ import re
+ import os
+
+ if re.match("__", os.path.basename(sys.argv[0])):
+ import cdist.emulator
+ emulator = cdist.emulator.Emulator(sys.argv)
+ emulator.run()
+ else:
+ commandline()
+
+ except KeyboardInterrupt:
+ exit_code = 2
+
+ except cdist.Error as e:
+ log = logging.getLogger("cdist")
+ log.error(e)
+ exit_code = 1
+
+ sys.exit(exit_code)
diff --git a/bin/cdist-build-helper b/bin/cdist-build-helper
new file mode 100755
index 00000000..0380b3f8
--- /dev/null
+++ b/bin/cdist-build-helper
@@ -0,0 +1,574 @@
+#!/bin/sh
+#
+# 2011-2013 Nico Schottelius (nico-cdist at schottelius.org)
+# 2016-2019 Darko Poljak (darko.poljak at gmail.com)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+#
+# This file contains the heavy lifting found usually in the Makefile.
+#
+
+usage() {
+ printf "usage: %s TARGET [TARGET-ARGS...]
+ Available targets:
+ changelog-changes
+ changelog-version
+ check-date
+ check-unittest
+ ml-release
+ archlinux-release
+ pypi-release
+ release-git-tag
+ sign-git-release
+ release
+ test
+ test-remote
+ pycodestyle
+ pep8
+ check-pycodestyle
+ shellcheck-global-explorers
+ shellcheck-type-explorers
+ shellcheck-manifests
+ shellcheck-local-gencodes
+ shellcheck-remote-gencodes
+ shellcheck-bin
+ shellcheck-gencodes
+ shellcheck-types
+ shellcheck
+ shellcheck-type-files
+ shellcheck-with-files
+ shellcheck-build-helper
+ check-shellcheck
+ version-branch
+ version
+ target-version
+ clean
+ distclean\n" "$1"
+}
+
+basename="${0##*/}"
+
+if [ $# -lt 1 ]
+then
+ usage "${basename}"
+ exit 1
+fi
+
+option=$1; shift
+
+SHELLCHECKCMD="shellcheck -s sh -f gcc -x"
+# Skip SC2154 for variables starting with __ since such variables are cdist
+# environment variables.
+SHELLCHECK_SKIP=': __.*is referenced but not assigned.*\[SC2154\]'
+SHELLCHECKTMP=".shellcheck.tmp"
+
+# Change to checkout directory
+basedir="${0%/*}/../"
+cd "$basedir"
+
+case "$option" in
+ changelog-changes)
+ if [ "$#" -eq 1 ]; then
+ start=$1
+ else
+ start="[[:digit:]]"
+ fi
+
+ end="[[:digit:]]"
+
+ awk -F: "BEGIN { start=0 }
+ {
+ if(start == 0) {
+ if (\$0 ~ /^$start/) {
+ start = 1
+ }
+ } else {
+ if (\$0 ~ /^$end/) {
+ exit
+ } else {
+ print \$0
+ }
+ }
+ }" "$basedir/docs/changelog"
+ ;;
+
+ changelog-version)
+ # get version from changelog
+ grep '^[[:digit:]]' "$basedir/docs/changelog" | head -n1 | sed 's/:.*//'
+ ;;
+
+ check-date)
+ # verify date in changelog is today
+ date_today="$(date +%Y-%m-%d)"
+ date_changelog=$(grep '^[[:digit:]]' "$basedir/docs/changelog" | head -n1 | sed 's/.*: //')
+
+ if [ "$date_today" != "$date_changelog" ]; then
+ printf "Date in changelog is not today\n"
+ printf "Changelog date: %s\n" "${date_changelog}"
+ exit 1
+ fi
+ ;;
+
+ check-unittest)
+ "$0" test
+ ;;
+
+ ml-release)
+ if [ $# -ne 1 ]; then
+ printf "%s ml-release version\n" "$0" >&2
+ exit 1
+ fi
+
+ version=$1; shift
+
+ (
+ cat << eof
+Subject: cdist $version has been released
+
+Hello .*,
+
+cdist $version has been released with the following changes:
+
+eof
+
+ "$0" changelog-changes "$version"
+ cat << eof
+
+eof
+ ) > mailinglist.tmp
+ ;;
+
+ archlinux-release)
+ if [ $# -ne 1 ]; then
+ printf "%s archlinux-release version\n" "$0" >&2
+ exit 1
+ fi
+ version=$1; shift
+
+ ARCHLINUXTAR="cdist-${version}-1.src.tar.gz"
+ ./PKGBUILD.in "${version}"
+ umask 022
+ mkaurball
+ burp -c system "${ARCHLINUXTAR}"
+ ;;
+
+ pypi-release)
+ # Ensure that pypi release has the right version
+ "$0" version
+
+ make docs-clean
+ make docs
+ python3 setup.py sdist upload
+ ;;
+
+ release-git-tag)
+ target_version=$($0 changelog-version)
+ if git rev-parse --verify "refs/tags/${target_version}" 2>/dev/null; then
+ printf "Tag for %s exists, aborting\n" "${target_version}"
+ exit 1
+ fi
+ printf "Enter tag description for %s: " "${target_version}"
+ read -r tagmessage
+
+ # setup for signed tags:
+ # gpg --fulL-gen-key
+ # gpg --list-secret-keys --keyid-format LONG
+ # git config --local user.signingkey
+ # for exporting pub key:
+ # gpg --armor --export > pubkey.asc
+ # gpg --output pubkey.gpg --export
+ # show tag with signature
+ # git show
+ # verify tag signature
+ # git tag -v
+ #
+ # gpg verify signature
+ # gpg --verify
+ # gpg --no-default-keyring --keyring --verify
+ # Ensure gpg-agent is running.
+ GPG_TTY=$(tty)
+ export GPG_TTY
+ gpg-agent
+
+ git tag -s "$target_version" -m "$tagmessage"
+ git push --tags
+ ;;
+
+ sign-git-release)
+ if [ $# -lt 2 ]
+ then
+ printf "usage: %s sign-git-release TAG TOKEN [ARCHIVE]\n" "$0"
+ printf " if ARCHIVE is not specified then it is created\n"
+ exit 1
+ fi
+ tag="$1"
+ if ! git rev-parse -q --verify "${tag}" >/dev/null 2>&1
+ then
+ printf "Tag \"%s\" not found.\n" "${tag}"
+ exit 1
+ fi
+ token="$2"
+ if [ $# -gt 2 ]
+ then
+ archivename="$3"
+ else
+ archivename="cdist-${tag}.tar"
+ git archive --prefix="cdist-${tag}/" -o "${archivename}" "${tag}" \
+ || exit 1
+ # make sure target version is generated
+ "$0" target-version
+ tar -x -f "${archivename}" || exit 1
+ cp cdist/version.py "cdist-${tag}/cdist/version.py" || exit 1
+ tar -c -f "${archivename}" "cdist-${tag}/" || exit 1
+ rm -r -f "cdist-${tag}/"
+ gzip "${archivename}" || exit 1
+ archivename="${archivename}.gz"
+ fi
+ gpg --armor --detach-sign "${archivename}" || exit 1
+
+ project="ungleich-public%2Fcdist"
+ sed_cmd='s/^.*"markdown":"\([^"]*\)".*$/\1/'
+
+ # upload archive
+ response_archive=$(curl -f -X POST \
+ --http1.1 \
+ -H "PRIVATE-TOKEN: ${token}" \
+ -F "file=@${archivename}" \
+ "https://code.ungleich.ch/api/v4/projects/${project}/uploads" \
+ | sed "${sed_cmd}") || exit 1
+
+ # upload archive signature
+ response_archive_sig=$(curl -f -X POST \
+ --http1.1 \
+ -H "PRIVATE-TOKEN: ${token}" \
+ -F "file=@${archivename}.asc" \
+ "https://code.ungleich.ch/api/v4/projects/${project}/uploads" \
+ | sed "${sed_cmd}") || exit 1
+
+ # make release
+ changelog=$("$0" changelog-changes "$1" | sed 's/^[[:space:]]*//')
+ release_notes=$(
+ printf "%s\n\n%s\n\n**Changelog**\n\n%s\n" \
+ "${response_archive}" "${response_archive_sig}" "${changelog}"
+ )
+ curl -f -X POST \
+ -H "PRIVATE-TOKEN: ${token}" \
+ -F "description=${release_notes}" \
+ "https://code.ungleich.ch/api/v4/projects/${project}/repository/tags/${tag}/release" \
+ || exit 1
+
+ # remove generated files (archive and asc)
+ if [ $# -eq 2 ]
+ then
+ rm -f "${archivename}"
+ fi
+ rm -f "${archivename}.asc"
+ ;;
+
+ release)
+ set -e
+ target_version=$($0 changelog-version)
+ target_branch=$($0 version-branch)
+
+ printf "Beginning release process for %s\n" "${target_version}"
+
+ # First check everything is sane
+ "$0" check-date
+ "$0" check-unittest
+ "$0" check-pycodestyle
+ "$0" check-shellcheck
+
+ # Generate version file to be included in packaging
+ "$0" target-version
+
+ # Ensure the git status is clean, else abort
+ if ! git diff-index --name-only --exit-code HEAD ; then
+ printf "Unclean tree, see files above, aborting.\n"
+ exit 1
+ fi
+
+ # Ensure we are on the master branch
+ masterbranch=yes
+ if [ "$(git rev-parse --abbrev-ref HEAD)" != "master" ]; then
+ printf "Releases are happening from the master branch, aborting.\n"
+
+ printf "Enter the magic word to release anyway:"
+ read -r magicword
+
+ if [ "$magicword" = "iknowwhatido" ]; then
+ masterbranch=no
+ else
+ exit 1
+ fi
+ fi
+
+ if [ "$masterbranch" = yes ]; then
+ # Ensure version branch exists
+ if ! git rev-parse --verify "refs/heads/${target_branch}" 2>/dev/null; then
+ git branch "$target_branch"
+ fi
+
+ # Merge master branch into version branch
+ git checkout "$target_branch"
+ git merge master
+ fi
+
+ # Verify that after the merge everything works
+ "$0" check-date
+ "$0" check-unittest
+
+ # Generate documentation (man and html)
+ # First, clean old generated docs
+ make docs-clean
+ make docs
+
+ #############################################################
+ # Everything green, let's do the release
+
+ # Tag the current commit
+ "$0" release-git-tag
+
+ # Also merge back the version branch
+ if [ "$masterbranch" = yes ]; then
+ git checkout master
+ git merge "$target_branch"
+ fi
+
+ # Publish git changes
+ # if you want to have mirror locally then uncomment this and comment below
+ # git push --mirror
+ git push
+ # push also new branch and set up tracking
+ git push -u origin "${target_branch}"
+ # fi
+
+ # Create and publish package for pypi
+ "$0" pypi-release
+
+ # sign git tag
+ printf "Enter upstream repository authentication token: "
+ read -r token
+ "$0" sign-git-release "${target_version}" "${token}"
+
+ # Announce change on ML
+ "$0" ml-release "${target_version}"
+
+ cat << eof
+Manual steps post release:
+ - cdist-web
+ - send generated mailinglist.tmp mail
+eof
+ ;;
+
+ test)
+ if [ ! -f "cdist/version.py" ]
+ then
+ printf "cdist/version.py is missing, generate it first.\n"
+ exit 1
+ fi
+
+ PYTHONPATH="$(pwd -P)"
+ export PYTHONPATH
+
+ if [ $# -lt 1 ]; then
+ python3 -m cdist.test
+ else
+ python3 -m unittest "$@"
+ fi
+ ;;
+
+ test-remote)
+ if [ ! -f "cdist/version.py" ]
+ then
+ printf "cdist/version.py is missing, generate it first.\n"
+ exit 1
+ fi
+
+ PYTHONPATH="$(pwd -P)"
+ export PYTHONPATH
+
+ python3 -m cdist.test.exec.remote
+ ;;
+
+ pycodestyle|pep8)
+ pycodestyle "${basedir}" "${basedir}/bin/cdist"
+ ;;
+
+ check-pycodestyle)
+ "$0" pycodestyle
+ printf "\\nPlease review pycodestyle report.\\n"
+ while true
+ do
+ printf "Continue (yes/no)?\n"
+ any=
+ read -r any
+ case "$any" in
+ yes)
+ break
+ ;;
+ no)
+ exit 1
+ ;;
+ *)
+ printf "Please answer with 'yes' or 'no' explicitly.\n"
+ ;;
+ esac
+ done
+ ;;
+
+ shellcheck-global-explorers)
+ # shellcheck disable=SC2086
+ find cdist/conf/explorer -type f -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}"
+ test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; }
+ ;;
+
+ shellcheck-type-explorers)
+ # shellcheck disable=SC2086
+ find cdist/conf/type -type f -path "*/explorer/*" -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}"
+ test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; }
+ ;;
+
+ shellcheck-manifests)
+ # shellcheck disable=SC2086
+ find cdist/conf/type -type f -name manifest -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}"
+ test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; }
+ ;;
+
+ shellcheck-local-gencodes)
+ # shellcheck disable=SC2086
+ find cdist/conf/type -type f -name gencode-local -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}"
+ test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; }
+ ;;
+
+ shellcheck-remote-gencodes)
+ # shellcheck disable=SC2086
+ find cdist/conf/type -type f -name gencode-remote -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}"
+ test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; }
+ ;;
+
+ # NOTE: shellcheck-scripts is kept for compatibility
+ shellcheck-bin|shellcheck-scripts)
+ # shellcheck disable=SC2086
+ ${SHELLCHECKCMD} bin/cdist-dump bin/cdist-new-type > "${SHELLCHECKTMP}"
+ test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; }
+ ;;
+
+ shellcheck-gencodes)
+ errors=false
+ "$0" shellcheck-local-gencodes || errors=true
+ "$0" shellcheck-remote-gencodes || errors=true
+ ! $errors || exit 1
+ ;;
+
+ shellcheck-types)
+ errors=false
+ "$0" shellcheck-type-explorers || errors=true
+ "$0" shellcheck-manifests || errors=true
+ "$0" shellcheck-gencodes || errors=true
+ ! $errors || exit 1
+ ;;
+
+ shellcheck)
+ errors=false
+ "$0" shellcheck-global-explorers || errors=true
+ "$0" shellcheck-types || errors=true
+ "$0" shellcheck-bin || errors=true
+ ! $errors || exit 1
+ ;;
+
+ shellcheck-type-files)
+ # shellcheck disable=SC2086
+ find cdist/conf/type -type f -path "*/files/*" -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}"
+ test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; }
+ ;;
+
+ shellcheck-with-files)
+ errors=false
+ "$0" shellcheck || errors=true
+ "$0" shellcheck-type-files || errors=true
+ ! $errors || exit 1
+ ;;
+
+ shellcheck-build-helper)
+ ${SHELLCHECKCMD} ./bin/cdist-build-helper
+ ;;
+
+ check-shellcheck)
+ "$0" shellcheck
+ printf "\\nPlease review shellcheck report.\\n"
+ while true
+ do
+ printf "Continue (yes/no)?\n"
+ any=
+ read -r any
+ case "$any" in
+ yes)
+ break
+ ;;
+ no)
+ exit 1
+ ;;
+ *)
+ printf "Please answer with 'yes' or 'no' explicitly.\n"
+ ;;
+ esac
+ done
+ ;;
+
+ version-branch)
+ "$0" changelog-version | cut -d. -f '1,2'
+ ;;
+
+ version)
+ printf "VERSION = \"%s\"\n" "$(git describe)" > cdist/version.py
+ ;;
+
+ target-version)
+ target_version=$($0 changelog-version)
+ printf "VERSION = \"%s\"\n" "${target_version}" > cdist/version.py
+ ;;
+
+ clean)
+ make clean
+
+ # Archlinux
+ rm -f cdist-*.pkg.tar.xz cdist-*.tar.gz
+ rm -rf pkg/ src/
+
+ rm -f MANIFEST PKGBUILD
+ rm -rf dist/
+
+ # Signed release
+ rm -f cdist-*.tar.gz
+ rm -f cdist-*.tar.gz.asc
+
+ # Temp files
+ rm -f ./*.tmp
+ rm -f ./.*.tmp
+ ;;
+
+ distclean)
+ "$0" clean
+ rm -f cdist/version.py
+ ;;
+ *)
+ printf "Unknown target: '%s'.\n" "${option}" >&2
+ usage "${basename}"
+ exit 1
+ ;;
+
+esac
diff --git a/bin/cdist-dump b/bin/cdist-dump
new file mode 100755
index 00000000..d29e5985
--- /dev/null
+++ b/bin/cdist-dump
@@ -0,0 +1,326 @@
+#!/bin/sh
+
+VERSION="0.0.1"
+RELEASE=""
+
+set -u
+# set -x
+
+hosts=
+cache_dir=~/.cdist/cache
+
+do_all=1
+do_global_explorer=
+do_type_explorer=
+do_script_stdout=
+do_script_stderr=
+do_gencode=
+do_code=
+do_messages=
+do_parameter=
+delimiter=':'
+ln=
+filename_prefix=1
+verbose=0
+
+myname=${0##*/}
+
+print_version()
+{
+ printf "%s %s %s\n" "${myname}" "${VERSION}" "${RELEASE}"
+}
+
+usage()
+{
+ cat << eof
+${myname}: [options] [host...]
+eof
+
+ print_version
+
+ cat << eof
+
+Dump data from cache directories.
+
+host
+ Dump data for specified hosts. If not specified then all data
+ from cache directory is dumped.
+
+Options
+ -a dump all
+ -C CACHE-DIR use specified CACHE-DIR (default: ~/.cdist/cache)
+ -c dump code-*
+ -d DELIMITER delimiter used for filename and line number prefix (default: ':')
+ -E dump global explorers
+ -e dump type explorers
+ -F disable filename prefix (enabled by default)
+ -f enable filename prefix (default)
+ -g dump gencode-*
+ -h show this help screen and exit
+ -L disable line number prefix (default)
+ -l enable line number prefix (disabled by default)
+ -m dump messages
+ -o dump executions' stdout
+ -p dump parameters
+ -r dump executions' stderr
+ -V show version and exit
+ -v increase verbosity
+eof
+}
+
+exit_err()
+{
+ printf "%s\n" "$1"
+ exit 1
+}
+
+# parse options
+while [ "$#" -ge 1 ]
+do
+ case "$1" in
+ -a)
+ do_all=1
+ ;;
+ -C)
+ if [ "$#" -ge 2 ]
+ then
+ case "$2" in
+ -*)
+ exit_err "Missing cache directory"
+ ;;
+ *)
+ cache_dir="$2"
+ shift
+ ;;
+ esac
+ else
+ exit_err "Missing cache directory"
+ fi
+ ;;
+ -c)
+ do_code=1
+ do_all=
+ ;;
+ -d)
+ if [ "$#" -ge 2 ]
+ then
+ case "$2" in
+ -*)
+ exit_err "Missing delimiter"
+ ;;
+ *)
+ delimiter="$2"
+ shift
+ ;;
+ esac
+ else
+ exit_err "Missing delimiter"
+ fi
+ ;;
+ -E)
+ do_global_explorer=1
+ do_all=
+ ;;
+ -e)
+ do_type_explorer=1
+ do_all=
+ ;;
+ -F)
+ filename_prefix=
+ ;;
+ -f)
+ filename_prefix=1
+ ;;
+ -g)
+ do_gencode=1
+ do_all=
+ ;;
+ -h)
+ usage
+ exit 0
+ ;;
+ -L)
+ ln=
+ ;;
+ -l)
+ ln=1
+ ;;
+ -m)
+ do_messages=1
+ do_all=
+ ;;
+ -o)
+ do_script_stdout=1
+ do_all=
+ ;;
+ -p)
+ do_parameter=1
+ do_all=
+ ;;
+ -r)
+ do_script_stderr=1
+ do_all=
+ ;;
+ -V)
+ print_version
+ exit 0
+ ;;
+ -v)
+ verbose=$((verbose + 1))
+ ;;
+ *)
+ hosts="${hosts} $1"
+ break
+ ;;
+ esac
+ shift
+done
+
+if [ "${ln}" = "1" ]
+then
+ ln="NR \"${delimiter}\""
+fi
+
+if [ "${filename_prefix}" = "1" ]
+then
+ filename_prefix="{}${delimiter}"
+fi
+
+if [ "${do_all}" = "1" ]
+then
+ do_global_explorer=1
+ do_type_explorer=1
+ do_script_stdout=1
+ do_script_stderr=1
+ do_gencode=1
+ do_code=1
+ do_messages=1
+ do_parameter=1
+fi
+
+set -- -size +0
+set -- "$@" \(
+or=
+
+print_verbose()
+{
+ if [ "${verbose}" -ge "$1" ]
+ then
+ printf "%s\n" "$2"
+ fi
+}
+
+hor_line()
+{
+ if [ $# -gt 0 ]
+ then
+ c="$1"
+ else
+ c='='
+ fi
+ printf "%78s\n" "" | tr ' ' "${c}"
+}
+
+if [ "${do_global_explorer}" ]
+then
+ print_verbose 2 "Dumping global explorers"
+ # shellcheck disable=SC2086
+ set -- "$@" ${or} \( \
+ -path "*/explorer/*" -a \
+ ! -path "*/conf/*" -a \
+ ! -path "*/object/*/explorer/*" \
+ \)
+ or="-o"
+fi
+
+if [ "${do_type_explorer}" ]
+then
+ print_verbose 2 "Dumping type explorers"
+ set -- "$@" ${or} -path "*/object/*/explorer/*"
+ or="-o"
+fi
+
+if [ "${do_script_stdout}" ]
+then
+ print_verbose 2 "Dumping execution's stdout"
+ set -- "$@" ${or} -path "*/stdout/*"
+ or="-o"
+fi
+
+if [ "${do_script_stderr}" ]
+then
+ print_verbose 2 "Dumping execution's stderr"
+ set -- "$@" ${or} -path "*/stderr/*"
+ or="-o"
+fi
+
+if [ "${do_gencode}" ]
+then
+ print_verbose 2 "Dumping gencode-*"
+ set -- "$@" ${or} \( -name "gencode-*" -a ! -path "*/stdout/*" -a ! -path "*/stderr/*" \)
+ or="-o"
+fi
+
+if [ "${do_code}" ]
+then
+ print_verbose 2 "Dumping code-*"
+ set -- "$@" ${or} \( -name "code-*" -a ! -path "*/stdout/*" -a ! -path "*/stderr/*" \)
+ or="-o"
+fi
+
+if [ "${do_messages}" ]
+then
+ print_verbose 2 "Dumping messages"
+ set -- "$@" ${or} -name "messages"
+ or="-o"
+fi
+
+if [ "${do_parameter}" ]
+then
+ print_verbose 2 "Dumping parameters"
+ set -- "$@" ${or} -path "*/parameter/*"
+ or="-o"
+fi
+
+set -- "$@" \)
+set -- '.' "$@" -exec awk -v prefix="${filename_prefix}" "{print prefix ${ln} \$0}" {} \;
+
+# printf "+ %s\n" "$*"
+
+print_verbose 2 "Using cache dir: ${cache_dir}"
+
+OLD_PWD=$(pwd)
+cd "${cache_dir}" || exit
+
+# If no host is specified then search all.
+[ -z "${hosts}" ] && hosts="-"
+
+for host in ${hosts}
+do
+ [ "${host}" = "-" ] && host=
+ # find host cache directory
+ host_dir=$(find . -name target_host -exec grep -l "${host}" {} +)
+ print_verbose 3 "found host directory files:"
+ print_verbose 3 "${host_dir}"
+
+ OLD_IFS="${IFS}"
+ IFS="
+ "
+
+ for d in ${host_dir}
+ do
+ dir=$(dirname "${d}")
+
+ print_verbose 0 "target host: $(cat "${dir}/target_host"), host directory: ${dir}"
+ hor_line '='
+
+ PREV_PWD=$(pwd)
+ cd "${dir}" || exit
+ # set -x
+ find "$@"
+ # set +x
+ cd "${PREV_PWD}" || exit
+ done
+ IFS="${OLD_IFS}"
+done
+cd "${OLD_PWD}" || exit
diff --git a/bin/cdist-new-type b/bin/cdist-new-type
new file mode 100755
index 00000000..79dcfd90
--- /dev/null
+++ b/bin/cdist-new-type
@@ -0,0 +1,159 @@
+#!/bin/sh
+
+basename="${0##*/}"
+
+if [ $# -lt 3 ]
+then
+ printf "usage: %s TYPE-NAME AUTHOR-NAME AUTHOR-EMAIL [TYPE-BASE-PATH]
+ TYPE-NAME Name of the type.
+ AUTHOR-NAME Type author's full name.
+ AUTHOR-EMAIL Type author's email.
+ TYPE-BASE-PATH Path to the base directory of the type. If not set it defaults
+ to '\$PWD/type'.\n" "${basename}"
+ exit 1
+fi
+
+type_name="$1"
+shift
+author_name="$1"
+shift
+author_email="$1"
+shift
+
+if [ $# -ge 1 ]
+then
+ type_base_path="$1"
+ shift
+else
+ #type_base_path=~/.cdist/type
+ type_base_path="$PWD/type"
+fi
+
+error() {
+ printf "%s\n" "$*" >&2
+}
+
+die() {
+ error "$@"
+ exit 1
+}
+
+cd "$type_base_path" || die "Could not change to type directory: $type_base_path.
+You have to specify type base path or run me from within a cdist conf directory,
+e.g. ~/.cdist."
+
+year=$(date +%Y)
+copyright="# $year $author_name ($author_email)"
+
+license="# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+"
+
+set -e
+
+mkdir "$type_name"
+cd "$type_name"
+
+### man page
+header="cdist-type${type_name}(7)"
+header_length="${#header}"
+cat >> man.rst << DONE
+$header
+$(while [ "${header_length}" -gt 0 ]; do printf "="; header_length=$((header_length - 1)); done; printf "\n";)
+
+NAME
+----
+cdist-type${type_name} - TODO
+
+
+DESCRIPTION
+-----------
+This space intentionally left blank.
+
+
+REQUIRED PARAMETERS
+-------------------
+None.
+
+
+OPTIONAL PARAMETERS
+-------------------
+None.
+
+
+BOOLEAN PARAMETERS
+------------------
+None.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # TODO
+ ${type_name}
+
+
+SEE ALSO
+--------
+:strong:\`TODO\`\\ (7)
+
+
+AUTHORS
+-------
+$author_name <$author_email>
+
+
+COPYING
+-------
+Copyright \(C) $year $author_name. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+DONE
+
+### manifest
+cat >> manifest << DONE
+#!/bin/sh -e
+#
+${copyright}
+#
+${license}
+
+os=\$(cat "\$__global/explorer/os")
+
+case "\$os" in
+ *)
+ printf "Your operating system (%s) is currently not supported by this type (%s)\n" "\$os" "\${__type##*/}" >&2
+ printf "Please contribute an implementation for it if you can.\n" >&2
+ exit 1
+ ;;
+esac
+DONE
+chmod +x manifest
+
+# gencode-remote
+cat >> gencode-remote << DONE
+#!/bin/sh -e
+#
+${copyright}
+#
+${license}
+DONE
+chmod +x gencode-remote
+
+printf "%s/%s\n" "$type_base_path" "$type_name"
diff --git a/cdist/__init__.py b/cdist/__init__.py
index 37651fb5..44366cd0 100644
--- a/cdist/__init__.py
+++ b/cdist/__init__.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
#
# 2010-2015 Nico Schottelius (nico-cdist at schottelius.org)
+# 2012-2017 Steven Armstrong (steven-cdist at armstrong.cc)
#
# This file is part of cdist.
#
@@ -21,11 +22,27 @@
import os
import hashlib
+import subprocess
import cdist.log
-import cdist.version
-VERSION = cdist.version.VERSION
+
+VERSION = 'unknown version'
+
+try:
+ import cdist.version
+ VERSION = cdist.version.VERSION
+except ModuleNotFoundError:
+ cdist_dir = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), os.pardir))
+ if os.path.isdir(os.path.join(cdist_dir, '.git')):
+ try:
+ VERSION = subprocess.check_output(
+ ['git', 'describe', '--always'],
+ cwd=cdist_dir,
+ universal_newlines=True)
+ except Exception:
+ pass
BANNER = """
.. . .x+=:. s
@@ -42,11 +59,14 @@ BANNER = """
"P' "" ""
"""
-REMOTE_COPY = "scp -o User=root"
+REMOTE_COPY = "scp -o User=root -q"
REMOTE_EXEC = "ssh -o User=root"
REMOTE_CMDS_CLEANUP_PATTERN = "ssh -o User=root -O exit -S {}"
+MIN_SUPPORTED_PYTHON_VERSION = '3.5'
+
+
class Error(Exception):
"""Base exception class for this project"""
pass
@@ -80,18 +100,159 @@ class CdistBetaRequired(cdist.Error):
return err_msg.format(*fmt_args)
-class CdistObjectError(Error):
- """Something went wrong with an object"""
+class CdistEntityError(Error):
+ """Something went wrong while executing cdist entity"""
+ def __init__(self, entity_name, entity_params, stdout_paths,
+ stderr_paths, subject=''):
+ self.entity_name = entity_name
+ self.entity_params = entity_params
+ self.stderr_paths = stderr_paths
+ self.stdout_paths = stdout_paths
+ if isinstance(subject, Error):
+ self.original_error = subject
+ else:
+ self.original_error = None
+ self.message = str(subject)
- def __init__(self, cdist_object, message):
- self.name = cdist_object.name
- self.source = " ".join(cdist_object.source)
- self.message = message
+ def _stdpath(self, stdpaths, header_name):
+ result = {}
+ for name, path in stdpaths:
+ if name not in result:
+ result[name] = []
+ try:
+ if os.path.exists(path) and os.path.getsize(path) > 0:
+ output = []
+ label_begin = name + ":" + header_name
+ output.append(label_begin)
+ output.append('\n')
+ output.append('-' * len(label_begin))
+ output.append('\n')
+ with open(path, 'r') as fd:
+ output.append(fd.read())
+ output.append('\n')
+ result[name].append(''.join(output))
+ except UnicodeError as ue:
+ result[name].append(('Cannot output {}:{} due to: {}.\n'
+ 'You can try to read the error file "{}"'
+ ' yourself.').format(
+ name, header_name, ue, path))
+ return result
+
+ def _stderr(self):
+ return self._stdpath(self.stderr_paths, 'stderr')
+
+ def _stdout(self):
+ return self._stdpath(self.stdout_paths, 'stdout')
+
+ def _update_dict_list(self, target, source):
+ for x in source:
+ if x not in target:
+ target[x] = []
+ target[x].extend(source[x])
+
+ @property
+ def std_streams(self):
+ std_dict = {}
+ self._update_dict_list(std_dict, self._stdout())
+ self._update_dict_list(std_dict, self._stderr())
+ return std_dict
def __str__(self):
- return '%s: %s (defined at %s)' % (self.name,
- self.message,
- self.source)
+ output = []
+ output.append(self.message)
+ output.append('\n\n')
+ header = "Error processing " + self.entity_name
+ under_header = '=' * len(header)
+ output.append(header)
+ output.append('\n')
+ output.append(under_header)
+ output.append('\n')
+ for param_name, param_value in self.entity_params:
+ output.append(param_name + ': ' + str(param_value))
+ output.append('\n')
+ output.append('\n')
+ for x in self.std_streams:
+ output.append(''.join(self.std_streams[x]))
+ return ''.join(output)
+
+
+class CdistObjectError(CdistEntityError):
+ """Something went wrong while working on a specific cdist object"""
+ def __init__(self, cdist_object, subject=''):
+ params = [
+ ('name', cdist_object.name, ),
+ ('path', cdist_object.absolute_path, ),
+ ('source', " ".join(cdist_object.source), ),
+ ('type', os.path.realpath(
+ cdist_object.cdist_type.absolute_path), ),
+ ]
+ stderr_paths = []
+ for stderr_name in os.listdir(cdist_object.stderr_path):
+ stderr_path = os.path.join(cdist_object.stderr_path,
+ stderr_name)
+ stderr_paths.append((stderr_name, stderr_path, ))
+ stdout_paths = []
+ for stdout_name in os.listdir(cdist_object.stdout_path):
+ stdout_path = os.path.join(cdist_object.stdout_path,
+ stdout_name)
+ stdout_paths.append((stdout_name, stdout_path, ))
+ super().__init__("object '{}'".format(cdist_object.name),
+ params, stdout_paths, stderr_paths, subject)
+
+
+class CdistObjectExplorerError(CdistEntityError):
+ """
+ Something went wrong while working on a specific
+ cdist object explorer
+ """
+ def __init__(self, cdist_object, explorer_name, explorer_path,
+ stderr_path, subject=''):
+ params = [
+ ('object name', cdist_object.name, ),
+ ('object path', cdist_object.absolute_path, ),
+ ('object source', " ".join(cdist_object.source), ),
+ ('object type', os.path.realpath(
+ cdist_object.cdist_type.absolute_path), ),
+ ('explorer name', explorer_name, ),
+ ('explorer path', explorer_path, ),
+ ]
+ stdout_paths = []
+ stderr_paths = [
+ ('remote', stderr_path, ),
+ ]
+ super().__init__("explorer '{}' of object '{}'".format(
+ explorer_name, cdist_object.name), params, stdout_paths,
+ stderr_paths, subject)
+
+
+class InitialManifestError(CdistEntityError):
+ """Something went wrong while executing initial manifest"""
+ def __init__(self, initial_manifest, stdout_path, stderr_path, subject=''):
+ params = [
+ ('path', initial_manifest, ),
+ ]
+ stdout_paths = [
+ ('init', stdout_path, ),
+ ]
+ stderr_paths = [
+ ('init', stderr_path, ),
+ ]
+ super().__init__('initial manifest', params, stdout_paths,
+ stderr_paths, subject)
+
+
+class GlobalExplorerError(CdistEntityError):
+ """Something went wrong while executing global explorer"""
+ def __init__(self, name, path, stderr_path, subject=''):
+ params = [
+ ('name', name, ),
+ ('path', path, ),
+ ]
+ stderr_paths = [
+ ('remote', stderr_path, ),
+ ]
+ super().__init__("global explorer '{}'".format(name),
+ params, [], stderr_paths, subject)
def file_to_list(filename):
@@ -119,6 +280,11 @@ def str_hash(s):
def home_dir():
if 'HOME' in os.environ:
- return os.path.join(os.environ['HOME'], ".cdist")
+ home = os.environ['HOME']
+ if home:
+ rv = os.path.join(home, ".cdist")
+ else:
+ rv = None
else:
- return None
+ rv = None
+ return rv
diff --git a/cdist/argparse.py b/cdist/argparse.py
index 5bce1b76..88759d7b 100644
--- a/cdist/argparse.py
+++ b/cdist/argparse.py
@@ -5,21 +5,26 @@ import logging
import collections
import functools
import cdist.configuration
+import cdist.log
+import cdist.preos
+import cdist.info
+import cdist.scan.commandline
# set of beta sub-commands
-BETA_COMMANDS = set(('install', 'inventory', ))
+BETA_COMMANDS = set(('install', 'inventory', 'scan', ))
# set of beta arguments for sub-commands
BETA_ARGS = {
- 'config': set(('jobs', 'tag', 'all_tagged_hosts', 'use_archiving', )),
+ 'config': set(('tag', 'all_tagged_hosts', 'use_archiving', )),
}
-EPILOG = "Get cdist at http://www.nico.schottelius.org/software/cdist/"
+EPILOG = "Get cdist at https://code.ungleich.ch/ungleich-public/cdist"
# Parser others can reuse
parser = None
_verbosity_level_off = -2
_verbosity_level = {
+ None: logging.WARNING,
_verbosity_level_off: logging.OFF,
-1: logging.ERROR,
0: logging.WARNING,
@@ -101,7 +106,7 @@ def get_parsers():
name="log level"),
help=('Set the specified verbosity level. '
'The levels, in order from the lowest to the highest, are: '
- 'ERROR (-1), WARNING (0), INFO (1), VERBOSE (2), DEBUG (3) '
+ 'ERROR (-1), WARNING (0), INFO (1), VERBOSE (2), DEBUG (3), '
'TRACE (4 or higher). If used along with -v then -v '
'increases last set value and -l overwrites last set '
'value.'),
@@ -122,16 +127,23 @@ def get_parsers():
'value.'),
action='count', default=None)
+ parser['colored_output'] = argparse.ArgumentParser(add_help=False)
+ parser['colored_output'].add_argument(
+ '--colors', metavar='WHEN',
+ help="Colorize cdist's output based on log level; "
+ "WHEN is 'always', 'never', or 'auto'.",
+ action='store', dest='colored_output', required=False,
+ choices=cdist.configuration.ColoredOutputOption.CHOICES)
+
parser['beta'] = argparse.ArgumentParser(add_help=False)
parser['beta'].add_argument(
'-b', '--beta',
help=('Enable beta functionality. '),
- action='store_true', dest='beta',
- default=False)
+ action='store_true', dest='beta', default=None)
# Main subcommand parser
parser['main'] = argparse.ArgumentParser(
- description='cdist ' + cdist.VERSION, parents=[parser['loglevel']])
+ description='cdist ' + cdist.VERSION)
parser['main'].add_argument(
'-V', '--version', help='Show version.', action='version',
version='%(prog)s ' + cdist.VERSION)
@@ -162,6 +174,16 @@ def get_parsers():
# Config
parser['config_main'] = argparse.ArgumentParser(add_help=False)
+ parser['config_main'].add_argument(
+ '-4', '--force-ipv4',
+ help=('Force to use IPv4 addresses only. No influence for custom'
+ ' remote commands.'),
+ action='store_const', dest='force_ipv', const=4)
+ parser['config_main'].add_argument(
+ '-6', '--force-ipv6',
+ help=('Force to use IPv6 addresses only. No influence for custom'
+ ' remote commands.'),
+ action='store_const', dest='force_ipv', const=6)
parser['config_main'].add_argument(
'-C', '--cache-path-pattern',
help=('Specify custom cache path pattern. If '
@@ -182,16 +204,27 @@ def get_parsers():
name="positive int"),
help=('Operate in parallel in specified maximum number of jobs. '
'Global explorers, object prepare and object run are '
- 'supported. Without argument CPU count is used by default. '
- 'Currently in beta.'),
+ 'supported. Without argument CPU count is used by default. '),
action='store', dest='jobs',
const=multiprocessing.cpu_count())
+ parser['config_main'].add_argument(
+ '--log-server',
+ action='store_true',
+ help=('Start a log server for sub processes to use. '
+ 'This is mainly useful when running cdist nested '
+ 'from a code-local script. Log server is alwasy '
+ 'implicitly started for \'install\' command.'))
parser['config_main'].add_argument(
'-n', '--dry-run',
help='Do not execute code.', action='store_true')
parser['config_main'].add_argument(
'-o', '--out-dir',
help='Directory to save cdist output in.', dest="out_path")
+ parser['config_main'].add_argument(
+ '-P', '--timestamp',
+ help=('Timestamp log messages with the current local date and time '
+ 'in the format: YYYYMMDDHHMMSS.us.'),
+ action='store_true', dest='timestamp')
parser['config_main'].add_argument(
'-R', '--use-archiving', nargs='?',
choices=('tar', 'tgz', 'tbz2', 'txz',),
@@ -221,6 +254,10 @@ def get_parsers():
'(should behave like ssh).'),
action='store', dest='remote_exec',
default=None)
+ parser['config_main'].add_argument(
+ '-S', '--disable-saving-output-streams',
+ help='Disable saving output streams.',
+ action='store_false', dest='save_output_streams', default=True)
# Config
parser['config_args'] = argparse.ArgumentParser(add_help=False)
@@ -233,14 +270,11 @@ def get_parsers():
help=('List hosts that have all specified tags, '
'if -t/--tag is specified.'),
action="store_true", dest="has_all_tags", default=False)
- parser['config_args'].add_argument(
- 'host', nargs='*', help='Host(s) to operate on.')
parser['config_args'].add_argument(
'-f', '--file',
help=('Read specified file for a list of additional hosts to '
'operate on or if \'-\' is given, read stdin (one host per '
- 'line). If no host or host file is specified then, by '
- 'default, read hosts from stdin.'),
+ 'line).'),
dest='hostfile', required=False)
parser['config_args'].add_argument(
'-p', '--parallel', nargs='?', metavar='HOST_MAX',
@@ -261,8 +295,11 @@ def get_parsers():
'list all hosts that contain any of specified tags. '
'Currently in beta.'),
dest='tag', required=False, action="store_true", default=False)
+ parser['config_args'].add_argument(
+ 'host', nargs='*', help='Host(s) to operate on.')
parser['config'] = parser['sub'].add_parser(
'config', parents=[parser['loglevel'], parser['beta'],
+ parser['colored_output'],
parser['common'],
parser['config_main'],
parser['inventory_common'],
@@ -275,15 +312,13 @@ def get_parsers():
parser['install'].set_defaults(func=cdist.install.Install.commandline)
# Inventory
- parser['inventory'] = parser['sub'].add_parser(
- 'inventory', parents=[parser['loglevel'], parser['beta'],
- parser['common'],
- parser['inventory_common']])
+ parser['inventory'] = parser['sub'].add_parser('inventory')
parser['invsub'] = parser['inventory'].add_subparsers(
title="Inventory commands", dest="subcommand")
parser['add-host'] = parser['invsub'].add_parser(
'add-host', parents=[parser['loglevel'], parser['beta'],
+ parser['colored_output'],
parser['common'],
parser['inventory_common']])
parser['add-host'].add_argument(
@@ -291,13 +326,12 @@ def get_parsers():
parser['add-host'].add_argument(
'-f', '--file',
help=('Read additional hosts to add from specified file '
- 'or from stdin if \'-\' (each host on separate line). '
- 'If no host or host file is specified then, by default, '
- 'read from stdin.'),
+ 'or from stdin if \'-\' (each host on separate line). '),
dest='hostfile', required=False)
parser['add-tag'] = parser['invsub'].add_parser(
'add-tag', parents=[parser['loglevel'], parser['beta'],
+ parser['colored_output'],
parser['common'],
parser['inventory_common']])
parser['add-tag'].add_argument(
@@ -306,20 +340,12 @@ def get_parsers():
parser['add-tag'].add_argument(
'-f', '--file',
help=('Read additional hosts to add tags from specified file '
- 'or from stdin if \'-\' (each host on separate line). '
- 'If no host or host file is specified then, by default, '
- 'read from stdin. If no tags/tagfile nor hosts/hostfile'
- ' are specified then tags are read from stdin and are'
- ' added to all hosts.'),
+ 'or from stdin if \'-\' (each host on separate line). '),
dest='hostfile', required=False)
parser['add-tag'].add_argument(
'-T', '--tag-file',
help=('Read additional tags to add from specified file '
- 'or from stdin if \'-\' (each tag on separate line). '
- 'If no tag or tag file is specified then, by default, '
- 'read from stdin. If no tags/tagfile nor hosts/hostfile'
- ' are specified then tags are read from stdin and are'
- ' added to all hosts.'),
+ 'or from stdin if \'-\' (each tag on separate line). '),
dest='tagfile', required=False)
parser['add-tag'].add_argument(
'-t', '--taglist',
@@ -329,6 +355,7 @@ def get_parsers():
parser['del-host'] = parser['invsub'].add_parser(
'del-host', parents=[parser['loglevel'], parser['beta'],
+ parser['colored_output'],
parser['common'],
parser['inventory_common']])
parser['del-host'].add_argument(
@@ -339,13 +366,12 @@ def get_parsers():
parser['del-host'].add_argument(
'-f', '--file',
help=('Read additional hosts to delete from specified file '
- 'or from stdin if \'-\' (each host on separate line). '
- 'If no host or host file is specified then, by default, '
- 'read from stdin.'),
+ 'or from stdin if \'-\' (each host on separate line). '),
dest='hostfile', required=False)
parser['del-tag'] = parser['invsub'].add_parser(
'del-tag', parents=[parser['loglevel'], parser['beta'],
+ parser['colored_output'],
parser['common'],
parser['inventory_common']])
parser['del-tag'].add_argument(
@@ -358,20 +384,13 @@ def get_parsers():
parser['del-tag'].add_argument(
'-f', '--file',
help=('Read additional hosts to delete tags for from specified '
- 'file or from stdin if \'-\' (each host on separate line). '
- 'If no host or host file is specified then, by default, '
- 'read from stdin. If no tags/tagfile nor hosts/hostfile'
- ' are specified then tags are read from stdin and are'
- ' deleted from all hosts.'),
+ 'file or from stdin if \'-\' (each host on separate '
+ 'line). '),
dest='hostfile', required=False)
parser['del-tag'].add_argument(
'-T', '--tag-file',
help=('Read additional tags from specified file '
- 'or from stdin if \'-\' (each tag on separate line). '
- 'If no tag or tag file is specified then, by default, '
- 'read from stdin. If no tags/tagfile nor'
- ' hosts/hostfile are specified then tags are read from'
- ' stdin and are added to all hosts.'),
+ 'or from stdin if \'-\' (each tag on separate line). '),
dest='tagfile', required=False)
parser['del-tag'].add_argument(
'-t', '--taglist',
@@ -381,6 +400,7 @@ def get_parsers():
parser['list'] = parser['invsub'].add_parser(
'list', parents=[parser['loglevel'], parser['beta'],
+ parser['colored_output'],
parser['common'],
parser['inventory_common']])
parser['list'].add_argument(
@@ -408,15 +428,78 @@ def get_parsers():
parser['inventory'].set_defaults(
func=cdist.inventory.Inventory.commandline)
+ # PreOS
+ parser['preos'] = parser['sub'].add_parser('preos', add_help=False)
+
# Shell
parser['shell'] = parser['sub'].add_parser(
- 'shell', parents=[parser['loglevel']])
+ 'shell', parents=[parser['loglevel'], parser['colored_output']])
parser['shell'].add_argument(
'-s', '--shell',
help=('Select shell to use, defaults to current shell. Used shell'
' should be POSIX compatible shell.'))
parser['shell'].set_defaults(func=cdist.shell.Shell.commandline)
+ # Info
+ parser['info'] = parser['sub'].add_parser('info')
+ parser['info'].add_argument(
+ '-a', '--all', help='Display all info. This is the default.',
+ action='store_true', default=False)
+ parser['info'].add_argument(
+ '-c', '--conf-dir',
+ help='Add configuration directory (can be repeated).',
+ action='append')
+ parser['info'].add_argument(
+ '-e', '--global-explorers',
+ help='Display info for global explorers.', action='store_true',
+ default=False)
+ parser['info'].add_argument(
+ '-F', '--fixed-string',
+ help='Interpret pattern as a fixed string.', action='store_true',
+ default=False)
+ parser['info'].add_argument(
+ '-f', '--full', help='Display full details.',
+ action='store_true', default=False)
+ parser['info'].add_argument(
+ '-g', '--config-file',
+ help='Use specified custom configuration file.',
+ dest="config_file", required=False)
+ parser['info'].add_argument(
+ '-t', '--types', help='Display info for types.',
+ action='store_true', default=False)
+ parser['info'].add_argument(
+ 'pattern', nargs='?', help='Glob pattern.')
+ parser['info'].set_defaults(func=cdist.info.Info.commandline)
+
+ # Scan = config + further
+ parser['scan'] = parser['sub'].add_parser('scan', add_help=False,
+ parents=[parser['config']])
+
+ parser['scan'] = parser['sub'].add_parser(
+ 'scan', parents=[parser['loglevel'],
+ parser['beta'],
+ parser['colored_output'],
+ parser['common'],
+ parser['config_main']])
+
+ parser['scan'].add_argument(
+ '-m', '--mode', help='Which modes should run',
+ action='append', default=[],
+ choices=['scan', 'trigger'])
+ parser['scan'].add_argument(
+ '--config',
+ action='store_true',
+ help='Try to configure detected hosts')
+ parser['scan'].add_argument(
+ '-I', '--interfaces',
+ action='append', default=[],
+ help='On which interfaces to scan/trigger')
+ parser['scan'].add_argument(
+ '-d', '--delay',
+ action='store', default=3600,
+ help='How long to wait before reconfiguring after last try')
+ parser['scan'].set_defaults(func=cdist.scan.commandline.commandline)
+
for p in parser:
parser[p].epilog = EPILOG
@@ -424,10 +507,15 @@ def get_parsers():
def handle_loglevel(args):
- if args.quiet:
+ if hasattr(args, 'quiet') and args.quiet:
args.verbose = _verbosity_level_off
- logging.root.setLevel(_verbosity_level[args.verbose])
+ logging.getLogger().setLevel(_verbosity_level[args.verbose])
+
+
+def handle_log_colors(args):
+ if cdist.configuration.ColoredOutputOption.translate(args.colored_output):
+ cdist.log.CdistFormatter.USE_COLORS = True
def parse_and_configure(argv, singleton=True):
@@ -441,6 +529,7 @@ def parse_and_configure(argv, singleton=True):
raise cdist.Error(str(e))
# Loglevels are handled globally in here
handle_loglevel(args)
+ handle_log_colors(args)
log = logging.getLogger("cdist")
diff --git a/cdist/conf/explorer/cpu_cores b/cdist/conf/explorer/cpu_cores
index 7f7a955e..81e5294e 100755
--- a/cdist/conf/explorer/cpu_cores
+++ b/cdist/conf/explorer/cpu_cores
@@ -25,13 +25,22 @@
os=$("$__explorer/os")
case "$os" in
"macosx")
- echo "$(sysctl -n hw.physicalcpu)"
+ sysctl -n hw.physicalcpu
+ ;;
+
+ "openbsd")
+ sysctl -n hw.ncpuonline
+ ;;
+
+ "freebsd"|"netbsd")
+ PATH=$(getconf PATH)
+ sysctl -n hw.ncpu
;;
*)
if [ -r /proc/cpuinfo ]; then
cores="$(grep "core id" /proc/cpuinfo | sort | uniq | wc -l)"
- if [ ${cores} -eq 0 ]; then
+ if [ "${cores}" -eq 0 ]; then
cores="1"
fi
echo "$cores"
diff --git a/cdist/conf/explorer/cpu_sockets b/cdist/conf/explorer/cpu_sockets
index 8a8194df..a32e2f00 100755
--- a/cdist/conf/explorer/cpu_sockets
+++ b/cdist/conf/explorer/cpu_sockets
@@ -25,14 +25,14 @@
os=$("$__explorer/os")
case "$os" in
"macosx")
- echo "$(system_profiler SPHardwareDataType | grep "Number of Processors" | awk -F': ' '{print $2}')"
+ system_profiler SPHardwareDataType | grep "Number of Processors" | awk -F': ' '{print $2}'
;;
*)
if [ -r /proc/cpuinfo ]; then
- sockets="$(grep "physical id" /proc/cpuinfo | sort | uniq | wc -l)"
- if [ ${sockets} -eq 0 ]; then
- sockets="$(cat /proc/cpuinfo | grep "processor" | wc -l)"
+ sockets="$(grep "physical id" /proc/cpuinfo | sort -u | wc -l)"
+ if [ "${sockets}" -eq 0 ]; then
+ sockets="$(grep -c "processor" /proc/cpuinfo)"
fi
echo "${sockets}"
fi
diff --git a/cdist/conf/explorer/disks b/cdist/conf/explorer/disks
old mode 100644
new mode 100755
index 52fef81e..56d62d10
--- a/cdist/conf/explorer/disks
+++ b/cdist/conf/explorer/disks
@@ -1,2 +1,66 @@
-cd /dev
-echo sd? hd? vd?
+#!/bin/sh -e
+#
+# based on previous work by other people, modified by:
+# 2020 Dennis Camera
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+# Finds disks of the system (excl. ram disks, floppy, cdrom)
+
+uname_s="$(uname -s)"
+
+case $uname_s in
+ FreeBSD)
+ sysctl -n kern.disks
+ ;;
+ OpenBSD)
+ sysctl -n hw.disknames | grep -Eo '[lsw]d[0-9]+'
+ ;;
+ NetBSD)
+ PATH=$(getconf PATH)
+ sysctl -n hw.disknames | awk -v RS=' ' '/^[lsw]d[0-9]+/'
+ ;;
+ Linux)
+ # list of major device numbers toexclude:
+ # ram disks, floppies, cdroms
+ # https://www.kernel.org/doc/Documentation/admin-guide/devices.txt
+ ign_majors='1 2 11'
+
+ if command -v lsblk >/dev/null 2>&1
+ then
+ lsblk -e "$(echo "$ign_majors" | tr ' ' ',')" -dno name
+ elif test -d /sys/block/
+ then
+ # shellcheck disable=SC2012
+ ls -1 /sys/block/ \
+ | awk -v ign_majors="$(echo "$ign_majors" | tr ' ' '|')" '
+ {
+ devfile = "/sys/block/" $0 "/dev"
+ getline devno < devfile
+ close(devfile)
+ if (devno !~ "^(" ign_majors "):") print
+ }'
+ else
+ echo "Don't know how to list disks on Linux without lsblk and sysfs." >&2
+ echo 'If you can, please submit a patch.'>&2
+ fi
+ ;;
+ *)
+ printf "Don't know how to list disks for %s operating system.\n" "${uname_s}" >&2
+ printf 'If you can please submit a patch\n' >&2
+ ;;
+esac \
+| xargs
diff --git a/cdist/conf/explorer/hostname b/cdist/conf/explorer/hostname
index 7715c6b0..dca004d1 100755
--- a/cdist/conf/explorer/hostname
+++ b/cdist/conf/explorer/hostname
@@ -1,7 +1,6 @@
#!/bin/sh
#
-# 2010-2014 Nico Schottelius (nico-cdist at schottelius.org)
-# 2012 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -19,7 +18,12 @@
# along with cdist. If not, see .
#
#
+# Retrieve the running hostname
+#
-if command -v uname >/dev/null; then
- uname -n
+if command -v hostname >/dev/null
+then
+ hostname
+else
+ uname -n
fi
diff --git a/cdist/conf/explorer/init b/cdist/conf/explorer/init
index 43a66a50..f27c77ef 100755
--- a/cdist/conf/explorer/init
+++ b/cdist/conf/explorer/init
@@ -1,7 +1,8 @@
-#!/bin/sh
+#!/bin/sh -e
#
# 2016 Daniel Heule (hda at sfs.biz)
# Copyright 2017, Philippe Gregoire
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -19,21 +20,423 @@
# along with cdist. If not, see .
#
#
-# Returns the process name of pid 1 ( normaly the init system )
-# for example at linux this value is "init" or "systemd" in most cases
+# Returns the name of the init system (PID 1)
+
+# Expected values:
+# Linux:
+# Adélie Linux:
+# sysvinit+openrc
+# Alpine Linux:
+# busybox-init+openrc
+# ArchLinux:
+# systemd, sysvinit
+# CRUX:
+# sysvinit
+# Debian:
+# systemd, upstart, sysvinit, openrc, ???
+# Devuan:
+# sysvinit, sysvinit+openrc
+# Gentoo:
+# sysvinit+openrc, openrc-init, systemd
+# OpenBMC:
+# systemd
+# OpenWrt:
+# procd, init???
+# RedHat (RHEL, CentOS, Fedora, RedHat Linux, ...):
+# systemd, upstart, upstart-legacy, sysvinit
+# Slackware:
+# sysvinit
+# SuSE:
+# systemd, sysvinit
+# Ubuntu:
+# systemd, upstart, upstart-legacy, sysvinit
+# VoidLinux:
+# runit
#
+# GNU:
+# Debian:
+# sysvinit, hurd-init
+#
+# BSD:
+# {Free,Open,Net}BSD:
+# init
+#
+# Mac OS X:
+# launchd, init+SystemStarter
+#
+# Solaris/Illumos:
+# smf, init???
-uname_s="$(uname -s)"
+# NOTE: init systems can be stacked. This is popular to run OpenRC on top of
+# sysvinit (Gentoo) or busybox-init (Alpine), but can also be used to run runit
+# as a systemd service. This makes init system detection very complicated
+# (which result is expected?) This script tries to untangle some combinations,
+# OpenRC on top of sysv or busybox (X+openrc), but will ignore others (runit as
+# a systemd service)
-case "$uname_s" in
- Linux)
- (pgrep -P0 -l | awk '/^1[ \t]/ {print $2;}') || true
- ;;
- FreeBSD)
- ps -o comm= -p 1 || true
- ;;
- *)
- # return a empty string as unknown value
- echo ""
- ;;
-esac
+# NOTE: When we have no idea, nothing will be printed!
+
+# NOTE:
+# When trying to gather information about the init system make sure to do so
+# without calling the binary! On some systems this triggers a reinitialisation
+# of the system which we don't want (e.g. embedded systems).
+
+
+set -e
+
+KERNEL_NAME=$(uname -s)
+
+KNOWN_INIT_SYSTEMS=$(cat </dev/null 2>&1 || return 1
+ launchctl getenv PATH >/dev/null || return 1
+ echo launchd
+}
+
+check_openrc() {
+ test -f /run/openrc/softlevel || return 1
+ echo openrc
+}
+
+check_procd() (
+ procd_path=${1:-/sbin/procd}
+ test -x "${procd_path}" || return 1
+ grep -q 'procd' "${procd_path}" || return 1
+ echo procd
+)
+
+check_runit() {
+ test -d /run/runit || return 1
+ echo runit
+}
+
+check_smf() {
+ # XXX: Is this the correct way??
+ test -f /etc/svc/volatile/svc_nonpersist.db || return 1
+ echo smf
+}
+
+check_systemd() {
+ # NOTE: sd_booted(3)
+ test -d /run/systemd/system/ || return 1
+ # systemctl --version | sed -e '/^systemd/!d;s/^systemd //'
+ echo systemd
+}
+
+check_systemstarter() {
+ test -d /System/Library/StartupItems/ || return 1
+ test -f /System/Library/StartupItems/LoginWindow/StartupParameters.plist || return 1
+ echo init+SystemStarter
+}
+
+check_sysvinit() (
+ init_path=${1:-/sbin/init}
+ test -x "${init_path}" || return 1
+ grep -q 'INIT_VERSION=sysvinit-[0-9.]*' "${init_path}" || return 1
+
+ # It is quite common to use SysVinit to stack other init systemd
+ # (like OpenRC) on top of it. So we check for that, too.
+ if stacked=$(check_openrc)
+ then
+ echo "sysvinit+${stacked}"
+ else
+ echo sysvinit
+ fi
+ unset stacked
+)
+
+check_upstart() {
+ test -x "$(command -v initctl)" || return 1
+ case $(initctl version)
+ in
+ *'(upstart '*')')
+ if test -d /etc/init
+ then
+ # modern (DBus-based?) upstart >= 0.5
+ echo upstart
+ elif test -d /etc/event.d
+ then
+ # ancient upstart
+ echo upstart-legacy
+ else
+ # whatever...
+ echo upstart
+ fi
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+}
+
+find_init_procfs() (
+ # First, check if the required file in procfs exists...
+ test -h /proc/1/exe || return 1
+
+ # Find init executable
+ init_exe=$(ls -l /proc/1/exe 2>/dev/null) || return 1
+ init_exe=${init_exe#* -> }
+
+ if ! test -x "$init_exe"
+ then
+ # On some rare occasions it can happen that the
+ # running init's binary has been replaced. In this
+ # case Linux adjusts the symlink to "X (deleted)"
+
+ # [root@fedora-12 ~]# readlink /proc/1/exe
+ # /sbin/init (deleted)
+ # [root@fedora-12 ~]# ls -l /proc/1/exe
+ # lrwxrwxrwx. 1 root root 0 2020-01-30 23:00 /proc/1/exe -> /sbin/init (deleted)
+
+ init_exe=${init_exe% (deleted)}
+ test -x "$init_exe" || return 1
+ fi
+
+ echo "${init_exe}"
+)
+
+guess_by_path() {
+ case $1
+ in
+ /bin/busybox)
+ check_busybox_init "$1" && return
+ ;;
+ /lib/systemd/systemd)
+ check_systemd "$1" && return
+ ;;
+ /hurd/init)
+ check_hurd_init "$1" && return
+ ;;
+ /sbin/launchd)
+ check_launchd "$1" && return
+ ;;
+ /usr/bin/runit|/sbin/runit)
+ check_runit "$1" && return
+ ;;
+ /sbin/openrc-init)
+ if check_openrc "$1" >/dev/null
+ then
+ echo openrc-init
+ return
+ fi
+ ;;
+ /sbin/procd)
+ check_procd "$1" && return
+ ;;
+ /sbin/init|*/init)
+ # init: it could be anything -> (explicit) no match
+ return 1
+ ;;
+ esac
+
+ # No match
+ return 1
+}
+
+guess_by_comm_name() {
+ case $1
+ in
+ busybox)
+ check_busybox_init && return
+ ;;
+ openrc-init)
+ if check_openrc >/dev/null
+ then
+ echo openrc-init
+ return 0
+ fi
+ ;;
+ init)
+ # init could be anything -> no match
+ return 1
+ ;;
+ *)
+ # Run check function by comm name if available.
+ # Fall back to comm name if either it does not exist or
+ # returns non-zero.
+ if type "check_$1" >/dev/null
+ then
+ "check_$1" && return
+ else
+ echo "$1" ; return 0
+ fi
+ esac
+
+ return 1
+}
+
+check_list() (
+ # List must be a multi-line input on stdin (one name per line)
+ while read -r init
+ do
+ "check_${init}" || continue
+ return 0
+ done
+ return 1
+)
+
+
+# BusyBox's versions of ps and pgrep do not support some options
+# depending on which compile-time options have been used.
+
+find_init_pgrep() {
+ pgrep -P0 -fl 2>/dev/null | awk -F '[[:blank:]]' '$1 == 1 { print $2 }'
+}
+
+find_init_ps() {
+ case $KERNEL_NAME
+ in
+ Darwin)
+ ps -o command -p 1 2>/dev/null | tail -n +2
+ ;;
+ FreeBSD)
+ ps -o args= -p 1 2>/dev/null | cut -d ' ' -f 1
+ ;;
+ Linux)
+ ps -o comm= -p 1 2>/dev/null
+ ;;
+ NetBSD)
+ ps -o comm= -p 1 2>/dev/null
+ ;;
+ OpenBSD)
+ ps -o args -p 1 2>/dev/null | tail -n +2 | cut -d ' ' -f 1
+ ;;
+ *)
+ ps -o args= -p 1 2>/dev/null
+ ;;
+ esac | trim # trim trailing whitespace (some ps like Darwin add it)
+}
+
+find_init() {
+ case $KERNEL_NAME
+ in
+ Linux|GNU|NetBSD)
+ find_init_procfs || find_init_pgrep || find_init_ps
+ ;;
+ FreeBSD)
+ find_init_procfs || find_init_ps
+ ;;
+ OpenBSD)
+ find_init_pgrep || find_init_ps
+ ;;
+ Darwin|SunOS)
+ find_init_ps
+ ;;
+ *)
+ echo "Don't know how to determine init." >&2
+ echo 'Please send a patch.' >&2
+ exit 1
+ esac
+}
+
+# -----
+
+init=$(find_init)
+
+# If we got a path, guess by the path first (fall back to file name if no match)
+# else guess by file name directly.
+# shellcheck disable=SC2015
+{
+ test -x "${init}" \
+ && guess_by_path "${init}" \
+ || guess_by_comm_name "$(basename "${init}")"
+} && exit 0 || true
+
+
+# Guessing based on the file path and name didn’t lead to a definitive result.
+#
+# We go through all of the checks until we find a match. To speed up the
+# process, common cases will be checked first based on the underlying kernel.
+
+{ common_candidates_by_kernel; echo "${KNOWN_INIT_SYSTEMS}"; } \
+ | unique | check_list
diff --git a/cdist/conf/explorer/interfaces b/cdist/conf/explorer/interfaces
index c1f2a57a..aeb55ed0 100755
--- a/cdist/conf/explorer/interfaces
+++ b/cdist/conf/explorer/interfaces
@@ -1,6 +1,6 @@
-#!/bin/sh
+#!/bin/sh -e
#
-# 2012 Sébastien Gross
+# 2019 Ander Punnar (ander-at-kvlt-dot-ee)
#
# This file is part of cdist.
#
@@ -17,35 +17,12 @@
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see .
#
-#
-# List all network interfaces in explorer/ifaces. One interface per line.
-#
-# If your OS is not supported please provide a ifconfig output
-#
-# Use ip, if available
-if command -v ip >/dev/null; then
- ip -o link show | sed -n 's/^[0-9]\+: \(.\+\): <.*/\1/p'
- exit 0
-fi
-
-if ! command -v ifconfig >/dev/null; then
- # no ifconfig, nothing we could do
- exit 0
-fi
-
-uname_s="$(uname -s)"
-REGEXP='s/^(.*)(:[[:space:]]*flags=|Link encap).*/\1/p'
-
-case "$uname_s" in
- Darwin)
- ifconfig -a | sed -n -E "$REGEXP"
- ;;
- Linux|*BSD)
- ifconfig -a | sed -n -r "$REGEXP"
- ;;
- *)
- echo "Unsupported ifconfig output for $uname_s" >&2
- exit 1
- ;;
-esac
+if command -v ip >/dev/null
+then
+ ip -o link show | sed -n 's/^[0-9]\+: \(.\+\): <.*/\1/p'
+elif command -v ifconfig >/dev/null
+then
+ ifconfig -a | sed -n -E 's/^(.*)(:[[:space:]]*flags=|Link encap).*/\1/p'
+fi \
+ | sort -u
diff --git a/cdist/conf/explorer/is-freebsd-jail b/cdist/conf/explorer/is-freebsd-jail
new file mode 100755
index 00000000..010917f5
--- /dev/null
+++ b/cdist/conf/explorer/is-freebsd-jail
@@ -0,0 +1,2 @@
+#!/bin/sh
+sysctl -n security.jail.jailed 2>/dev/null | grep "1" || true
diff --git a/cdist/conf/explorer/kernel_name b/cdist/conf/explorer/kernel_name
old mode 100644
new mode 100755
index 98ebac2a..1f9cfca4
--- a/cdist/conf/explorer/kernel_name
+++ b/cdist/conf/explorer/kernel_name
@@ -1 +1,2 @@
+#!/bin/sh
uname -s
diff --git a/cdist/conf/explorer/lsb_codename b/cdist/conf/explorer/lsb_codename
index eebd3e0f..26bb8e3d 100755
--- a/cdist/conf/explorer/lsb_codename
+++ b/cdist/conf/explorer/lsb_codename
@@ -20,8 +20,9 @@
#
set +e
-case "$($__explorer/os)" in
+case "$("$__explorer/os")" in
openwrt)
+ # shellcheck disable=SC1091
(. /etc/openwrt_release && echo "$DISTRIB_CODENAME")
;;
*)
diff --git a/cdist/conf/explorer/lsb_description b/cdist/conf/explorer/lsb_description
index 23f45421..b1009627 100755
--- a/cdist/conf/explorer/lsb_description
+++ b/cdist/conf/explorer/lsb_description
@@ -20,8 +20,9 @@
#
set +e
-case "$($__explorer/os)" in
+case "$("$__explorer/os")" in
openwrt)
+ # shellcheck disable=SC1091
(. /etc/openwrt_release && echo "$DISTRIB_DESCRIPTION")
;;
*)
diff --git a/cdist/conf/explorer/lsb_id b/cdist/conf/explorer/lsb_id
index 9754eb63..82ff9977 100755
--- a/cdist/conf/explorer/lsb_id
+++ b/cdist/conf/explorer/lsb_id
@@ -20,8 +20,9 @@
#
set +e
-case "$($__explorer/os)" in
+case "$("$__explorer/os")" in
openwrt)
+ # shellcheck disable=SC1091
(. /etc/openwrt_release && echo "$DISTRIB_ID")
;;
*)
diff --git a/cdist/conf/explorer/lsb_release b/cdist/conf/explorer/lsb_release
index 35b5547c..5ebfff1a 100755
--- a/cdist/conf/explorer/lsb_release
+++ b/cdist/conf/explorer/lsb_release
@@ -20,8 +20,9 @@
#
set +e
-case "$($__explorer/os)" in
+case "$("$__explorer/os")" in
openwrt)
+ # shellcheck disable=SC1091
(. /etc/openwrt_release && echo "$DISTRIB_RELEASE")
;;
*)
diff --git a/cdist/conf/explorer/machine b/cdist/conf/explorer/machine
index d4a0e106..7ecb67e3 100755
--- a/cdist/conf/explorer/machine
+++ b/cdist/conf/explorer/machine
@@ -22,6 +22,6 @@
#
#
-if command -v uname 2>&1 >/dev/null; then
+if command -v uname >/dev/null 2>&1 ; then
uname -m
fi
diff --git a/cdist/conf/explorer/machine_type b/cdist/conf/explorer/machine_type
index 3b4f0308..1c84f4d7 100755
--- a/cdist/conf/explorer/machine_type
+++ b/cdist/conf/explorer/machine_type
@@ -2,6 +2,7 @@
#
# 2014 Daniel Heule (hda at sfs.biz)
# 2014 Thomas Oettli (otho at sfs.biz)
+# 2020 Evilham (contact at evilham.com)
#
# This file is part of cdist.
#
@@ -18,63 +19,91 @@
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see .
#
-#
-# FIXME: other system types (not linux ...)
+os=$("$__explorer/os")
-if [ -d "/proc/vz" -a ! -d "/proc/bc" ]; then
- echo openvz
- exit
-fi
-
-if [ -e "/proc/1/environ" ] &&
- cat "/proc/1/environ" | tr '\000' '\n' | grep -Eiq '^container='; then
- echo lxc
- exit
-fi
-
-if [ -r /proc/cpuinfo ]; then
- # this should only exist on virtual guest machines,
- # tested on vmware, xen, kvm
- if grep -q "hypervisor" /proc/cpuinfo; then
- # this file is aviable in xen guest systems
- if [ -r /sys/hypervisor/type ]; then
- if grep -q -i "xen" /sys/hypervisor/type; then
- echo virtual_by_xen
- exit
+vendor_string_to_machine_type() {
+ for vendor in vmware bochs kvm qemu virtualbox bhyve; do
+ if echo "${1}" | grep -q -i "${vendor}"; then
+ if [ "${vendor}" = "bochs" ] || [ "${vendor}" = "qemu" ]; then
+ vendor="kvm"
fi
- else
- if [ -r /sys/class/dmi/id/product_name ]; then
- if grep -q -i 'vmware' /sys/class/dmi/id/product_name; then
- echo "virtual_by_vmware"
- exit
- elif grep -q -i 'bochs' /sys/class/dmi/id/product_name; then
- echo "virtual_by_kvm"
- exit
- elif grep -q -i 'virtualbox' /sys/class/dmi/id/product_name; then
- echo "virtual_by_virtualbox"
- exit
- fi
- fi
-
- if [ -r /sys/class/dmi/id/sys_vendor ]; then
- if grep -q -i 'qemu' /sys/class/dmi/id/sys_vendor; then
- echo "virtual_by_kvm"
- exit
- fi
- fi
-
- if [ -r /sys/class/dmi/id/chassis_vendor ]; then
- if grep -q -i 'qemu' /sys/class/dmi/id/chassis_vendor; then
- echo "virtual_by_kvm"
- exit
- fi
- fi
+ echo "virtual_by_${vendor}"
+ exit
fi
- echo "virtual_by_unknown"
- else
- echo "physical"
- fi
-else
- echo "unknown"
-fi
+ done
+}
+
+case "$os" in
+ "freebsd")
+ # FreeBSD does not have /proc/cpuinfo even when procfs is used.
+ # Instead there is a sysctl kern.vm_guest.
+ # Which is 'none' if physical, else the virtualisation.
+ vm_guest="$(sysctl -n kern.vm_guest 2>/dev/null || true)"
+ if [ -n "${vm_guest}" ]; then
+ if [ "${vm_guest}" = "none" ]; then
+ echo "physical"
+ exit
+ fi
+ echo "virtual_by_${vm_guest}"
+ exit
+ fi
+ ;;
+
+ "openbsd")
+ # OpenBSD can also use the sysctl's: hw.vendor or hw.product.
+ # Note we can be reasonably sure about a machine being virtualised
+ # as long as we can identify the virtualisation technology.
+ # But not so much about it being physical...
+ # Patches are welcome / reach out if you have better ideas.
+ for sysctl in hw.vendor hw.product; do
+ # This exits if we can make a reasonable judgement
+ vendor_string_to_machine_type "$(sysctl -n "${sysctl}")"
+ done
+ ;;
+
+ *)
+ # Defaulting to linux for compatibility with previous cdist behaviour
+
+ if [ -d "/proc/vz" ] && [ ! -d "/proc/bc" ]; then
+ echo openvz
+ exit
+ fi
+
+ if [ -e "/proc/1/environ" ] &&
+ tr '\000' '\n' < "/proc/1/environ" | grep -Eiq '^container='; then
+ echo lxc
+ exit
+ fi
+
+ if [ -r /proc/cpuinfo ]; then
+ # this should only exist on virtual guest machines,
+ # tested on vmware, xen, kvm, bhyve
+ if grep -q "hypervisor" /proc/cpuinfo; then
+ # this file is aviable in xen guest systems
+ if [ -r /sys/hypervisor/type ]; then
+ if grep -q -i "xen" /sys/hypervisor/type; then
+ echo virtual_by_xen
+ exit
+ fi
+ else
+ for vendor_file in /sys/class/dmi/id/product_name \
+ /sys/class/dmi/id/sys_vendor \
+ /sys/class/dmi/id/chasis_vendor; do
+ if [ -r ${vendor_file} ]; then
+ # This exits if we can make a reasonable judgement
+ vendor_string_to_machine_type "$(cat "${vendor_file}")"
+ fi
+ done
+ fi
+ echo "virtual_by_unknown"
+ exit
+ else
+ echo "physical"
+ exit
+ fi
+ fi
+ ;;
+esac
+
+echo "unknown"
diff --git a/cdist/conf/explorer/memory b/cdist/conf/explorer/memory
index 4e3efff8..63aba9c6 100755
--- a/cdist/conf/explorer/memory
+++ b/cdist/conf/explorer/memory
@@ -1,8 +1,9 @@
-#!/bin/sh
+#!/bin/sh -e
#
# 2014 Daniel Heule (hda at sfs.biz)
# 2014 Thomas Oettli (otho at sfs.biz)
# Copyright 2017, Philippe Gregoire
+# 2020 Dennis Camera
#
# This file is part of cdist.
#
@@ -19,23 +20,74 @@
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see .
#
-#
+# Returns the amount of memory physically installed in the system, or if that
+# cannot be determined the amount available to the operating system kernel,
+# in kibibytes (kiB).
-# FIXME: other system types (not linux ...)
+str2bytes() {
+ awk -F' ' '
+ $2 == "B" || !$2 { print $1 }
+ $2 == "kB" { print $1 * 1000 }
+ $2 == "MB" { print $1 * 1000 * 1000 }
+ $2 == "GB" { print $1 * 1000 * 1000 * 1000 }
+ $2 == "TB" { print $1 * 1000 * 1000 * 1000 * 1000 }
+ $2 == "kiB" { print $1 * 1024 }
+ $2 == "MiB" { print $1 * 1024 * 1024 }
+ $2 == "GiB" { print $1 * 1024 * 1024 * 1024 }
+ $2 == "TiB" { print $1 * 1024 * 1024 * 1024 * 1024 }'
+}
-os=$("$__explorer/os")
-case "$os" in
- "macosx")
- echo "$(sysctl -n hw.memsize)/1024" | bc
- ;;
+bytes2kib() {
+ set -- "$(cat)"
+ test "$1" -gt 0 && echo $(($1 / 1024))
+}
- "openbsd")
- echo "$(sysctl -n hw.physmem) / 1048576" | bc
- ;;
- *)
- if [ -r /proc/meminfo ]; then
- grep "MemTotal:" /proc/meminfo | awk '{print $2}'
- fi
- ;;
+case $(uname -s)
+in
+ (Darwin)
+ sysctl -n hw.memsize | bytes2kib
+ ;;
+ (FreeBSD)
+ sysctl -n hw.realmem | bytes2kib
+ ;;
+ (NetBSD|OpenBSD)
+ # NOTE: This reports "usable" memory, not physically installed memory.
+ command -p sysctl -n hw.physmem | bytes2kib
+ ;;
+ (SunOS)
+ # Make sure that awk from xpg4 is used for the scripts to work
+ export PATH="/usr/xpg4/bin:${PATH}"
+ prtconf \
+ | awk -F ': ' '
+ $1 == "Memory size" { sub(/Megabytes/, "MiB", $2); print $2 }
+ /^$/ { exit }' \
+ | str2bytes \
+ | bytes2kib
+ ;;
+ (Linux)
+ if test -d /sys/devices/system/memory
+ then
+ # Use memory blocks if the architecture (e.g. x86, PPC64, s390)
+ # supports them (they denote physical memory)
+ num_mem_blocks=$(cat /sys/devices/system/memory/memory[0-9]*/state | grep -cxF online)
+ mem_block_size=$(cat /sys/devices/system/memory/block_size_bytes)
+
+ echo $((num_mem_blocks * 0x$mem_block_size)) | bytes2kib && exit
+ fi
+ if test -r /proc/meminfo
+ then
+ # Fall back to meminfo file on other architectures (e.g. ARM, MIPS,
+ # PowerPC)
+ # NOTE: This is "usable" memory, not physically installed memory.
+ awk -F ': +' '$1 == "MemTotal" { sub(/B$/, "iB", $2); print $2 }' /proc/meminfo \
+ | str2bytes \
+ | bytes2kib
+ fi
+ ;;
+ (*)
+ printf "Your kernel (%s) is currently not supported by the memory explorer\n" "$(uname -s)" >&2
+ printf "Please contribute an implementation for it if you can.\n" >&2
+ exit 1
+ ;;
esac
diff --git a/cdist/conf/explorer/os b/cdist/conf/explorer/os
index d522300c..46d87f3e 100755
--- a/cdist/conf/explorer/os
+++ b/cdist/conf/explorer/os
@@ -143,9 +143,16 @@ case "$uname_s" in
esac
if [ -f /etc/os-release ]; then
+ # after sles15, suse don't provide an /etc/SuSE-release anymore, but there is almost no difference between sles and opensuse leap, so call it suse
+ # shellcheck disable=SC1091
+ if (. /etc/os-release && echo "${ID_LIKE}" | grep -q '\(^\|\ \)suse\($\|\ \)')
+ then
+ echo suse
+ exit 0
+ fi
# already lowercase, according to:
# https://www.freedesktop.org/software/systemd/man/os-release.html
- awk -F= '/^ID=/ {print $2;}' /etc/os-release
+ awk -F= '/^ID=/ { if ($2 ~ /^'"'"'(.*)'"'"'$/ || $2 ~ /^"(.*)"$/) { print substr($2, 2, length($2) - 2) } else { print $2 } }' /etc/os-release
exit 0
fi
diff --git a/cdist/conf/explorer/os_release b/cdist/conf/explorer/os_release
new file mode 100644
index 00000000..6489446b
--- /dev/null
+++ b/cdist/conf/explorer/os_release
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# 2018 Adam Dej (dejko.a at gmail.com)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+#
+
+# See os-release(5) and http://0pointer.de/blog/projects/os-release
+
+if test -f /etc/os-release
+then
+ # Linux and FreeBSD (usually a symlink)
+ cat /etc/os-release
+elif test -f /usr/lib/os-release
+then
+ # systemd
+ cat /usr/lib/os-release
+elif test -f /var/run/os-release
+then
+ # FreeBSD (created by os-release service)
+ cat /var/run/os-release
+fi
+
diff --git a/cdist/conf/explorer/os_version b/cdist/conf/explorer/os_version
index 380782cc..3b02dedd 100755
--- a/cdist/conf/explorer/os_version
+++ b/cdist/conf/explorer/os_version
@@ -22,7 +22,7 @@
#
#
-case "$($__explorer/os)" in
+case "$("$__explorer/os")" in
amazon)
cat /etc/system-release
;;
@@ -31,7 +31,32 @@ case "$($__explorer/os)" in
cat /etc/arch-release
;;
debian)
- cat /etc/debian_version
+ debian_version=$(cat /etc/debian_version)
+ case $debian_version
+ in
+ testing/unstable)
+ # previous to Debian 4.0 testing/unstable was used
+ # cf. https://metadata.ftp-master.debian.org/changelogs/main/b/base-files/base-files_11_changelog
+ echo 3.99
+ ;;
+ */sid)
+ # sid versions don't have a number, so we decode by codename:
+ case $(expr "$debian_version" : '\([a-z]\{1,\}\)/')
+ in
+ bullseye) echo 10.99 ;;
+ buster) echo 9.99 ;;
+ stretch) echo 8.99 ;;
+ jessie) echo 7.99 ;;
+ wheezy) echo 6.99 ;;
+ squeeze) echo 5.99 ;;
+ lenny) echo 4.99 ;;
+ *) exit 1
+ esac
+ ;;
+ *)
+ echo "$debian_version"
+ ;;
+ esac
;;
devuan)
cat /etc/devuan_version
@@ -45,6 +70,11 @@ case "$($__explorer/os)" in
macosx)
sw_vers -productVersion
;;
+ freebsd)
+ # Apparently uname -r is not a reliable way to get the patch level.
+ # See: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=251743
+ freebsd-version
+ ;;
*bsd|solaris)
uname -r
;;
@@ -70,4 +100,7 @@ case "$($__explorer/os)" in
ubuntu)
lsb_release -sr
;;
+ alpine)
+ cat /etc/alpine-release
+ ;;
esac
diff --git a/cdist/conf/type/__acl/explorer/acl_is b/cdist/conf/type/__acl/explorer/acl_is
new file mode 100755
index 00000000..a693c023
--- /dev/null
+++ b/cdist/conf/type/__acl/explorer/acl_is
@@ -0,0 +1,31 @@
+#!/bin/sh -e
+#
+# 2018 Ander Punnar (ander-at-kvlt-dot-ee)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+[ ! -e "/$__object_id" ] && exit 0
+
+if ! command -v getfacl > /dev/null
+then
+ echo 'getfacl not available' >&2
+ exit 1
+fi
+
+getfacl "/$__object_id" 2>/dev/null \
+ | grep -Eo '^(default:)?(user|group|(mask|other):):[^:][[:graph:]]+' \
+ || true
diff --git a/cdist/conf/type/__acl/explorer/file_is b/cdist/conf/type/__acl/explorer/file_is
new file mode 100755
index 00000000..096cffd1
--- /dev/null
+++ b/cdist/conf/type/__acl/explorer/file_is
@@ -0,0 +1,31 @@
+#!/bin/sh -e
+#
+# 2018 Ander Punnar (ander-at-kvlt-dot-ee)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+if [ -e "/$__object_id" ]
+then
+ if [ -d "/$__object_id" ]
+ then echo directory
+ elif [ -f "/$__object_id" ]
+ then echo regular
+ else echo other
+ fi
+else
+ echo missing
+fi
diff --git a/cdist/conf/type/__acl/explorer/getent b/cdist/conf/type/__acl/explorer/getent
new file mode 100755
index 00000000..7e6c2c30
--- /dev/null
+++ b/cdist/conf/type/__acl/explorer/getent
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+
+getent passwd | awk -F: '{print "user:"$1}'
+getent group | awk -F: '{print "group:"$1}'
diff --git a/cdist/conf/type/__acl/gencode-remote b/cdist/conf/type/__acl/gencode-remote
new file mode 100755
index 00000000..32318e91
--- /dev/null
+++ b/cdist/conf/type/__acl/gencode-remote
@@ -0,0 +1,143 @@
+#!/bin/sh -e
+#
+# 2018 Ander Punnar (ander-at-kvlt-dot-ee)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+file_is="$( cat "$__object/explorer/file_is" )"
+
+if [ "$file_is" = 'missing' ] \
+ && [ -z "$__cdist_dry_run" ] \
+ && [ ! -f "$__object/parameter/file" ] \
+ && [ ! -f "$__object/parameter/directory" ]
+then
+ exit 0
+fi
+
+os="$( cat "$__global/explorer/os" )"
+
+acl_path="/$__object_id"
+
+acl_is="$( cat "$__object/explorer/acl_is" )"
+
+if [ -f "$__object/parameter/source" ]
+then
+ acl_source="$( cat "$__object/parameter/source" )"
+
+ if [ "$acl_source" = '-' ]
+ then
+ acl_should="$( cat "$__object/stdin" )"
+ else
+ acl_should="$( grep -Ev '^#|^$' "$acl_source" )"
+ fi
+elif [ -f "$__object/parameter/entry" ]
+then
+ acl_should="$( cat "$__object/parameter/entry" )"
+else
+ echo 'no parameters set' >&2
+ exit 1
+fi
+
+# instead of setfacl's non-helpful message "Option -m: Invalid argument near character X"
+# let's check if target has necessary users and groups, since mistyped or missing
+# users/groups in target is most common reason.
+echo "$acl_should" \
+ | grep -Po '(user|group):[^:]+' \
+ | sort -u \
+ | while read -r l
+ do
+ if ! grep "$l" -Fxq "$__object/explorer/getent"
+ then
+ echo "no $l' in target" | sed "s/:/ '/" >&2
+ exit 1
+ fi
+ done
+
+if [ -f "$__object/parameter/default" ]
+then
+ acl_should="$( echo "$acl_should" \
+ | sed 's/^default://' \
+ | sort -u \
+ | sed 's/\(.*\)/default:\1\n\1/' )"
+fi
+
+if [ "$file_is" = 'regular' ] \
+ && echo "$acl_should" | grep -Eq '^default:'
+then
+ # only directories can have default ACLs,
+ # but instead of error,
+ # let's just remove default entries
+ acl_should="$( echo "$acl_should" | grep -Ev '^default:' )"
+fi
+
+if echo "$acl_should" | awk -F: '{ print $NF }' | grep -Fq 'X'
+then
+ [ "$file_is" = 'directory' ] && rep=x || rep=-
+
+ acl_should="$( echo "$acl_should" | sed "s/\\(.*\\)X/\\1$rep/" )"
+fi
+
+setfacl_exec='setfacl'
+
+if [ -f "$__object/parameter/recursive" ]
+then
+ if echo "$os" | grep -Fq 'freebsd'
+ then
+ echo "$os setfacl do not support recursive operations" >&2
+ else
+ setfacl_exec="$setfacl_exec -R"
+ fi
+fi
+
+if [ -f "$__object/parameter/remove" ]
+then
+ echo "$acl_is" | while read -r acl
+ do
+ # skip wanted ACL entries which already exist
+ # and skip mask and other entries, because we
+ # can't actually remove them, but only change.
+ if echo "$acl_should" | grep -Eq "^$acl" \
+ || echo "$acl" | grep -Eq '^(default:)?(mask|other)'
+ then continue
+ fi
+
+ if echo "$os" | grep -Fq 'freebsd'
+ then
+ remove="$acl"
+ else
+ remove="$( echo "$acl" | sed 's/:...$//' )"
+ fi
+
+ echo "$setfacl_exec -x \"$remove\" \"$acl_path\""
+ echo "removed '$remove'" >> "$__messages_out"
+ done
+fi
+
+for acl in $acl_should
+do
+ if ! echo "$acl_is" | grep -Eq "^$acl"
+ then
+ if echo "$os" | grep -Fq 'freebsd' \
+ && echo "$acl" | grep -Eq '^default:'
+ then
+ echo "setting default ACL in $os is currently not supported" >&2
+ else
+ echo "$setfacl_exec -m \"$acl\" \"$acl_path\""
+ echo "added '$acl'" >> "$__messages_out"
+ fi
+ fi
+done
diff --git a/cdist/conf/type/__acl/man.rst b/cdist/conf/type/__acl/man.rst
new file mode 100644
index 00000000..307be72b
--- /dev/null
+++ b/cdist/conf/type/__acl/man.rst
@@ -0,0 +1,108 @@
+cdist-type__acl(7)
+==================
+
+NAME
+----
+cdist-type__acl - Set ACL entries
+
+
+DESCRIPTION
+-----------
+Fully supported and tested on Linux (ext4 filesystem), partial support for FreeBSD.
+
+See ``setfacl`` and ``acl`` manpages for more details.
+
+One of ``--entry`` or ``--source`` must be used.
+
+
+OPTIONAL MULTIPLE PARAMETERS
+----------------------------
+entry
+ Set ACL entry following ``getfacl`` output syntax.
+ Must be used if ``--source`` is not used.
+
+
+OPTIONAL PARAMETERS
+-------------------
+source
+ Read ACL entries from stdin or file.
+ Ordering of entries is not important.
+ When reading from file, comments and empty lines are ignored.
+ Must be used if ``--entry`` is not used.
+
+file
+ Create/change file with ``__file`` using ``user:group:mode`` pattern.
+
+directory
+ Create/change directory with ``__directory`` using ``user:group:mode`` pattern.
+
+
+BOOLEAN PARAMETERS
+------------------
+default
+ Set all ACL entries as default too.
+ Only directories can have default ACLs.
+ Setting default ACL in FreeBSD is currently not supported.
+
+recursive
+ Make ``setfacl`` recursive (Linux only), but not ``getfacl`` in explorer.
+
+remove
+ Remove undefined ACL entries.
+ ``mask`` and ``other`` entries can't be removed, but only changed.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ __acl /srv/project \
+ --default \
+ --recursive \
+ --remove \
+ --entry user:alice:rwx \
+ --entry user:bob:r-x \
+ --entry group:project-group:rwx \
+ --entry group:some-other-group:r-x \
+ --entry mask::r-x \
+ --entry other::r-x
+
+ # give Alice read-only access to subdir,
+ # but don't allow her to see parent content.
+
+ __acl /srv/project2 \
+ --remove \
+ --entry default:group:secret-project:rwx \
+ --entry group:secret-project:rwx \
+ --entry user:alice:--x
+
+ __acl /srv/project2/subdir \
+ --default \
+ --remove \
+ --entry group:secret-project:rwx \
+ --entry user:alice:r-x
+
+ # read acl from stdin
+ echo 'user:alice:rwx' \
+ | __acl /path/to/directory --source -
+
+ # create/change directory too
+ __acl /path/to/directory \
+ --default \
+ --remove \
+ --directory root:root:770 \
+ --entry user:nobody:rwx
+
+
+AUTHORS
+-------
+Ander Punnar
+
+
+COPYING
+-------
+Copyright \(C) 2018 Ander Punnar. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__acl/manifest b/cdist/conf/type/__acl/manifest
new file mode 100755
index 00000000..5fd23110
--- /dev/null
+++ b/cdist/conf/type/__acl/manifest
@@ -0,0 +1,11 @@
+#!/bin/sh -e
+
+for p in file directory
+do
+ [ ! -f "$__object/parameter/$p" ] && continue
+
+ "__$p" "/$__object_id" \
+ --owner "$( awk -F: '{print $1}' "$__object/parameter/$p" )" \
+ --group "$( awk -F: '{print $2}' "$__object/parameter/$p" )" \
+ --mode "$( awk -F: '{print $3}' "$__object/parameter/$p" )"
+done
diff --git a/cdist/conf/type/__acl/parameter/boolean b/cdist/conf/type/__acl/parameter/boolean
new file mode 100644
index 00000000..8b96693f
--- /dev/null
+++ b/cdist/conf/type/__acl/parameter/boolean
@@ -0,0 +1,3 @@
+recursive
+default
+remove
diff --git a/cdist/conf/type/__acl/parameter/optional b/cdist/conf/type/__acl/parameter/optional
new file mode 100644
index 00000000..5a0c29a3
--- /dev/null
+++ b/cdist/conf/type/__acl/parameter/optional
@@ -0,0 +1,3 @@
+source
+file
+directory
diff --git a/cdist/conf/type/__acl/parameter/optional_multiple b/cdist/conf/type/__acl/parameter/optional_multiple
new file mode 100644
index 00000000..4c884f03
--- /dev/null
+++ b/cdist/conf/type/__acl/parameter/optional_multiple
@@ -0,0 +1 @@
+entry
diff --git a/cdist/conf/type/__apt_backports/man.rst b/cdist/conf/type/__apt_backports/man.rst
new file mode 100644
index 00000000..7036fb84
--- /dev/null
+++ b/cdist/conf/type/__apt_backports/man.rst
@@ -0,0 +1,104 @@
+cdist-type__debian_backports(7)
+===============================
+
+NAME
+----
+cdist-type__apt_backports - Install backports
+
+
+DESCRIPTION
+-----------
+This singleton type installs backports for the current OS release.
+It aborts if backports are not supported for the specified OS or
+no version codename could be fetched (like Debian unstable).
+
+The package index will be automatically updated if required.
+
+It supports backports from following OSes:
+
+- Debian
+- Devuan
+- Ubuntu
+
+
+REQUIRED PARAMETERS
+-------------------
+None.
+
+
+OPTIONAL PARAMETERS
+-------------------
+state
+ Represents the state of the backports repository. ``present`` or
+ ``absent``, defaults to ``present``.
+
+ Will be directly passed to :strong:`cdist-type__apt_source`\ (7).
+
+mirror
+ The mirror to fetch the backports from. Will defaults to the generic
+ mirror of the current OS.
+
+ Will be directly passed to :strong:`cdist-type__apt_source`\ (7).
+
+
+BOOLEAN PARAMETERS
+------------------
+None.
+
+
+MESSAGES
+--------
+None.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # setup the backports
+ __apt_backports
+ __apt_backports --state absent
+ __apt_backports --state present --mirror "http://ftp.de.debian.org/debian/"
+
+ # install a backports package
+ # currently for the buster release backports
+ require="__apt_backports" __package_apt wireguard \
+ --target-release buster-backports
+
+
+ABORTS
+------
+Aborts if the detected os is not Debian.
+
+Aborts if no distribuition codename could be detected. This is common for the
+unstable distribution, but there is no backports repository for it already.
+
+
+CAVEATS
+-------
+For Ubuntu, it setup all componenents for the backports repository: ``main``,
+``restricted``, ``universe`` and ``multiverse``. The user may not want to
+install proprietary packages, which will only be installed if the user
+explicitly uses the backports target-release. The user may change this behavior
+to install backports packages without the need of explicitly select it.
+
+
+SEE ALSO
+--------
+`Official Debian Backports site `_
+
+:strong:`cdist-type__apt_source`\ (7)
+
+
+AUTHORS
+-------
+Matthias Stecher
+
+
+COPYING
+-------
+Copyright \(C) 2020 Matthias Stecher. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__apt_backports/manifest b/cdist/conf/type/__apt_backports/manifest
new file mode 100755
index 00000000..bc47d8de
--- /dev/null
+++ b/cdist/conf/type/__apt_backports/manifest
@@ -0,0 +1,81 @@
+#!/bin/sh -e
+# __apt_backports/manifest
+#
+# 2020 Matthias Stecher (matthiasstecher at gmx.de)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+#
+# Enables/disables backports repository. Utilises __apt_source for it.
+#
+
+
+# Get the distribution codename by /etc/os-release.
+# is already executed in a subshell by string substitution
+# lsb_release may not be given in all installations
+codename_os_release() {
+ # shellcheck disable=SC1090
+ . "$__global/explorer/os_release"
+ printf "%s" "$VERSION_CODENAME"
+}
+
+# detect backport distribution
+os="$(cat "$__global/explorer/os")"
+case "$os" in
+ debian)
+ dist="$( codename_os_release )"
+ components="main"
+ mirror="http://deb.debian.org/debian/"
+ ;;
+ devuan)
+ dist="$( codename_os_release )"
+ components="main"
+ mirror="http://deb.devuan.org/merged"
+ ;;
+ ubuntu)
+ dist="$( codename_os_release )"
+ components="main restricted universe multiverse"
+ mirror="http://archive.ubuntu.com/ubuntu"
+ ;;
+
+ *)
+ printf "Backports for %s are not supported!\n" "$os" >&2
+ exit 1
+ ;;
+esac
+
+# error if no codename given (e.g. on Debian unstable)
+if [ -z "$dist" ]; then
+ printf "No backports for unkown version of distribution %s!\n" "$os" >&2
+ exit 1
+fi
+
+
+# parameters
+state="$(cat "$__object/parameter/state")"
+
+# mirror already set for the os, only override user-values
+if [ -f "$__object/parameter/mirror" ]; then
+ mirror="$(cat "$__object/parameter/mirror")"
+fi
+
+
+# install the given backports repository
+__apt_source "${dist}-backports" \
+ --state "$state" \
+ --distribution "${dist}-backports" \
+ --component "$components" \
+ --uri "$mirror"
diff --git a/cdist/conf/type/__apt_backports/parameter/default/state b/cdist/conf/type/__apt_backports/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__apt_backports/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__apt_backports/parameter/optional b/cdist/conf/type/__apt_backports/parameter/optional
new file mode 100644
index 00000000..4b05c235
--- /dev/null
+++ b/cdist/conf/type/__apt_backports/parameter/optional
@@ -0,0 +1,2 @@
+state
+mirror
diff --git a/cdist/conf/type/__pf_apply/singleton b/cdist/conf/type/__apt_backports/singleton
similarity index 100%
rename from cdist/conf/type/__pf_apply/singleton
rename to cdist/conf/type/__apt_backports/singleton
diff --git a/cdist/conf/type/__apt_default_release/man.rst b/cdist/conf/type/__apt_default_release/man.rst
new file mode 100644
index 00000000..0277a06f
--- /dev/null
+++ b/cdist/conf/type/__apt_default_release/man.rst
@@ -0,0 +1,46 @@
+cdist-type__apt_default_release(7)
+==================================
+
+NAME
+----
+cdist-type__apt_default_release - Configure the default release for apt
+
+
+DESCRIPTION
+-----------
+Configure the default release for apt, using the APT::Default-Release
+configuration value.
+
+REQUIRED PARAMETERS
+-------------------
+release
+ The value to set APT::Default-Release to.
+
+ This can contain release name, codename or release version. Examples:
+ 'stable', 'testing', 'unstable', 'stretch', 'buster', '4.0', '5.0*'.
+
+
+OPTIONAL PARAMETERS
+-------------------
+None.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ __apt_default_release --release stretch
+
+
+AUTHORS
+-------
+Matthijs Kooijman
+
+
+COPYING
+-------
+Copyright \(C) 2017 Matthijs Kooijman. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__apt_default_release/manifest b/cdist/conf/type/__apt_default_release/manifest
new file mode 100755
index 00000000..1232efb5
--- /dev/null
+++ b/cdist/conf/type/__apt_default_release/manifest
@@ -0,0 +1,41 @@
+#!/bin/sh -e
+#
+# 2014 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2017 Matthijs Kooijman (matthijs at stdin.nl)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+
+os=$(cat "$__global/explorer/os")
+release="$(cat "$__object/parameter/release")"
+
+case "$os" in
+ ubuntu|debian|devuan)
+ __file /etc/apt/apt.conf.d/99-default-release \
+ --owner root --group root --mode 644 \
+ --source - << DONE
+APT::Default-Release "$release";
+DONE
+ ;;
+ *)
+ cat >&2 << DONE
+The developer of this type (${__type##*/}) did not think your operating system
+($os) would have any use for it. If you think otherwise please submit a patch.
+DONE
+ exit 1
+ ;;
+esac
diff --git a/cdist/conf/type/__apt_default_release/parameter/required b/cdist/conf/type/__apt_default_release/parameter/required
new file mode 100644
index 00000000..d7025695
--- /dev/null
+++ b/cdist/conf/type/__apt_default_release/parameter/required
@@ -0,0 +1 @@
+release
diff --git a/cdist/conf/type/__apt_default_release/singleton b/cdist/conf/type/__apt_default_release/singleton
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__apt_key/explorer/state b/cdist/conf/type/__apt_key/explorer/state
index f7940741..38f1bd3c 100755
--- a/cdist/conf/type/__apt_key/explorer/state
+++ b/cdist/conf/type/__apt_key/explorer/state
@@ -27,6 +27,18 @@ else
keyid="$__object_id"
fi
-apt-key export "$keyid" | head -n 1 | grep -Fqe "BEGIN PGP PUBLIC KEY BLOCK" \
- && echo present \
- || echo absent
+keydir="$(cat "$__object/parameter/keydir")"
+keyfile="$keydir/$__object_id.gpg"
+
+if [ -d "$keydir" ]
+then
+ if [ -f "$keyfile" ]
+ then echo present
+ else echo absent
+ fi
+else
+ # fallback to deprecated apt-key
+ apt-key export "$keyid" | head -n 1 | grep -Fqe "BEGIN PGP PUBLIC KEY BLOCK" \
+ && echo present \
+ || echo absent
+fi
diff --git a/cdist/conf/type/__apt_key/gencode-remote b/cdist/conf/type/__apt_key/gencode-remote
index 9c4fa00c..0c96ff67 100755
--- a/cdist/conf/type/__apt_key/gencode-remote
+++ b/cdist/conf/type/__apt_key/gencode-remote
@@ -31,12 +31,82 @@ if [ "$state_should" = "$state_is" ]; then
exit 0
fi
+keydir="$(cat "$__object/parameter/keydir")"
+keyfile="$keydir/$__object_id.gpg"
+
case "$state_should" in
present)
keyserver="$(cat "$__object/parameter/keyserver")"
- echo "apt-key adv --keyserver \"$keyserver\" --recv-keys \"$keyid\""
+
+ if [ -f "$__object/parameter/uri" ]; then
+ uri="$(cat "$__object/parameter/uri")"
+
+ if [ -d "$keydir" ]; then
+ cat << EOF
+
+curl -s -L \\
+ -o "$keyfile" \\
+ "$uri"
+
+key="\$( cat "$keyfile" )"
+
+if echo "\$key" | grep -Fq 'BEGIN PGP PUBLIC KEY BLOCK'
+then
+ echo "\$key" | gpg --dearmor > "$keyfile"
+fi
+
+EOF
+ else
+ # fallback to deprecated apt-key
+ echo "curl -s -L '$uri' | apt-key add -"
+ fi
+ elif [ -d "$keydir" ]; then
+ # we need to kill gpg after 30 seconds, because gpg
+ # can get stuck if keyserver is not responding.
+ # exporting env var and not exit 1,
+ # because we need to clean up and kill dirmngr.
+ cat << EOF
+
+gpgtmphome="\$( mktemp -d )"
+
+if timeout 30s \\
+ gpg --homedir "\$gpgtmphome" \\
+ --keyserver "$keyserver" \\
+ --recv-keys "$keyid"
+then
+ gpg --homedir "\$gpgtmphome" \\
+ --export "$keyid" \\
+ > "$keyfile"
+else
+ export GPG_GOT_STUCK=1
+fi
+
+GNUPGHOME="\$gpgtmphome" gpgconf --kill dirmngr
+
+rm -rf "\$gpgtmphome"
+
+if [ -n "\$GPG_GOT_STUCK" ]
+then
+ echo "GPG GOT STUCK - no response from keyserver after 30 seconds" >&2
+ exit 1
+fi
+
+EOF
+ else
+ # fallback to deprecated apt-key
+ echo "apt-key adv --keyserver \"$keyserver\" --recv-keys \"$keyid\""
+ fi
+
+ echo "added '$keyid'" >> "$__messages_out"
;;
absent)
- echo "apt-key del \"$keyid\""
+ if [ -f "$keyfile" ]; then
+ echo "rm '$keyfile'"
+ else
+ # fallback to deprecated apt-key
+ echo "apt-key del \"$keyid\""
+ fi
+
+ echo "removed '$keyid'" >> "$__messages_out"
;;
esac
diff --git a/cdist/conf/type/__apt_key/man.rst b/cdist/conf/type/__apt_key/man.rst
index 9009877e..234bc715 100644
--- a/cdist/conf/type/__apt_key/man.rst
+++ b/cdist/conf/type/__apt_key/man.rst
@@ -28,6 +28,12 @@ keyserver
the keyserver from which to fetch the key. If omitted the default set
in ./parameter/default/keyserver is used.
+keydir
+ key save location, defaults to ``/etc/apt/trusted.pgp.d``
+
+uri
+ the URI from which to download the key
+
EXAMPLES
--------
@@ -47,15 +53,20 @@ EXAMPLES
# same thing with other keyserver
__apt_key UbuntuArchiveKey --keyid 437D05B5 --keyserver keyserver.ubuntu.com
+ # download key from the internet
+ __apt_key rabbitmq \
+ --uri http://www.rabbitmq.com/rabbitmq-signing-key-public.asc
+
AUTHORS
-------
Steven Armstrong
+Ander Punnar
COPYING
-------
-Copyright \(C) 2011-2014 Steven Armstrong. You can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation, either version 3 of the
+Copyright \(C) 2011-2019 Steven Armstrong and Ander Punnar. You can
+redistribute it and/or modify it under the terms of the GNU General Public
+License as published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
diff --git a/cdist/conf/type/__apt_key/manifest b/cdist/conf/type/__apt_key/manifest
new file mode 100755
index 00000000..010357cd
--- /dev/null
+++ b/cdist/conf/type/__apt_key/manifest
@@ -0,0 +1,8 @@
+#!/bin/sh -e
+
+__package gnupg
+
+if [ -f "$__object/parameter/uri" ]
+then __package curl
+else __package dirmngr
+fi
diff --git a/cdist/conf/type/__apt_key/parameter/default/keydir b/cdist/conf/type/__apt_key/parameter/default/keydir
new file mode 100644
index 00000000..190eb2de
--- /dev/null
+++ b/cdist/conf/type/__apt_key/parameter/default/keydir
@@ -0,0 +1 @@
+/etc/apt/trusted.gpg.d
diff --git a/cdist/conf/type/__apt_key/parameter/optional b/cdist/conf/type/__apt_key/parameter/optional
index 18cf2586..de647375 100644
--- a/cdist/conf/type/__apt_key/parameter/optional
+++ b/cdist/conf/type/__apt_key/parameter/optional
@@ -1,3 +1,5 @@
state
keyid
keyserver
+keydir
+uri
diff --git a/cdist/conf/type/__apt_norecommends/man.rst b/cdist/conf/type/__apt_norecommends/man.rst
index 001fffe4..9297b518 100644
--- a/cdist/conf/type/__apt_norecommends/man.rst
+++ b/cdist/conf/type/__apt_norecommends/man.rst
@@ -32,11 +32,12 @@ EXAMPLES
AUTHORS
-------
Steven Armstrong
+Dennis Camera
COPYING
-------
-Copyright \(C) 2014 Steven Armstrong. You can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
+Copyright \(C) 2014 Steven Armstrong, 2020 Dennis Camera.
+You can redistribute it and/or modify it under the terms of the GNU General
+Public License as published by the Free Software Foundation, either version 3 of
+the License, or (at your option) any later version.
diff --git a/cdist/conf/type/__apt_norecommends/manifest b/cdist/conf/type/__apt_norecommends/manifest
index e737df89..fc187784 100755
--- a/cdist/conf/type/__apt_norecommends/manifest
+++ b/cdist/conf/type/__apt_norecommends/manifest
@@ -1,6 +1,7 @@
#!/bin/sh -e
#
# 2014 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -19,26 +20,28 @@
#
-os=$(cat "$__global/explorer/os")
+os=$(cat "${__global:?}/explorer/os")
-case "$os" in
- ubuntu|debian|devuan)
- # No stinking recommends thank you very much.
- # If I want something installed I will do so myself.
- __file /etc/apt/apt.conf.d/99-no-recommends \
- --owner root --group root --mode 644 \
- --source - << DONE
-APT::Install-Recommends "0";
-APT::Install-Suggests "0";
-APT::AutoRemove::RecommendsImportant "0";
-APT::AutoRemove::SuggestsImportant "0";
-DONE
- ;;
- *)
- cat >&2 << DONE
+case ${os}
+in
+ (ubuntu|debian|devuan)
+ __file /etc/apt/apt.conf.d/00InstallRecommends --state present \
+ --owner root --group root --mode 0644 --source - <<-'EOF'
+ APT::Install-Recommends "false";
+ APT::Install-Suggests "false";
+ APT::AutoRemove::RecommendsImportant "false";
+ APT::AutoRemove::SuggestsImportant "false";
+ EOF
+
+ # TODO: Remove the following object after some time
+ require=__file/etc/apt/apt.conf.d/00InstallRecommends \
+ __file /etc/apt/apt.conf.d/99-no-recommends --state absent
+ ;;
+ (*)
+ cat >&2 <.
#
-name="$__object_id"
-
__package software-properties-common
require="__package/software-properties-common" \
diff --git a/cdist/conf/type/__apt_source/gencode-remote b/cdist/conf/type/__apt_source/gencode-remote
new file mode 100755
index 00000000..1e8592c6
--- /dev/null
+++ b/cdist/conf/type/__apt_source/gencode-remote
@@ -0,0 +1,28 @@
+#!/bin/sh -e
+#
+# 2018 Steven Armstrong (steven-cdist at armstrong.cc)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+#
+
+name="$__object_id"
+destination="/etc/apt/sources.list.d/${name}.list"
+
+if grep -q "^__file${destination}" "$__messages_in"; then
+ printf 'apt-get update || apt-get update\n'
+fi
+
diff --git a/cdist/conf/type/__apt_source/man.rst b/cdist/conf/type/__apt_source/man.rst
index 8aa6c144..d1acb388 100644
--- a/cdist/conf/type/__apt_source/man.rst
+++ b/cdist/conf/type/__apt_source/man.rst
@@ -8,7 +8,8 @@ cdist-type__apt_source - Manage apt sources
DESCRIPTION
-----------
-This cdist type allows you to manage apt sources.
+This cdist type allows you to manage apt sources. It invokes index update
+internally when needed so call of index updating type is not needed.
REQUIRED PARAMETERS
@@ -63,7 +64,7 @@ Steven Armstrong
COPYING
-------
-Copyright \(C) 2011-2014 Steven Armstrong. You can redistribute it
+Copyright \(C) 2011-2018 Steven Armstrong. You can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
diff --git a/cdist/conf/type/__apt_source/manifest b/cdist/conf/type/__apt_source/manifest
index 7957bf8c..35f15909 100755
--- a/cdist/conf/type/__apt_source/manifest
+++ b/cdist/conf/type/__apt_source/manifest
@@ -1,6 +1,6 @@
#!/bin/sh -e
#
-# 2011-2013 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2011-2018 Steven Armstrong (steven-cdist at armstrong.cc)
#
# This file is part of cdist.
#
@@ -50,5 +50,3 @@ __file "/etc/apt/sources.list.d/${name}.list" \
--source "$__object/files/source.list" \
--owner root --group root --mode 0644 \
--state "$state"
-
-require="$__object_name" __apt_update_index
diff --git a/cdist/conf/type/__apt_source/nonparallel b/cdist/conf/type/__apt_source/nonparallel
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__apt_unattended_upgrades/man.rst b/cdist/conf/type/__apt_unattended_upgrades/man.rst
new file mode 100644
index 00000000..2231b5f9
--- /dev/null
+++ b/cdist/conf/type/__apt_unattended_upgrades/man.rst
@@ -0,0 +1,68 @@
+cdist-type__apt_unattended_upgrades(7)
+======================================
+
+NAME
+----
+cdist-type__apt_unattended_upgrades - automatic installation of updates
+
+
+DESCRIPTION
+-----------
+
+Install and configure unattended-upgrades package.
+
+For more information see https://wiki.debian.org/UnattendedUpgrades.
+
+
+OPTIONAL MULTIPLE PARAMETERS
+----------------------------
+option
+ Set options for unattended-upgrades. See examples.
+
+ Supported options with default values (as of 2020-01-17) are:
+
+ - AutoFixInterruptedDpkg, default is "true"
+ - MinimalSteps, default is "true"
+ - InstallOnShutdown, default is "false"
+ - Mail, default is "" (empty)
+ - MailOnlyOnError, default is "false"
+ - Remove-Unused-Kernel-Packages, default is "true"
+ - Remove-New-Unused-Dependencies, default is "true"
+ - Remove-Unused-Dependencies, default is "false"
+ - Automatic-Reboot, default is "false"
+ - Automatic-Reboot-WithUsers, default is "true"
+ - Automatic-Reboot-Time, default is "02:00"
+ - SyslogEnable, default is "false"
+ - SyslogFacility, default is "daemon"
+ - OnlyOnACPower, default is "true"
+ - Skip-Updates-On-Metered-Connections, default is "true"
+ - Verbose, default is "false"
+ - Debug, default is "false"
+
+blacklist
+ Python regular expressions, matching packages to exclude from upgrading.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ __apt_unattended_upgrades \
+ --option Mail=root \
+ --option MailOnlyOnError=true \
+ --blacklist multipath-tools \
+ --blacklist open-iscsi
+
+
+AUTHORS
+-------
+Ander Punnar
+
+
+COPYING
+-------
+Copyright \(C) 2020 Ander Punnar. You can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or (at your option) any
+later version.
diff --git a/cdist/conf/type/__apt_unattended_upgrades/manifest b/cdist/conf/type/__apt_unattended_upgrades/manifest
new file mode 100755
index 00000000..3c00e2f4
--- /dev/null
+++ b/cdist/conf/type/__apt_unattended_upgrades/manifest
@@ -0,0 +1,80 @@
+#!/bin/sh -e
+#
+# 2020 Ander Punnar (ander-at-kvlt-dot-ee)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+__package unattended-upgrades
+
+export require='__package/unattended-upgrades'
+
+# in normal circumstances 20auto-upgrades is managed
+# by debconf and it can only contain these lines
+
+__file /etc/apt/apt.conf.d/20auto-upgrades \
+ --owner root \
+ --group root \
+ --mode 644 \
+ --source - << EOF
+APT::Periodic::Update-Package-Lists "1";
+APT::Periodic::Unattended-Upgrade "1";
+EOF
+
+# lets not write into upstream 50unattended-upgrades file,
+# but use our own config file to avoid clashes
+
+conf_file='/etc/apt/apt.conf.d/51unattended-upgrades-cdist'
+
+conf='# this file is managed by cdist'
+
+if [ -f "$__object/parameter/option" ]
+then
+ o=''
+
+ while read -r l
+ do
+ o="$( printf '%s\nUnattended-Upgrade::%s "%s";\n' "$o" "${l%%=*}" "${l#*=}" )"
+ done \
+ < "$__object/parameter/option"
+
+ conf="$( printf '%s\n%s\n' "$conf" "$o" )"
+fi
+
+if [ -f "$__object/parameter/blacklist" ]
+then
+ b='Unattended-Upgrade::Package-Blacklist {'
+
+ while read -r l
+ do
+ b="$( printf '%s\n"%s";\n' "$b" "$l" )"
+ done \
+ < "$__object/parameter/blacklist"
+
+ conf="$( printf '%s\n%s\n}\n' "$conf" "$b" )"
+fi
+
+if [ "$( echo "$conf" | wc -l )" -gt 1 ]
+then
+ echo "$conf" \
+ | __file "$conf_file" \
+ --owner root \
+ --group root \
+ --mode 644 \
+ --source -
+else
+ __file "$conf_file" --state absent
+fi
diff --git a/cdist/conf/type/__apt_unattended_upgrades/parameter/optional_multiple b/cdist/conf/type/__apt_unattended_upgrades/parameter/optional_multiple
new file mode 100644
index 00000000..ea4fba2b
--- /dev/null
+++ b/cdist/conf/type/__apt_unattended_upgrades/parameter/optional_multiple
@@ -0,0 +1,2 @@
+option
+blacklist
diff --git a/cdist/conf/type/__apt_unattended_upgrades/singleton b/cdist/conf/type/__apt_unattended_upgrades/singleton
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__block/gencode-remote b/cdist/conf/type/__block/gencode-remote
index f269c785..7a1f4064 100755
--- a/cdist/conf/type/__block/gencode-remote
+++ b/cdist/conf/type/__block/gencode-remote
@@ -18,6 +18,11 @@
# along with cdist. If not, see .
#
+# quote function from http://www.etalabs.net/sh_tricks.html
+quote() {
+ printf '%s\n' "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/"
+}
+
file="$(cat "$__object/parameter/file" 2>/dev/null || echo "/$__object_id")"
state_should=$(cat "$__object/parameter/state")
prefix=$(cat "$__object/parameter/prefix" 2>/dev/null || echo "#cdist:__block/$__object_id")
@@ -41,28 +46,29 @@ fi
remove_block() {
cat << DONE
-tmpfile=\$(mktemp ${file}.cdist.XXXXXXXXXX)
+tmpfile=\$(mktemp ${quoted_file}.cdist.XXXXXXXXXX)
# preserve ownership and permissions of existing file
-if [ -f "$file" ]; then
- cp -p "$file" "\$tmpfile"
+if [ -f $quoted_file ]; then
+ cp -p $quoted_file "\$tmpfile"
fi
-awk -v prefix="^$prefix\$" -v suffix="^$suffix\$" '
+awk -v prefix=$(quote "$prefix") -v suffix=$(quote "$suffix") '
{
- if (match(\$0,prefix)) {
+ if (\$0 == prefix) {
triggered=1
}
if (triggered) {
- if (match(\$0,suffix)) {
+ if (\$0 == suffix) {
triggered=0
}
} else {
print
}
-}' "$file" > "\$tmpfile"
-mv -f "\$tmpfile" "$file"
+}' $quoted_file > "\$tmpfile"
+mv -f "\$tmpfile" $quoted_file
DONE
}
+quoted_file="$(quote "$file")"
case "$state_should" in
present)
if [ "$state_is" = "changed" ]; then
@@ -72,7 +78,7 @@ case "$state_should" in
echo add >> "$__messages_out"
fi
cat << DONE
-cat >> "$file" << ${__type##*/}_DONE
+cat >> $quoted_file << '${__type##*/}_DONE'
$(cat "$block")
${__type##*/}_DONE
DONE
diff --git a/cdist/conf/type/__block/manifest b/cdist/conf/type/__block/manifest
index 8fea3e83..726950d3 100755
--- a/cdist/conf/type/__block/manifest
+++ b/cdist/conf/type/__block/manifest
@@ -18,8 +18,6 @@
# along with cdist. If not, see .
#
-
-file="$(cat "$__object/parameter/file" 2>/dev/null || echo "/$__object_id")"
prefix=$(cat "$__object/parameter/prefix" 2>/dev/null || echo "#cdist:__block/$__object_id")
suffix=$(cat "$__object/parameter/suffix" 2>/dev/null || echo "#/cdist:__block/$__object_id")
text=$(cat "$__object/parameter/text")
diff --git a/cdist/conf/type/__ccollect_source/gencode-remote b/cdist/conf/type/__ccollect_source/gencode-remote
index 56003fef..57353c24 100755
--- a/cdist/conf/type/__ccollect_source/gencode-remote
+++ b/cdist/conf/type/__ccollect_source/gencode-remote
@@ -42,21 +42,20 @@ get_current_value() {
}
set_group() {
- echo chgrp \"$1\" \"$destination\"
- echo chgrp $1 >> "$__messages_out"
+ echo "chgrp '$1' '$destination'"
+ echo "chgrp '$1'" >> "$__messages_out"
}
set_owner() {
- echo chown \"$1\" \"$destination\"
- echo chown $1 >> "$__messages_out"
+ echo "chown '$1' '$destination'"
+ echo "chown '$1'" >> "$__messages_out"
}
set_mode() {
- echo chmod \"$1\" \"$destination\"
- echo chmod $1 >> "$__messages_out"
+ echo "chmod '$1' '$destination'"
+ echo "chmod '$1'" >> "$__messages_out"
}
-set_attributes=
case "$state_should" in
present|exists)
# Note: Mode - needs to happen last as a chown/chgrp can alter mode by
@@ -67,11 +66,11 @@ case "$state_should" in
# change 0xxx format to xxx format => same as stat returns
if [ "$attribute" = mode ]; then
- value_should="$(echo $value_should | sed 's/^0\(...\)/\1/')"
+ value_should="$(echo "$value_should" | sed 's/^0\(...\)/\1/')"
fi
value_is="$(get_current_value "$attribute" "$value_should")"
- if [ -f "$__object/files/set-attributes" -o "$value_should" != "$value_is" ]; then
+ if [ -f "$__object/files/set-attributes" ] || [ "$value_should" != "$value_is" ]; then
"set_$attribute" "$value_should"
fi
fi
@@ -81,7 +80,7 @@ case "$state_should" in
absent)
if [ "$type" = "file" ]; then
- echo rm -f \"$destination\"
+ echo "rm -f '$destination'"
echo remove >> "$__messages_out"
fi
;;
diff --git a/cdist/conf/type/__ccollect_source/manifest b/cdist/conf/type/__ccollect_source/manifest
index 238c7e76..727a4c97 100755
--- a/cdist/conf/type/__ccollect_source/manifest
+++ b/cdist/conf/type/__ccollect_source/manifest
@@ -22,7 +22,7 @@ name="$__object_id"
state="$(cat "$__object/parameter/state")"
source="$(cat "$__object/parameter/source")"
destination="$(cat "$__object/parameter/destination")"
-ccollectconf="$(cat "$__object/parameter/ccollectconf" | sed 's,/$,,')"
+ccollectconf="$(sed 's,/$,,' "$__object/parameter/ccollectconf")"
sourcedir="$ccollectconf/sources"
basedir="$sourcedir/$name"
@@ -55,5 +55,5 @@ if [ -f "$__object/parameter/exclude" ]; then
fi
if [ -f "$__object/parameter/create-destination" ]; then
- __directory "${destination}" --parents --state ${state}
+ __directory "${destination}" --parents --state "${state}"
fi
diff --git a/cdist/conf/type/__cdist/man.rst b/cdist/conf/type/__cdist/man.rst
index 9e1c72cb..be082781 100644
--- a/cdist/conf/type/__cdist/man.rst
+++ b/cdist/conf/type/__cdist/man.rst
@@ -30,7 +30,7 @@ username
source
Select the source from which to clone cdist from.
- Defaults to "git://github.com/ungleich/cdist.git".
+ Defaults to "git@code.ungleich.ch:ungleich-public/cdist.git".
branch
@@ -47,7 +47,7 @@ EXAMPLES
__cdist /home/cdist/cdist
# Use alternative source
- __cdist --source "git://github.com/ungleich/cdist" /home/cdist/cdist
+ __cdist --source "git@code.ungleich.ch:ungleich-public/cdist.git" /home/cdist/cdist
AUTHORS
diff --git a/cdist/conf/type/__cdist/manifest b/cdist/conf/type/__cdist/manifest
index a97cf288..0b0f1263 100755
--- a/cdist/conf/type/__cdist/manifest
+++ b/cdist/conf/type/__cdist/manifest
@@ -37,6 +37,7 @@ source="$(cat "$__object/parameter/source")"
# out of it
home=/home/$username
+# shellcheck disable=SC2086
__user "$username" --home "$home" $shell
require="__user/$username" __directory "$home" \
diff --git a/cdist/conf/type/__cdist/parameter/default/source b/cdist/conf/type/__cdist/parameter/default/source
index 3f8e31ad..1ad3a250 100644
--- a/cdist/conf/type/__cdist/parameter/default/source
+++ b/cdist/conf/type/__cdist/parameter/default/source
@@ -1 +1 @@
-git://github.com/ungleich/cdist.git
+git@code.ungleich.ch:ungleich-public/cdist.git
diff --git a/cdist/conf/type/__check_messages/gencode-remote b/cdist/conf/type/__check_messages/gencode-remote
new file mode 100755
index 00000000..ec36cecc
--- /dev/null
+++ b/cdist/conf/type/__check_messages/gencode-remote
@@ -0,0 +1,26 @@
+#!/bin/sh -e
+#
+# 2019 Ander Punnar (ander-at-kvlt-dot-ee)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+if grep -Eq \
+ "$( cat "$__object/parameter/pattern" )" \
+ "$__messages_in"
+then
+ tee "$__messages_out" < "$__object/parameter/execute"
+fi
diff --git a/cdist/conf/type/__check_messages/man.rst b/cdist/conf/type/__check_messages/man.rst
new file mode 100644
index 00000000..5c80a0ae
--- /dev/null
+++ b/cdist/conf/type/__check_messages/man.rst
@@ -0,0 +1,52 @@
+cdist-type__check_messages(7)
+=============================
+
+NAME
+----
+cdist-type__check_messages - Check messages for pattern and execute command on match.
+
+
+DESCRIPTION
+-----------
+Check messages for pattern and execute command on match.
+
+This type is useful if you chain together multiple related types using
+dependencies and want to restart service if at least one type changes
+something.
+
+For more information about messages see `cdist messaging `_.
+
+For more information about dependencies and execution order see
+`cdist manifest `_ documentation.
+
+
+REQUIRED PARAMETERS
+-------------------
+pattern
+ Extended regular expression pattern for search (passed to ``grep -E``).
+
+execute
+ Command to execute on pattern match.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ __check_messages munin \
+ --pattern '^__(file|link|line)/etc/munin/' \
+ --execute 'service munin-node restart'
+
+
+AUTHORS
+-------
+Ander Punnar
+
+
+COPYING
+-------
+Copyright \(C) 2019 Ander Punnar. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__check_messages/parameter/required b/cdist/conf/type/__check_messages/parameter/required
new file mode 100644
index 00000000..374363cb
--- /dev/null
+++ b/cdist/conf/type/__check_messages/parameter/required
@@ -0,0 +1,2 @@
+pattern
+execute
diff --git a/cdist/conf/type/__clean_path/explorer/list b/cdist/conf/type/__clean_path/explorer/list
new file mode 100755
index 00000000..2bdc63a5
--- /dev/null
+++ b/cdist/conf/type/__clean_path/explorer/list
@@ -0,0 +1,40 @@
+#!/bin/sh -e
+#
+# 2019 Ander Punnar (ander-at-kvlt-dot-ee)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+if [ -f "$__object/parameter/path" ]
+then
+ path="$( cat "$__object/parameter/path" )"
+else
+ path="/$__object_id"
+fi
+
+[ ! -d "$path" ] && exit 0
+
+pattern="$( cat "$__object/parameter/pattern" )"
+
+if [ -f "$__object/parameter/exclude" ]
+then
+ exclude="$( cat "$__object/parameter/exclude" )"
+
+ find "$path" -mindepth 1 -maxdepth 1 -regex "$pattern" \
+ -and -not -regex "$exclude"
+else
+ find "$path" -mindepth 1 -maxdepth 1 -regex "$pattern"
+fi
diff --git a/cdist/conf/type/__clean_path/gencode-remote b/cdist/conf/type/__clean_path/gencode-remote
new file mode 100755
index 00000000..2899c4a5
--- /dev/null
+++ b/cdist/conf/type/__clean_path/gencode-remote
@@ -0,0 +1,53 @@
+#!/bin/sh -e
+#
+# 2019 Ander Punnar (ander-at-kvlt-dot-ee)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+[ ! -s "$__object/explorer/list" ] && exit 0
+
+if [ -f "$__object/parameter/path" ]
+then
+ path="$( cat "$__object/parameter/path" )"
+else
+ path="/$__object_id"
+fi
+
+pattern="$( cat "$__object/parameter/pattern" )"
+
+if [ -f "$__object/parameter/exclude" ]
+then
+ exclude="$( cat "$__object/parameter/exclude" )"
+
+ echo "find '$path' -mindepth 1 -maxdepth 1 -regex '$pattern'" \
+ "-and -not -regex '$exclude'" \
+ '-exec rm -rf {} \;'
+else
+ echo "find '$path' -mindepth 1 -maxdepth 1 -regex '$pattern'" \
+ '-exec rm -rf {} \;'
+fi
+
+while read -r f
+do
+ echo "removed '$f'" >> "$__messages_out"
+done \
+< "$__object/explorer/list"
+
+if [ -f "$__object/parameter/onchange" ]
+then
+ cat "$__object/parameter/onchange"
+fi
diff --git a/cdist/conf/type/__clean_path/man.rst b/cdist/conf/type/__clean_path/man.rst
new file mode 100644
index 00000000..31d90701
--- /dev/null
+++ b/cdist/conf/type/__clean_path/man.rst
@@ -0,0 +1,68 @@
+cdist-type__clean_path(7)
+=========================
+
+NAME
+----
+cdist-type__clean_path - Remove files and directories which match the pattern.
+
+
+DESCRIPTION
+-----------
+Remove files and directories which match the pattern.
+
+Provided path must be a directory.
+
+Patterns are passed to ``find``'s ``-regex`` - see ``find(1)`` for more details.
+
+Look up of files and directories is non-recursive (``-maxdepth 1``).
+
+Parent directory is excluded (``-mindepth 1``).
+
+This type is not POSIX compatible (sorry, Solaris users).
+
+
+REQUIRED PARAMETERS
+-------------------
+pattern
+ Pattern of files which are removed from path.
+
+
+OPTIONAL PARAMETERS
+-------------------
+path
+ Path which will be cleaned. Defaults to ``$__object_id``.
+
+exclude
+ Pattern of files which are excluded from removal.
+
+onchange
+ The code to run if files or directories were removed.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ __clean_path /etc/apache2/conf-enabled \
+ --pattern '.+' \
+ --exclude '.+\(charset\.conf\|security\.conf\)' \
+ --onchange 'service apache2 restart'
+
+ __clean_path apache2-conf-enabled \
+ --path /etc/apache2/conf-enabled \
+ --pattern '.+' \
+ --exclude '.+\(charset\.conf\|security\.conf\)' \
+ --onchange 'service apache2 restart'
+
+AUTHORS
+-------
+Ander Punnar
+
+
+COPYING
+-------
+Copyright \(C) 2019 Ander Punnar. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__clean_path/parameter/optional b/cdist/conf/type/__clean_path/parameter/optional
new file mode 100644
index 00000000..3b97f71c
--- /dev/null
+++ b/cdist/conf/type/__clean_path/parameter/optional
@@ -0,0 +1,3 @@
+exclude
+onchange
+path
diff --git a/cdist/conf/type/__clean_path/parameter/required b/cdist/conf/type/__clean_path/parameter/required
new file mode 100644
index 00000000..54774947
--- /dev/null
+++ b/cdist/conf/type/__clean_path/parameter/required
@@ -0,0 +1 @@
+pattern
diff --git a/cdist/conf/type/__config_file/manifest b/cdist/conf/type/__config_file/manifest
index 3155f79b..be8f9f67 100755
--- a/cdist/conf/type/__config_file/manifest
+++ b/cdist/conf/type/__config_file/manifest
@@ -19,7 +19,8 @@
#
set -- "/${__object_id}"
-for param in $(ls "$__object/parameter/"); do
+cd "$__object/parameter/"
+for param in *; do
case "$param" in
source)
source="$(cat "$__object/parameter/source")"
diff --git a/cdist/conf/type/__consul/files/versions/1.0.6/cksum b/cdist/conf/type/__consul/files/versions/1.0.6/cksum
new file mode 100644
index 00000000..b70b55f4
--- /dev/null
+++ b/cdist/conf/type/__consul/files/versions/1.0.6/cksum
@@ -0,0 +1 @@
+4120550353 48801129 consul
diff --git a/cdist/conf/type/__consul/files/versions/1.0.6/source b/cdist/conf/type/__consul/files/versions/1.0.6/source
new file mode 100644
index 00000000..769d3134
--- /dev/null
+++ b/cdist/conf/type/__consul/files/versions/1.0.6/source
@@ -0,0 +1 @@
+https://releases.hashicorp.com/consul/1.0.6/consul_1.0.6_linux_amd64.zip
diff --git a/cdist/conf/type/__consul/files/versions/1.2.3/cksum b/cdist/conf/type/__consul/files/versions/1.2.3/cksum
new file mode 100644
index 00000000..6352409e
--- /dev/null
+++ b/cdist/conf/type/__consul/files/versions/1.2.3/cksum
@@ -0,0 +1 @@
+191982 110369685
diff --git a/cdist/conf/type/__consul/files/versions/1.2.3/source b/cdist/conf/type/__consul/files/versions/1.2.3/source
new file mode 100644
index 00000000..5e67bc37
--- /dev/null
+++ b/cdist/conf/type/__consul/files/versions/1.2.3/source
@@ -0,0 +1 @@
+https://releases.hashicorp.com/consul/1.2.3/consul_1.2.3_linux_amd64.zip
diff --git a/cdist/conf/type/__consul/files/versions/1.3.0/cksum b/cdist/conf/type/__consul/files/versions/1.3.0/cksum
new file mode 100644
index 00000000..7a885378
--- /dev/null
+++ b/cdist/conf/type/__consul/files/versions/1.3.0/cksum
@@ -0,0 +1 @@
+1714523667 98363467 consul
diff --git a/cdist/conf/type/__consul/files/versions/1.3.0/source b/cdist/conf/type/__consul/files/versions/1.3.0/source
new file mode 100644
index 00000000..18a1ba8e
--- /dev/null
+++ b/cdist/conf/type/__consul/files/versions/1.3.0/source
@@ -0,0 +1 @@
+https://releases.hashicorp.com/consul/1.3.0/consul_1.3.0_linux_amd64.zip
diff --git a/cdist/conf/type/__consul/files/versions/1.5.0/cksum b/cdist/conf/type/__consul/files/versions/1.5.0/cksum
new file mode 100644
index 00000000..efca9caa
--- /dev/null
+++ b/cdist/conf/type/__consul/files/versions/1.5.0/cksum
@@ -0,0 +1 @@
+886614099 103959898 consul
diff --git a/cdist/conf/type/__consul/files/versions/1.5.0/source b/cdist/conf/type/__consul/files/versions/1.5.0/source
new file mode 100644
index 00000000..cafa9248
--- /dev/null
+++ b/cdist/conf/type/__consul/files/versions/1.5.0/source
@@ -0,0 +1 @@
+https://releases.hashicorp.com/consul/1.5.0/consul_1.5.0_linux_amd64.zip
diff --git a/cdist/conf/type/__consul/gencode-remote b/cdist/conf/type/__consul/gencode-remote
new file mode 100755
index 00000000..2a21054f
--- /dev/null
+++ b/cdist/conf/type/__consul/gencode-remote
@@ -0,0 +1,63 @@
+#!/bin/sh -e
+#
+# 2018 Darko Poljak (darko.poljak at gmail.com)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+#set -x
+
+if [ ! -f "$__object/parameter/direct" ]; then
+ # Nothing here, staged file is used.
+ exit 0
+fi
+
+state=$(cat "$__object/parameter/state")
+destination="/usr/local/bin/consul"
+
+if [ "$state" = "absent" ]; then
+ printf 'rm -f "%s"' "$destination"
+ exit 0
+fi
+
+versions_dir="$__type/files/versions"
+version="$(cat "$__object/parameter/version")"
+version_dir="$versions_dir/$version"
+
+source=$(cat "$version_dir/source")
+source_file_name="${source##*/}"
+cksum_should=$(cut -d' ' -f1,2 "$version_dir/cksum")
+
+cat << eof
+ tmpdir=\$(mktemp -d -p /tmp "${__type##*/}.XXXXXXXXXX")
+ curl -s -L "$source" > "\$tmpdir/$source_file_name"
+ unzip -p "\$tmpdir/$source_file_name" > "${destination}.tmp"
+ rm -rf "\$tmpdir"
+
+ cksum_is=\$(cksum "${destination}.tmp" | cut -d' ' -f1,2)
+ if [ "\$cksum_is" = "$cksum_should" ]; then
+ rm -f "${destination}"
+ mv "${destination}.tmp" "${destination}"
+ chown root:root "$destination"
+ chmod 755 "$destination"
+ else
+ rm -f "${destination}.tmp"
+ echo "Failed to verify checksum for $__object_name" >&2
+ exit 1
+ fi
+eof
+
+echo "/usr/local/bin/consul created" >> "$__messages_out"
diff --git a/cdist/conf/type/__consul/man.rst b/cdist/conf/type/__consul/man.rst
index 19ceb535..5b2db50a 100644
--- a/cdist/conf/type/__consul/man.rst
+++ b/cdist/conf/type/__consul/man.rst
@@ -10,7 +10,8 @@ DESCRIPTION
-----------
Downloads and installs the consul binary from https://dl.bintray.com/mitchellh/consul.
Note that the consul binary is downloaded on the server (the machine running
-cdist) and then deployed to the target host using the __file type.
+cdist) and then deployed to the target host using the __file type unless --direct
+parameter is used.
REQUIRED PARAMETERS
@@ -28,6 +29,22 @@ version
supported versions. Defaults to the latest known version.
+BOOLEAN PARAMETERS
+------------------
+direct
+ Download and deploy consul binary directly on the target machine.
+
+
+MESSAGES
+--------
+If consul binary is created using __staged_file then underlaying __file type messages are emitted.
+
+If consul binary is created by direct method then the following messages are emitted:
+
+/usr/local/bin/consul created
+ consul binary was created
+
+
EXAMPLES
--------
@@ -36,6 +53,9 @@ EXAMPLES
# just install using defaults
__consul
+ # install by downloading consul binary directly on the target machine
+ __consul --direct
+
# specific version
__consul \
--version 0.4.1
@@ -43,7 +63,8 @@ EXAMPLES
AUTHORS
-------
-Steven Armstrong
+| Steven Armstrong
+| Darko Poljak
COPYING
diff --git a/cdist/conf/type/__consul/manifest b/cdist/conf/type/__consul/manifest
index cd79e5d9..156eb667 100755
--- a/cdist/conf/type/__consul/manifest
+++ b/cdist/conf/type/__consul/manifest
@@ -2,6 +2,7 @@
#
# 2015 Steven Armstrong (steven-cdist at armstrong.cc)
# 2016 Nico Schottelius (nico-cdist at schottelius.org)
+# 2018 Darko Poljak (darko.poljak at gmail.com)
#
# This file is part of cdist.
#
@@ -23,7 +24,7 @@
os=$(cat "$__global/explorer/os")
case "$os" in
- scientific|centos|redhat|ubuntu|debian|devuan|archlinux|gentoo)
+ alpine|scientific|centos|redhat|ubuntu|debian|devuan|archlinux|gentoo)
# any linux should work
:
;;
@@ -44,12 +45,17 @@ if [ ! -d "$version_dir" ]; then
exit 1
fi
-__staged_file /usr/local/bin/consul \
- --source "$(cat "$version_dir/source")" \
- --cksum "$(cat "$version_dir/cksum")" \
- --fetch-command 'curl -s -L "%s"' \
- --prepare-command 'unzip -p "%s"' \
- --state "$(cat "$__object/parameter/state")" \
- --group root \
- --owner root \
- --mode 755
+if [ -f "$__object/parameter/direct" ]; then
+ __package unzip
+ __package curl
+else
+ __staged_file /usr/local/bin/consul \
+ --source "$(cat "$version_dir/source")" \
+ --cksum "$(cat "$version_dir/cksum")" \
+ --fetch-command 'curl -s -L "%s"' \
+ --prepare-command 'unzip -p "%s"' \
+ --state "$(cat "$__object/parameter/state")" \
+ --group root \
+ --owner root \
+ --mode 755
+fi
diff --git a/cdist/conf/type/__consul/parameter/boolean b/cdist/conf/type/__consul/parameter/boolean
new file mode 100644
index 00000000..aa81b5e0
--- /dev/null
+++ b/cdist/conf/type/__consul/parameter/boolean
@@ -0,0 +1 @@
+direct
diff --git a/cdist/conf/type/__consul/parameter/default/version b/cdist/conf/type/__consul/parameter/default/version
index d2b13eb6..af0b7ddb 100644
--- a/cdist/conf/type/__consul/parameter/default/version
+++ b/cdist/conf/type/__consul/parameter/default/version
@@ -1 +1 @@
-0.6.4
+1.0.6
diff --git a/cdist/conf/type/__consul_agent/files/consul.sys-openrc b/cdist/conf/type/__consul_agent/files/consul.sys-openrc
new file mode 100644
index 00000000..1dbe9375
--- /dev/null
+++ b/cdist/conf/type/__consul_agent/files/consul.sys-openrc
@@ -0,0 +1,38 @@
+#!/sbin/openrc-run
+# 2019 Nico Schottelius (nico-cdist at schottelius.org)
+
+description="consul agent"
+
+pidfile="${CONSUL_PIDFILE:-"/var/run/$RC_SVCNAME/pidfile"}"
+command="${CONSUL_BINARY:-"/usr/local/bin/consul"}"
+
+
+checkconfig() {
+ if [ ! -d /var/run/consul ] ; then
+ mkdir -p /var/run/consul || return 1
+ chown consul:consul /var/run/$NAME || return 1
+ chmod 2770 /var/run/$NAME || return 1
+ fi
+}
+
+start() {
+ need net
+
+ start-stop-daemon --start --quiet --oknodo \
+ --pidfile "$pidfile" --background \
+ --exec $command -- agent -pid-file="$pidfile" -config-dir /etc/consul/conf.d
+}
+start_pre() {
+ checkconfig
+}
+
+stop() {
+ if [ "${RC_CMD}" = "restart" ] ; then
+ checkconfig || return 1
+ fi
+
+ ebegin "Stopping $RC_SVCNAME"
+ start-stop-daemon --stop --exec "$command" \
+ --pidfile "$pidfile" --quiet
+ eend $?
+}
diff --git a/cdist/conf/type/__consul_agent/files/consul.sysv-debian b/cdist/conf/type/__consul_agent/files/consul.sysv-debian
index a75c555d..4f43c000 100644
--- a/cdist/conf/type/__consul_agent/files/consul.sysv-debian
+++ b/cdist/conf/type/__consul_agent/files/consul.sysv-debian
@@ -1,6 +1,6 @@
#!/bin/sh
#
-# 2015 Nico Schottelius (nico-cdist at schottelius.org)
+# 2015-2018 Nico Schottelius (nico-cdist at schottelius.org)
# 2015 Steven Armstrong (steven-cdist at armstrong.cc)
#
# This file is part of cdist.
@@ -18,11 +18,24 @@
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see .
#
+### BEGIN INIT INFO
+# Provides: consul
+# Required-Start: $network $local_fs $remote_fs
+# Required-Stop: $local_fs
+# Should-Start:
+# Should-Stop:
+# Short-Description: consul
+# Description: consul agent
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+### END INIT INFO
if [ -f "/etc/default/consul" ]; then
+ # shellcheck disable=SC1091
. /etc/default/consul
fi
+# shellcheck disable=SC1091
. /lib/lsb/init-functions
NAME=consul
diff --git a/cdist/conf/type/__consul_agent/files/consul.sysv-redhat b/cdist/conf/type/__consul_agent/files/consul.sysv-redhat
index 13dafd2e..58fc9bd9 100644
--- a/cdist/conf/type/__consul_agent/files/consul.sysv-redhat
+++ b/cdist/conf/type/__consul_agent/files/consul.sysv-redhat
@@ -11,49 +11,52 @@
# pidfile: /var/run/consul/pidfile
# Source function library.
+
+# shellcheck disable=SC1091
. /etc/init.d/functions
NAME=consul
CONSUL=/usr/local/bin/consul
-CONFIG=/etc/$NAME/conf.d
-PID_FILE=/var/run/$NAME/pidfile
-LOG_FILE=/var/log/$NAME
+CONFIG="/etc/$NAME/conf.d"
+PID_FILE="/var/run/$NAME/pidfile"
+LOG_FILE="/var/log/$NAME"
-[ -e /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
-export GOMAXPROCS=${GOMAXPROCS:-2}
+# shellcheck disable=SC1090
+[ -e "/etc/sysconfig/$NAME" ] && . "/etc/sysconfig/$NAME"
+export GOMAXPROCS="${GOMAXPROCS:-2}"
-mkdir -p /var/run/$NAME
-chown consul:consul /var/run/$NAME
-chmod 2770 /var/run/$NAME
+mkdir -p "/var/run/$NAME"
+chown consul:consul "/var/run/$NAME"
+chmod 2770 "/var/run/$NAME"
start() {
- echo -n "Starting $NAME: "
+ printf "Starting %s: " "$NAME"
daemon --user=consul \
--pidfile="$PID_FILE" \
"$CONSUL" agent -pid-file="$PID_FILE" -config-dir "$CONFIG" >> "$LOG_FILE" &
retcode=$?
- touch /var/lock/subsys/$NAME
- return $retcode
+ touch "/var/lock/subsys/$NAME"
+ return "$retcode"
}
stop() {
- echo -n "Shutting down $NAME: "
- killproc -p "$PID_FILE" $NAME
+ printf "Shutting down %s: " "$NAME"
+ killproc -p "$PID_FILE" "$NAME"
retcode=$?
- rm -f /var/lock/subsys/$NAME
- return $retcode
+ rm -f "/var/lock/subsys/$NAME"
+ return "$retcode"
}
case "$1" in
start)
- if $(status -p "$PID_FILE" $NAME >/dev/null); then
+ if status -p "$PID_FILE" "$NAME" >/dev/null; then
echo "$NAME already running"
else
start
fi
;;
stop)
- if $(status -p "$PID_FILE" $NAME >/dev/null); then
+ if status -p "$PID_FILE" "$NAME" >/dev/null; then
stop
else
echo "$NAME not running"
@@ -63,25 +66,25 @@ case "$1" in
"$CONSUL" info
;;
status)
- status -p "$PID_FILE" $NAME
+ status -p "$PID_FILE" "$NAME"
exit $?
;;
restart)
- if $(status -p "$PID_FILE" $NAME >/dev/null); then
+ if status -p "$PID_FILE" "$NAME" >/dev/null; then
stop
fi
start
;;
reload)
- if $(status -p "$PID_FILE" $NAME >/dev/null); then
- kill -HUP `cat $PID_FILE`
+ if status -p "$PID_FILE" "$NAME" >/dev/null; then
+ kill -HUP "$(cat "$PID_FILE")"
else
echo "$NAME not running"
fi
;;
condrestart)
- if [ -f /var/lock/subsys/$NAME ]; then
- if $(status -p "$PID_FILE" $NAME >/dev/null); then
+ if [ -f "/var/lock/subsys/$NAME" ]; then
+ if status -p "$PID_FILE" "$NAME" >/dev/null; then
stop
fi
start
diff --git a/cdist/conf/type/__consul_agent/man.rst b/cdist/conf/type/__consul_agent/man.rst
index 966abc60..62ee70bb 100644
--- a/cdist/conf/type/__consul_agent/man.rst
+++ b/cdist/conf/type/__consul_agent/man.rst
@@ -116,6 +116,9 @@ verify-incoming
verify-outgoing
enforce the use of TLS and verify the peers authenticity on outgoing connections
+use-distribution-package
+ uses distribution package instead of upstream binary
+
EXAMPLES
--------
diff --git a/cdist/conf/type/__consul_agent/manifest b/cdist/conf/type/__consul_agent/manifest
index 820018c9..7b54529c 100755
--- a/cdist/conf/type/__consul_agent/manifest
+++ b/cdist/conf/type/__consul_agent/manifest
@@ -1,7 +1,8 @@
#!/bin/sh -e
#
# 2015 Steven Armstrong (steven-cdist at armstrong.cc)
-# 2015 Nico Schottelius (nico-cdist at schottelius.org)
+# 2015-2020 Nico Schottelius (nico-cdist at schottelius.org)
+# 2019 Timothée Floure (timothee.floure at ungleich.ch)
#
# This file is part of cdist.
#
@@ -19,132 +20,87 @@
# along with cdist. If not, see .
#
-
os=$(cat "$__global/explorer/os")
-case "$os" in
- scientific|centos|debian|devuan|redhat|ubuntu)
- # whitelist safeguard
- :
- ;;
- *)
- echo "Your operating system ($os) is currently not supported by this type (${__type##*/})." >&2
- echo "Please contribute an implementation for it if you can." >&2
- exit 1
- ;;
-esac
+###
+# Type parameters.
state="$(cat "$__object/parameter/state")"
user="$(cat "$__object/parameter/user")"
group="$(cat "$__object/parameter/group")"
-data_dir="/var/lib/consul"
-conf_dir="/etc/consul/conf.d"
-conf_file="config.json"
+release=$(cat "$__global/explorer/lsb_release")
+if [ -f "$__object/parameter/use-distribution-package" ]; then
+ use_distribution_package=1
+fi
-# FIXME: there has got to be a better way to handle the dependencies in this case
-case "$state" in
- present)
- __group "$group" --system --state "$state"
- require="__group/$group" \
- __user "$user" --system --gid "$group" \
- --home "$data_dir" --state "$state"
- export require="__user/consul"
- ;;
- absent)
- echo "Sorry, state=absent currently not supported :-(" >&2
- exit 1
- require="$__object_name" \
- __user "$user" --system --gid "$group" --state "$state"
- require="__user/$user" \
- __group "$group" --system --state "$state"
- ;;
+###
+# Those are default that might be overriden by os-specific logic.
+
+data_dir="/var/lib/consul"
+
+
+
+tls_dir="$conf_dir/tls"
+
+case "$os" in
+ alpine)
+ conf_dir="/etc/consul"
+ conf_file="server.json"
+ ;;
+ *)
+ conf_dir="/etc/consul/conf.d"
+ conf_file="config.json"
+ ;;
esac
-__directory /etc/consul \
- --owner root --group "$group" --mode 750 --state "$state"
-require="__directory/etc/consul" \
- __directory "$conf_dir" \
- --owner root --group "$group" --mode 750 --state "$state"
+###
+# Sane deployment, based on distribution package when available.
-if [ -f "$__object/parameter/ca-file-source" -o -f "$__object/parameter/cert-file-source" -o -f "$__object/parameter/key-file-source" ]; then
- # create directory for ssl certs
- require="__directory/etc/consul" \
- __directory /etc/consul/ssl \
- --owner root --group "$group" --mode 750 --state "$state"
-fi
+distribution_setup () {
+ case "$os" in
+ debian)
+ # consul is only available starting Debian 10 (buster).
+ # See https://packages.debian.org/buster/consul
+ if [ "$release" -lt 10 ]; then
+ echo "Consul is not available for your debian release." >&2
+ echo "Please use the 'manual' (i.e. non-package) installation or \
+ upgrade the target system." >&2
+ exit 1
+ fi
-__directory "$data_dir" \
- --owner "$user" --group "$group" --mode 770 --state "$state"
+ # Override previously defined environment to match debian packaging.
+ conf_dir='/etc/consul.d'
+ user='consul'
+ group='consul'
+ ;;
+ alpine)
+ # consul is only available starting Alpine 3.12 (= edge during the 3.11 cycle).
+ # See https://pkgs.alpinelinux.org/packages?name=consul&branch=edge
+ # Override previously defined environment to match alpine packaging.
+ conf_dir='/etc/consul'
+ conf_file='server.json'
+ data_dir='/var/consul'
+ user='consul'
+ group='consul'
+ ;;
+ *)
+ echo "Your operating system ($os) is currently not supported with the \
+ --use-distribution-package flag (${__type##*/})." >&2
+ echo "Please use non-package installation or contribute an \
+ implementation for if you can." >&2
+ exit 1
+ ;;
+ esac
-# Generate json config file
-(
-echo "{"
+ # Install consul package.
+ __package consul --state "$state"
-# parameters we define ourself
-printf ' "data_dir": "%s"\n' "$data_dir"
+ export config_deployment_requires="__package/consul"
+}
-for param in $(ls "$__object/parameter/"); do
- case "$param" in
- state|user|group|json-config) continue ;;
- ca-file-source|cert-file-source|key-file-source)
- source="$(cat "$__object/parameter/$param")"
- destination="/etc/consul/ssl/${source##*/}"
- require="__directory/etc/consul/ssl" \
- __file "$destination" \
- --owner root --group consul --mode 640 \
- --source "$source" \
- --state "$state"
- key="$(echo "${param%-*}" | tr '-' '_')"
- printf ' ,"%s": "%s"\n' "$key" "$destination"
- ;;
- disable-remote-exec|disable-update-check|leave-on-terminate|rejoin-after-leave|server|enable-syslog|verify-incoming|verify-outgoing)
- # handle boolean parameters
- key="$(echo "$param" | tr '-' '_')"
- printf ' ,"%s": true\n' "$key"
- ;;
- retry-join)
- # join multiple parameters into json array
- retry_join="$(awk '{printf "\""$1"\","}' "$__object/parameter/retry-join")"
- # remove trailing ,
- printf ' ,"retry_join": [%s]\n' "${retry_join%*,}"
- ;;
- retry-join-wan)
- # join multiple parameters into json array over wan
- retry_join_wan="$(awk '{printf "\""$1"\","}' "$__object/parameter/retry-join-wan")"
- # remove trailing ,
- printf ' ,"retry_join_wan": [%s]\n' "${retry_join_wan%*,}"
- ;;
- bootstrap-expect)
- # integer key=value parameters
- key="$(echo "$param" | tr '-' '_')"
- printf ' ,"%s": %s\n' "$key" "$(cat "$__object/parameter/$param")"
- ;;
- *)
- # string key=value parameters
- key="$(echo "$param" | tr '-' '_')"
- printf ' ,"%s": "%s"\n' "$key" "$(cat "$__object/parameter/$param")"
- ;;
- esac
-done
-if [ -f "$__object/parameter/json-config" ]; then
- json_config="$(cat "$__object/parameter/json-config")"
- if [ "$json_config" = "-" ]; then
- json_config="$__object/stdin"
- fi
- # remove leading and trailing whitespace and commas from first and last line
- # indent each line with 3 spaces for consistency
- json=$(sed -e 's/^[ \t]*/ /' -e '1s/^[ \t,]*//' -e '$s/[ \t,]*$//' "$json_config")
- printf ' ,%s\n' "$json"
-fi
-echo "}"
-) | \
-require="__directory${conf_dir}" \
- __config_file "${conf_dir}/${conf_file}" \
- --owner root --group "$group" --mode 640 \
- --state "$state" \
- --onchange 'service consul status >/dev/null && service consul reload || true' \
- --source -
+###
+# LEGACY manual deployment, kept for compatibility reasons.
init_sysvinit()
{
@@ -178,48 +134,186 @@ init_upstart()
require="__file/etc/init/consul.conf" __start_on_boot consul
}
-# Install init script to start on boot
-case "$os" in
- centos|redhat)
- os_version="$(sed 's/[^0-9.]//g' "$__global/explorer/os_version")"
- major_version="${os_version%%.*}"
- case "$major_version" in
- [456])
- init_sysvinit redhat
- ;;
- 7)
- init_systemd
- ;;
- *)
- echo "Unsupported CentOS/Redhat version: $os_version" >&2
- exit 1
- ;;
- esac
- ;;
+manual_setup () {
+ case "$os" in
+ alpine|scientific|centos|debian|devuan|redhat|ubuntu)
+ # whitelist safeguard
+ :
+ ;;
+ *)
+ echo "Your operating system ($os) is currently not supported by this \
+ type (${__type##*/})." >&2
+ echo "Please contribute an implementation for it if you can." >&2
+ exit 1
+ ;;
+ esac
- debian)
- os_version=$(cat "$__global/explorer/os_version")
- major_version="${os_version%%.*}"
+ # FIXME: there has got to be a better way to handle the dependencies in this case
+ case "$state" in
+ present)
+ __group "$group" --system --state "$state"
+ require="__group/$group" __user "$user" \
+ --system --gid "$group" --home "$data_dir" --state "$state"
+ ;;
+ *)
+ echo "The $state state is not (yet?) supported by this type." >&2
+ exit 1
+ ;;
+ esac
- case "$major_version" in
- [567])
- init_sysvinit debian
- ;;
- 8)
- init_systemd
- ;;
- *)
- echo "Unsupported Debian version $os_version" >&2
- exit 1
- ;;
- esac
- ;;
+ # Create data directory.
+ require="__user/consul" __directory "$data_dir" \
+ --owner "$user" --group "$group" --mode 770 --state "$state"
- devuan)
- init_sysvinit debian
- ;;
+ # Create config directory.
+ require="__user/consul" __directory "$conf_dir" \
+ --parents --owner root --group "$group" --mode 750 --state "$state"
- ubuntu)
- init_upstart
- ;;
-esac
+ # Install init script to start on boot
+ case "$os" in
+ devuan)
+ init_sysvinit debian
+ ;;
+ centos|redhat)
+ os_version="$(sed 's/[^0-9.]//g' "$__global/explorer/os_version")"
+ major_version="${os_version%%.*}"
+ case "$major_version" in
+ [456])
+ init_sysvinit redhat
+ ;;
+ 7)
+ init_systemd
+ ;;
+ *)
+ echo "Unsupported CentOS/Redhat version: $os_version" >&2
+ exit 1
+ ;;
+ esac
+ ;;
+
+ debian)
+ os_version=$(cat "$__global/explorer/os_version")
+ major_version="${os_version%%.*}"
+
+ case "$major_version" in
+ [567])
+ init_sysvinit debian
+ ;;
+ [89]|10)
+ init_systemd
+ ;;
+ *)
+ echo "Unsupported Debian version $os_version" >&2
+ exit 1
+ ;;
+ esac
+ ;;
+
+ ubuntu)
+ init_upstart
+ ;;
+ esac
+
+ config_deployment_requires="__user/consul __directory/$conf_dir"
+}
+
+###
+# Trigger requested installation method.
+if [ $use_distribution_package ]; then
+ distribution_setup
+else
+ manual_setup
+fi
+
+###
+# Install TLS certificates.
+
+if [ -f "$__object/parameter/ca-file-source" ] || \
+ [ -f "$__object/parameter/cert-file-source" ] || \
+ [ -f "$__object/parameter/key-file-source" ]; then
+
+ requires="$config_deployment_requires" __directory "$tls_dir" \
+ --owner root --group "$group" --mode 750 --state "$state"
+
+ # Append to service restart requirements.
+ restart_requires="$restart_requires __directory/$conf_dir/tls"
+fi
+
+###
+# Generate and deploy configuration.
+
+json_configuration=$(
+ echo "{"
+
+ # parameters we define ourself
+ printf ' "data_dir": "%s"\n' "$data_dir"
+
+ cd "$__object/parameter/"
+ for param in *; do
+ case "$param" in
+ state|user|group|json-config|use-distribution-package) continue ;;
+ ca-file-source|cert-file-source|key-file-source)
+ source="$(cat "$__object/parameter/$param")"
+ destination="$tls_dir/${source##*/}"
+ require="__directory/$tls_dir" \
+ __file "$destination" \
+ --owner root --group consul --mode 640 \
+ --source "$source" \
+ --state "$state"
+ key="$(echo "${param%-*}" | tr '-' '_')"
+ printf ' ,"%s": "%s"\n' "$key" "$destination"
+ ;;
+ disable-remote-exec|disable-update-check|leave-on-terminate\
+ |rejoin-after-leave|server|enable-syslog|verify-incoming|verify-outgoing)
+ # handle boolean parameters
+ key="$(echo "$param" | tr '-' '_')"
+ printf ' ,"%s": true\n' "$key"
+ ;;
+ retry-join)
+ # join multiple parameters into json array
+ retry_join="$(awk '{printf "\""$1"\","}' "$__object/parameter/retry-join")"
+ # remove trailing ,
+ printf ' ,"retry_join": [%s]\n' "${retry_join%*,}"
+ ;;
+ retry-join-wan)
+ # join multiple parameters into json array over wan
+ retry_join_wan="$(awk '{printf "\""$1"\","}' "$__object/parameter/retry-join-wan")"
+ # remove trailing ,
+ printf ' ,"retry_join_wan": [%s]\n' "${retry_join_wan%*,}"
+ ;;
+ bootstrap-expect)
+ # integer key=value parameters
+ key="$(echo "$param" | tr '-' '_')"
+ printf ' ,"%s": %s\n' "$key" "$(cat "$__object/parameter/$param")"
+ ;;
+ *)
+ # string key=value parameters
+ key="$(echo "$param" | tr '-' '_')"
+ printf ' ,"%s": "%s"\n' "$key" "$(cat "$__object/parameter/$param")"
+ ;;
+ esac
+ done
+ if [ -f "$__object/parameter/json-config" ]; then
+ json_config="$(cat "$__object/parameter/json-config")"
+ if [ "$json_config" = "-" ]; then
+ json_config="$__object/stdin"
+ fi
+ # remove leading and trailing whitespace and commas from first and last line
+ # indent each line with 3 spaces for consistency
+ json=$(sed -e 's/^[ \t]*/ /' -e '1s/^[ \t,]*//' -e '$s/[ \t,]*$//' "$json_config")
+ printf ' ,%s\n' "$json"
+ fi
+ echo "}"
+)
+echo "$json_configuration" | require="$config_deployment_requires" \
+ __file "$conf_dir/$conf_file" \
+ --owner root --group "$group" --mode 640 \
+ --state "$state" \
+ --source -
+
+# Set configuration deployment as requirement for service restart.
+restart_requires="__file/$conf_dir/$conf_file"
+
+###
+# Restart consul agent after everything else.
+require="$restart_requires" __service consul --action restart
diff --git a/cdist/conf/type/__consul_agent/parameter/boolean b/cdist/conf/type/__consul_agent/parameter/boolean
index 91f7f17e..c86853c3 100644
--- a/cdist/conf/type/__consul_agent/parameter/boolean
+++ b/cdist/conf/type/__consul_agent/parameter/boolean
@@ -6,3 +6,4 @@ server
enable-syslog
verify-incoming
verify-outgoing
+use-distribution-package
diff --git a/cdist/conf/type/__consul_check/explorer/conf-dir b/cdist/conf/type/__consul_check/explorer/conf-dir
new file mode 120000
index 00000000..daa712c3
--- /dev/null
+++ b/cdist/conf/type/__consul_check/explorer/conf-dir
@@ -0,0 +1 @@
+../../__consul_service/explorer/conf-dir
\ No newline at end of file
diff --git a/cdist/conf/type/__consul_check/manifest b/cdist/conf/type/__consul_check/manifest
index 8149b130..522aa1a9 100755
--- a/cdist/conf/type/__consul_check/manifest
+++ b/cdist/conf/type/__consul_check/manifest
@@ -19,7 +19,7 @@
#
name="$(cat "$__object/parameter/name" 2>/dev/null || echo "$__object_id")"
-conf_dir="/etc/consul/conf.d"
+conf_dir=$(cat "$__object/explorer/conf-dir")
conf_file="check_${name}.json"
state="$(cat "$__object/parameter/state")"
@@ -40,7 +40,7 @@ if [ ! -f "$__object/parameter/interval" ]; then
fi
done
fi
-if [ -f "$__object/parameter/docker-container-id" -a ! -f "$__object/parameter/script" ]; then
+if [ -f "$__object/parameter/docker-container-id" ] && [ ! -f "$__object/parameter/script" ]; then
echo "When using --docker-container-id you must also define --script." >&2
exit 1
fi
@@ -50,7 +50,8 @@ fi
echo "{"
printf ' "check": {\n'
printf ' "name": "%s"\n' "$name"
-for param in $(ls "$__object/parameter/"); do
+cd "$__object/parameter/"
+for param in *; do
case "$param" in
state|name) continue ;;
*)
diff --git a/cdist/conf/type/__consul_service/explorer/conf-dir b/cdist/conf/type/__consul_service/explorer/conf-dir
new file mode 100644
index 00000000..0fc9ef84
--- /dev/null
+++ b/cdist/conf/type/__consul_service/explorer/conf-dir
@@ -0,0 +1,15 @@
+# Determine the configuration directory used by consul.
+
+check_dir () {
+ if [ -d "$1" ]; then
+ printf '%s' "$1"
+ exit
+ fi
+}
+
+check_dir '/etc/consul/conf.d'
+check_dir '/etc/consul.d'
+check_dir '/etc/consul'
+
+echo 'Could not determine consul configuration dir. Exiting.' >&2
+exit 1
diff --git a/cdist/conf/type/__consul_service/manifest b/cdist/conf/type/__consul_service/manifest
index d7a1b6e3..d16f18e0 100755
--- a/cdist/conf/type/__consul_service/manifest
+++ b/cdist/conf/type/__consul_service/manifest
@@ -19,20 +19,20 @@
#
name="$(cat "$__object/parameter/name" 2>/dev/null || echo "$__object_id")"
-conf_dir="/etc/consul/conf.d"
+conf_dir=$(cat "$__object/explorer/conf-dir")
conf_file="service_${name}.json"
state="$(cat "$__object/parameter/state")"
# Sanity checks
-if [ -f "$__object/parameter/check-script" -a -f "$__object/parameter/check-ttl" ]; then
+if [ -f "$__object/parameter/check-script" ] && [ -f "$__object/parameter/check-ttl" ]; then
echo "Use either --check-script together with --check-interval OR --check-ttl, but not both" >&2
exit 1
fi
-if [ -f "$__object/parameter/check-script" -a ! -f "$__object/parameter/check-interval" ]; then
+if [ -f "$__object/parameter/check-script" ] && [ ! -f "$__object/parameter/check-interval" ]; then
echo "When using --check-script you must also define --check-interval" >&2
exit 1
fi
-if [ -f "$__object/parameter/check-http" -a ! -f "$__object/parameter/check-interval" ]; then
+if [ -f "$__object/parameter/check-http" ] && [ ! -f "$__object/parameter/check-interval" ]; then
echo "When using --check-http you must also define --check-interval" >&2
exit 1
fi
@@ -42,9 +42,10 @@ fi
echo "{"
printf ' "service": {\n'
printf ' "name": "%s"\n' "$name"
-for param in $(ls "$__object/parameter/"); do
+cd "$__object/parameter/"
+for param in *; do
case "$param" in
- state|name|check-interval) continue ;;
+ state|name|check-interval|conf-dir) continue ;;
check-script)
printf ' ,"check": {\n'
printf ' "script": "%s"\n' "$(cat "$__object/parameter/check-script")"
@@ -85,7 +86,6 @@ echo " }"
# end json file
echo "}"
) | \
-require="__directory${conf_dir}" \
__config_file "${conf_dir}/${conf_file}" \
--owner root --group consul --mode 640 \
--state "$state" \
diff --git a/cdist/conf/type/__consul_template/files/consul-template.sysv b/cdist/conf/type/__consul_template/files/consul-template.sysv
index 0a463020..b263915a 100644
--- a/cdist/conf/type/__consul_template/files/consul-template.sysv
+++ b/cdist/conf/type/__consul_template/files/consul-template.sysv
@@ -10,72 +10,75 @@
# pidfile: /var/run/consul-template/pidfile
# Source function library.
+
+# shellcheck disable=SC1091
. /etc/init.d/functions
NAME=consul-template
CONSUL_TEMPLATE=/usr/local/bin/consul-template
-CONFIG=/etc/$NAME/conf.d
-PID_FILE=/var/run/$NAME/pidfile
-LOG_FILE=/var/log/$NAME
+CONFIG="/etc/$NAME/conf.d"
+PID_FILE="/var/run/$NAME/pidfile"
+LOG_FILE="/var/log/$NAME"
-[ -e /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
-export CONSUL_TEMPLATE_LOG=${CONSUL_TEMPLATE_LOG:-info}
-export GOMAXPROCS=${GOMAXPROCS:-2}
+# shellcheck disable=SC1090
+[ -e "/etc/sysconfig/$NAME" ] && . "/etc/sysconfig/$NAME"
+export CONSUL_TEMPLATE_LOG="${CONSUL_TEMPLATE_LOG:-info}"
+export GOMAXPROCS="${GOMAXPROCS:-2}"
-mkdir -p /var/run/$NAME
+mkdir -p "/var/run/$NAME"
start() {
- echo -n "Starting $NAME: "
+ printf "Starting %s: " "$NAME"
daemon --pidfile="$PID_FILE" \
"$CONSUL_TEMPLATE" -config "$CONFIG" >> "$LOG_FILE" 2>&1 &
- echo $! > "$PID_FILE"
+ echo "$!" > "$PID_FILE"
retcode=$?
- touch /var/lock/subsys/$NAME
- return $retcode
+ touch "/var/lock/subsys/$NAME"
+ return "$retcode"
}
stop() {
- echo -n "Shutting down $NAME: "
- killproc -p $PID_FILE $CONSUL_TEMPLATE
+ printf "Shutting down %s: " "$NAME"
+ killproc -p "$PID_FILE" "$CONSUL_TEMPLATE"
retcode=$?
- rm -f /var/lock/subsys/$NAME
- return $retcode
+ rm -f "/var/lock/subsys/$NAME"
+ return "$retcode"
}
case "$1" in
start)
- if $(status -p "$PID_FILE" $NAME >/dev/null); then
+ if status -p "$PID_FILE" "$NAME" >/dev/null; then
echo "$NAME already running"
else
start
fi
;;
stop)
- if $(status -p "$PID_FILE" $NAME >/dev/null); then
+ if status -p "$PID_FILE" "$NAME" >/dev/null; then
stop
else
echo "$NAME not running"
fi
;;
status)
- status -p "$PID_FILE" $NAME
+ status -p "$PID_FILE" "$NAME"
exit $?
;;
restart)
- if $(status -p "$PID_FILE" $NAME >/dev/null); then
+ if status -p "$PID_FILE" "$NAME" >/dev/null; then
stop
fi
start
;;
reload)
- if $(status -p "$PID_FILE" $NAME >/dev/null); then
- kill -HUP `cat $PID_FILE`
+ if status -p "$PID_FILE" "$NAME" >/dev/null; then
+ kill -HUP "$(cat "$PID_FILE")"
else
echo "$NAME not running"
fi
;;
condrestart)
- if [ -f /var/lock/subsys/$NAME ]; then
- if $(status -p "$PID_FILE" $NAME >/dev/null); then
+ if [ -f "/var/lock/subsys/$NAME" ]; then
+ if status -p "$PID_FILE" "$NAME" >/dev/null; then
stop
fi
start
diff --git a/cdist/conf/type/__consul_template/manifest b/cdist/conf/type/__consul_template/manifest
index 2236e5bd..b02fc332 100755
--- a/cdist/conf/type/__consul_template/manifest
+++ b/cdist/conf/type/__consul_template/manifest
@@ -75,7 +75,8 @@ require="__directory/etc/consul-template" \
# Generate hcl config file
(
-for param in $(ls "$__object/parameter/"); do
+cd "$__object/parameter/"
+for param in *; do
case "$param" in
auth-password|state|ssl-*|syslog-*|version|vault-token|vault-ssl*) continue ;;
auth-username)
diff --git a/cdist/conf/type/__consul_template_template/manifest b/cdist/conf/type/__consul_template_template/manifest
index 5fe657d0..1eae1fad 100755
--- a/cdist/conf/type/__consul_template_template/manifest
+++ b/cdist/conf/type/__consul_template_template/manifest
@@ -26,32 +26,36 @@ template_dir="/etc/consul-template/template"
require=""
# Sanity checks
-if [ -f "$__object/parameter/source" -a -f "$__object/parameter/source-file" ]; then
+if [ -f "$__object/parameter/source" ] && [ -f "$__object/parameter/source-file" ]; then
echo "Use either --source OR --source-file, but not both." >&2
exit 1
fi
-if [ ! -f "$__object/parameter/source" -a ! -f "$__object/parameter/source-file" ]; then
+if [ ! -f "$__object/parameter/source" ] && [ ! -f "$__object/parameter/source-file" ]; then
echo "Either --source OR --source-file must be given." >&2
exit 1
fi
+if [ -f "$__object/parameter/source-file" ]; then
+ destination="${template_dir}/${name}"
+ require="__file${destination}"
+fi
+
# Generate hcl config file
-(
+{
printf 'template {\n'
-for param in $(ls "$__object/parameter/"); do
+cd "$__object/parameter/"
+for param in *; do
case "$param" in
source-file)
source="$(cat "$__object/parameter/$param")"
if [ "$source" = "-" ]; then
source="$__object/stdin"
fi
- destination="${template_dir}/${name}"
require="__directory${template_dir}" \
__file "$destination" \
--owner root --group root --mode 640 \
--source "$source" \
--state "$state"
- export require="__file${destination}"
printf ' source = "%s"\n' "$destination"
;;
@@ -65,7 +69,7 @@ for param in $(ls "$__object/parameter/"); do
esac
done
printf '}\n'
-) | \
+} | \
require="$require __directory${conf_dir}" \
__config_file "${conf_dir}/${conf_file}" \
--owner root --group root --mode 640 \
diff --git a/cdist/conf/type/__consul_watch_checks/explorer/conf-dir b/cdist/conf/type/__consul_watch_checks/explorer/conf-dir
new file mode 120000
index 00000000..daa712c3
--- /dev/null
+++ b/cdist/conf/type/__consul_watch_checks/explorer/conf-dir
@@ -0,0 +1 @@
+../../__consul_service/explorer/conf-dir
\ No newline at end of file
diff --git a/cdist/conf/type/__consul_watch_checks/manifest b/cdist/conf/type/__consul_watch_checks/manifest
index ebb49e2e..4976b25a 100755
--- a/cdist/conf/type/__consul_watch_checks/manifest
+++ b/cdist/conf/type/__consul_watch_checks/manifest
@@ -20,12 +20,12 @@
cdist_type="${__type##*/}"
watch_type="${cdist_type##*_}"
-conf_dir="/etc/consul/conf.d"
+conf_dir=$(cat "$__object/explorer/conf-dir")
conf_file="watch_${watch_type}_${__object_id}.json"
state="$(cat "$__object/parameter/state")"
# Sanity checks
-if [ -f "$__object/parameter/filter-service" -a -f "$__object/parameter/filter-state" ]; then
+if [ -f "$__object/parameter/filter-service" ] && [ -f "$__object/parameter/filter-state" ]; then
echo "Use either --filter-service or --filter-state but not both." >&2
exit 1
fi
@@ -35,7 +35,8 @@ fi
echo "{"
printf ' "watches": [{\n'
printf ' "type": "%s"\n' "$watch_type"
-for param in $(ls "$__object/parameter/"); do
+cd "$__object/parameter/"
+for param in *; do
case "$param" in
state) continue ;;
filter-*)
diff --git a/cdist/conf/type/__consul_watch_event/explorer/conf-dir b/cdist/conf/type/__consul_watch_event/explorer/conf-dir
new file mode 120000
index 00000000..daa712c3
--- /dev/null
+++ b/cdist/conf/type/__consul_watch_event/explorer/conf-dir
@@ -0,0 +1 @@
+../../__consul_service/explorer/conf-dir
\ No newline at end of file
diff --git a/cdist/conf/type/__consul_watch_event/manifest b/cdist/conf/type/__consul_watch_event/manifest
index 099054a5..b17680c1 100755
--- a/cdist/conf/type/__consul_watch_event/manifest
+++ b/cdist/conf/type/__consul_watch_event/manifest
@@ -20,7 +20,7 @@
cdist_type="${__type##*/}"
watch_type="${cdist_type##*_}"
-conf_dir="/etc/consul/conf.d"
+conf_dir=$(cat "$__object/explorer/conf-dir")
conf_file="watch_${watch_type}_${__object_id}.json"
state="$(cat "$__object/parameter/state")"
@@ -29,7 +29,8 @@ state="$(cat "$__object/parameter/state")"
echo "{"
printf ' "watches": [{\n'
printf ' "type": "%s"\n' "$watch_type"
-for param in $(ls "$__object/parameter/"); do
+cd "$__object/parameter/"
+for param in *; do
case "$param" in
state) continue ;;
*)
diff --git a/cdist/conf/type/__consul_watch_key/explorer/conf-dir b/cdist/conf/type/__consul_watch_key/explorer/conf-dir
new file mode 120000
index 00000000..daa712c3
--- /dev/null
+++ b/cdist/conf/type/__consul_watch_key/explorer/conf-dir
@@ -0,0 +1 @@
+../../__consul_service/explorer/conf-dir
\ No newline at end of file
diff --git a/cdist/conf/type/__consul_watch_key/manifest b/cdist/conf/type/__consul_watch_key/manifest
index 099054a5..b17680c1 100755
--- a/cdist/conf/type/__consul_watch_key/manifest
+++ b/cdist/conf/type/__consul_watch_key/manifest
@@ -20,7 +20,7 @@
cdist_type="${__type##*/}"
watch_type="${cdist_type##*_}"
-conf_dir="/etc/consul/conf.d"
+conf_dir=$(cat "$__object/explorer/conf-dir")
conf_file="watch_${watch_type}_${__object_id}.json"
state="$(cat "$__object/parameter/state")"
@@ -29,7 +29,8 @@ state="$(cat "$__object/parameter/state")"
echo "{"
printf ' "watches": [{\n'
printf ' "type": "%s"\n' "$watch_type"
-for param in $(ls "$__object/parameter/"); do
+cd "$__object/parameter/"
+for param in *; do
case "$param" in
state) continue ;;
*)
diff --git a/cdist/conf/type/__consul_watch_keyprefix/explorer/conf-dir b/cdist/conf/type/__consul_watch_keyprefix/explorer/conf-dir
new file mode 120000
index 00000000..daa712c3
--- /dev/null
+++ b/cdist/conf/type/__consul_watch_keyprefix/explorer/conf-dir
@@ -0,0 +1 @@
+../../__consul_service/explorer/conf-dir
\ No newline at end of file
diff --git a/cdist/conf/type/__consul_watch_keyprefix/manifest b/cdist/conf/type/__consul_watch_keyprefix/manifest
index 099054a5..b17680c1 100755
--- a/cdist/conf/type/__consul_watch_keyprefix/manifest
+++ b/cdist/conf/type/__consul_watch_keyprefix/manifest
@@ -20,7 +20,7 @@
cdist_type="${__type##*/}"
watch_type="${cdist_type##*_}"
-conf_dir="/etc/consul/conf.d"
+conf_dir=$(cat "$__object/explorer/conf-dir")
conf_file="watch_${watch_type}_${__object_id}.json"
state="$(cat "$__object/parameter/state")"
@@ -29,7 +29,8 @@ state="$(cat "$__object/parameter/state")"
echo "{"
printf ' "watches": [{\n'
printf ' "type": "%s"\n' "$watch_type"
-for param in $(ls "$__object/parameter/"); do
+cd "$__object/parameter/"
+for param in *; do
case "$param" in
state) continue ;;
*)
diff --git a/cdist/conf/type/__consul_watch_nodes/explorer/conf-dir b/cdist/conf/type/__consul_watch_nodes/explorer/conf-dir
new file mode 120000
index 00000000..daa712c3
--- /dev/null
+++ b/cdist/conf/type/__consul_watch_nodes/explorer/conf-dir
@@ -0,0 +1 @@
+../../__consul_service/explorer/conf-dir
\ No newline at end of file
diff --git a/cdist/conf/type/__consul_watch_nodes/manifest b/cdist/conf/type/__consul_watch_nodes/manifest
index 099054a5..b17680c1 100755
--- a/cdist/conf/type/__consul_watch_nodes/manifest
+++ b/cdist/conf/type/__consul_watch_nodes/manifest
@@ -20,7 +20,7 @@
cdist_type="${__type##*/}"
watch_type="${cdist_type##*_}"
-conf_dir="/etc/consul/conf.d"
+conf_dir=$(cat "$__object/explorer/conf-dir")
conf_file="watch_${watch_type}_${__object_id}.json"
state="$(cat "$__object/parameter/state")"
@@ -29,7 +29,8 @@ state="$(cat "$__object/parameter/state")"
echo "{"
printf ' "watches": [{\n'
printf ' "type": "%s"\n' "$watch_type"
-for param in $(ls "$__object/parameter/"); do
+cd "$__object/parameter/"
+for param in *; do
case "$param" in
state) continue ;;
*)
diff --git a/cdist/conf/type/__consul_watch_service/explorer/conf-dir b/cdist/conf/type/__consul_watch_service/explorer/conf-dir
new file mode 120000
index 00000000..daa712c3
--- /dev/null
+++ b/cdist/conf/type/__consul_watch_service/explorer/conf-dir
@@ -0,0 +1 @@
+../../__consul_service/explorer/conf-dir
\ No newline at end of file
diff --git a/cdist/conf/type/__consul_watch_service/manifest b/cdist/conf/type/__consul_watch_service/manifest
index 2825c716..e8d18328 100755
--- a/cdist/conf/type/__consul_watch_service/manifest
+++ b/cdist/conf/type/__consul_watch_service/manifest
@@ -20,7 +20,7 @@
cdist_type="${__type##*/}"
watch_type="${cdist_type##*_}"
-conf_dir="/etc/consul/conf.d"
+conf_dir=$(cat "$__object/explorer/conf-dir")
conf_file="watch_${watch_type}_${__object_id}.json"
state="$(cat "$__object/parameter/state")"
@@ -29,7 +29,8 @@ state="$(cat "$__object/parameter/state")"
echo "{"
printf ' "watches": [{\n'
printf ' "type": "%s"\n' "$watch_type"
-for param in $(ls "$__object/parameter/"); do
+cd "$__object/parameter/"
+for param in *; do
case "$param" in
state) continue ;;
passingonly)
diff --git a/cdist/conf/type/__consul_watch_services/explorer/conf-dir b/cdist/conf/type/__consul_watch_services/explorer/conf-dir
new file mode 120000
index 00000000..daa712c3
--- /dev/null
+++ b/cdist/conf/type/__consul_watch_services/explorer/conf-dir
@@ -0,0 +1 @@
+../../__consul_service/explorer/conf-dir
\ No newline at end of file
diff --git a/cdist/conf/type/__consul_watch_services/manifest b/cdist/conf/type/__consul_watch_services/manifest
index 099054a5..b17680c1 100755
--- a/cdist/conf/type/__consul_watch_services/manifest
+++ b/cdist/conf/type/__consul_watch_services/manifest
@@ -20,7 +20,7 @@
cdist_type="${__type##*/}"
watch_type="${cdist_type##*_}"
-conf_dir="/etc/consul/conf.d"
+conf_dir=$(cat "$__object/explorer/conf-dir")
conf_file="watch_${watch_type}_${__object_id}.json"
state="$(cat "$__object/parameter/state")"
@@ -29,7 +29,8 @@ state="$(cat "$__object/parameter/state")"
echo "{"
printf ' "watches": [{\n'
printf ' "type": "%s"\n' "$watch_type"
-for param in $(ls "$__object/parameter/"); do
+cd "$__object/parameter/"
+for param in *; do
case "$param" in
state) continue ;;
*)
diff --git a/cdist/conf/type/__cron/explorer/entry b/cdist/conf/type/__cron/explorer/entry
index 2167e045..801861a3 100644
--- a/cdist/conf/type/__cron/explorer/entry
+++ b/cdist/conf/type/__cron/explorer/entry
@@ -24,7 +24,7 @@ user="$(cat "$__object/parameter/user")"
if [ -f "$__object/parameter/raw_command" ]; then
command="$(cat "$__object/parameter/command")"
- crontab -u $user -l 2>/dev/null | grep "^$command\$" || true
+ crontab -u "$user" -l 2>/dev/null | grep "^$command\$" || true
else
- crontab -u $user -l 2>/dev/null | grep "# $name\$" || true
+ crontab -u "$user" -l 2>/dev/null | grep "# $name\$" || true
fi
diff --git a/cdist/conf/type/__cron/gencode-remote b/cdist/conf/type/__cron/gencode-remote
index f58896af..9debbc47 100755
--- a/cdist/conf/type/__cron/gencode-remote
+++ b/cdist/conf/type/__cron/gencode-remote
@@ -31,24 +31,28 @@ if [ -f "$__object/parameter/raw" ]; then
elif [ -f "$__object/parameter/raw_command" ]; then
entry="$command"
else
- minute="$(cat "$__object/parameter/minute" 2>/dev/null || echo "*")"
- hour="$(cat "$__object/parameter/hour" 2>/dev/null || echo "*")"
- day_of_month="$(cat "$__object/parameter/day_of_month" 2>/dev/null || echo "*")"
- month="$(cat "$__object/parameter/month" 2>/dev/null || echo "*")"
- day_of_week="$(cat "$__object/parameter/day_of_week" 2>/dev/null || echo "*")"
+ minute="$(cat "$__object/parameter/minute")"
+ hour="$(cat "$__object/parameter/hour")"
+ day_of_month="$(cat "$__object/parameter/day_of_month")"
+ month="$(cat "$__object/parameter/month")"
+ day_of_week="$(cat "$__object/parameter/day_of_week")"
entry="$minute $hour $day_of_month $month $day_of_week $command # $name"
fi
mkdir "$__object/files"
echo "$entry" > "$__object/files/entry"
-if diff -q "$__object/files/entry" "$__object/explorer/entry" >/dev/null; then
- state_is=present
+if [ -s "$__object/explorer/entry" ]; then
+ if diff -q "$__object/files/entry" "$__object/explorer/entry" >/dev/null; then
+ state_is=present
+ else
+ state_is=modified
+ fi
else
state_is=absent
fi
-state_should="$(cat "$__object/parameter/state" 2>/dev/null || echo "present")"
+state_should="$(cat "$__object/parameter/state")"
[ "$state_is" = "$state_should" ] && exit 0
@@ -58,7 +62,7 @@ state_should="$(cat "$__object/parameter/state" 2>/dev/null || echo "present")"
# These are the old markers
prefix="#cdist:__cron/$__object_id"
suffix="#/cdist:__cron/$__object_id"
-filter="^# DO NOT EDIT THIS FILE|^# \(.* installed on |^# \(Cron version V|^# \(Cronie version .\..\)$"
+filter='^# DO NOT EDIT THIS FILE|^# \(.* installed on |^# \(Cron version V|^# \(Cronie version .\..\)$'
cat << DONE
crontab -u $user -l 2>/dev/null | grep -v -E "$filter" | awk -v prefix="$prefix" -v suffix="$suffix" '
{
diff --git a/cdist/conf/type/__cron/man.rst b/cdist/conf/type/__cron/man.rst
index d0694738..e39bfb5c 100644
--- a/cdist/conf/type/__cron/man.rst
+++ b/cdist/conf/type/__cron/man.rst
@@ -21,6 +21,11 @@ command
OPTIONAL PARAMETERS
-------------------
+**NOTE**: All time-related parameters (``--minute``, ``--hour``, ``--day_of_month``
+``--month`` and ``--day_of_week``) defaults to ``*``, which means to execute it
+**always**. If you set ``--hour 0`` to execute the cronjob only at midnight, it
+will execute **every** minute in the first hour of the morning all days.
+
state
Either present or absent. Defaults to present.
minute
diff --git a/cdist/conf/type/__cron/manifest b/cdist/conf/type/__cron/manifest
index 53973e07..e7b51863 100755
--- a/cdist/conf/type/__cron/manifest
+++ b/cdist/conf/type/__cron/manifest
@@ -22,3 +22,12 @@ if [ -f "$__object/parameter/raw" ] && [ -f "$__object/parameter/raw_command" ];
echo "ERROR: both raw and raw_command specified" >&2
exit 1
fi
+
+case "$(cat "$__object/parameter/state")" in
+ present) ;;
+ absent) ;;
+
+ *)
+ echo "ERROR: unkown cron state" >&2
+ exit 2
+esac
diff --git a/cdist/conf/type/__cron/nonparallel b/cdist/conf/type/__cron/nonparallel
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__cron/parameter/default/day_of_month b/cdist/conf/type/__cron/parameter/default/day_of_month
new file mode 100644
index 00000000..72e8ffc0
--- /dev/null
+++ b/cdist/conf/type/__cron/parameter/default/day_of_month
@@ -0,0 +1 @@
+*
diff --git a/cdist/conf/type/__cron/parameter/default/day_of_week b/cdist/conf/type/__cron/parameter/default/day_of_week
new file mode 100644
index 00000000..72e8ffc0
--- /dev/null
+++ b/cdist/conf/type/__cron/parameter/default/day_of_week
@@ -0,0 +1 @@
+*
diff --git a/cdist/conf/type/__cron/parameter/default/hour b/cdist/conf/type/__cron/parameter/default/hour
new file mode 100644
index 00000000..72e8ffc0
--- /dev/null
+++ b/cdist/conf/type/__cron/parameter/default/hour
@@ -0,0 +1 @@
+*
diff --git a/cdist/conf/type/__cron/parameter/default/minute b/cdist/conf/type/__cron/parameter/default/minute
new file mode 100644
index 00000000..72e8ffc0
--- /dev/null
+++ b/cdist/conf/type/__cron/parameter/default/minute
@@ -0,0 +1 @@
+*
diff --git a/cdist/conf/type/__cron/parameter/default/month b/cdist/conf/type/__cron/parameter/default/month
new file mode 100644
index 00000000..72e8ffc0
--- /dev/null
+++ b/cdist/conf/type/__cron/parameter/default/month
@@ -0,0 +1 @@
+*
diff --git a/cdist/conf/type/__cron/parameter/default/state b/cdist/conf/type/__cron/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__cron/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__daemontools/files/init.d-svscan b/cdist/conf/type/__daemontools/files/init.d-svscan
index 127dfdb3..996eb4e8 100644
--- a/cdist/conf/type/__daemontools/files/init.d-svscan
+++ b/cdist/conf/type/__daemontools/files/init.d-svscan
@@ -23,9 +23,9 @@ fi
case "$1" in
start)
- echo -n "Starting daemontools: "
- if [ ! `pidof svscan` ]; then
- echo -n "svscan "
+ printf "Starting daemontools: "
+ if ! pidof svscan > /dev/null 2>&1; then
+ printf "svscan "
env - PATH="$PATH" svscan /service 2>&1 | setuidgid daemon multilog t /var/log/svscan &
echo "."
else
@@ -33,23 +33,28 @@ case "$1" in
fi
;;
stop)
- echo -n "Stopping daemontools: "
- if [ `pidof svscan` ]; then
- echo -n "svscan"
- while [ `pidof svscan` ]; do
- kill `pidof svscan`
- echo -n "."
+ printf "Stopping daemontools: "
+ pids="$(pidof svscan)"
+ if [ -n "${pids}" ]
+ then
+ printf "svscan"
+ while [ -n "${pids}" ]
+ do
+ # shellcheck disable=SC2086
+ kill ${pids}
+ printf "."
+ pids="$(pidof svscan)"
done
fi
- echo -n " services"
- for i in `ls -d /service/*`; do
- svc -dx $i
- echo -n "."
+ printf " services"
+ for i in /service/*; do
+ svc -dx "$i"
+ printf "."
done
- echo -n " logging "
- for i in `ls -d /service/*/log`; do
- svc -dx $i
- echo -n "."
+ printf " logging "
+ for i in /service/*/log; do
+ svc -dx "$i"
+ printf "."
done
echo ""
;;
diff --git a/cdist/conf/type/__daemontools/manifest b/cdist/conf/type/__daemontools/manifest
index 75f26ca0..b04c7e07 100755
--- a/cdist/conf/type/__daemontools/manifest
+++ b/cdist/conf/type/__daemontools/manifest
@@ -3,35 +3,38 @@
pkg=$(cat "$__object/parameter/from-package")
servicedir=$(cat "$__object/parameter/servicedir")
-__package $pkg
-__directory $servicedir --mode 755
+__package "$pkg"
+__directory "$servicedir" --mode 700
-if [ -f "$__object/parameter/install-init-script" ]; then
- os=$(cat "$__global/explorer/os")
- init=$(cat "$__global/explorer/init")
+os=$(cat "$__global/explorer/os")
+init=$(cat "$__global/explorer/init")
- case $init in
- init)
- case $os in
- freebsd)
- __config_file /etc/rc.conf.d/svscan --source - <<-EOT
- svscan_enable="YES"
- svscan_servicedir="$servicedir"
- EOT
- require="$require __package/$pkg __directory/$servicedir __config_file/etc/rc.conf.d/svscan" \
- __process svscan --start 'service svscan start'
- ;;
- *)
+require=""
+case $os in
+ freebsd)
+ # TODO change to __start_on_boot once it supports freebsd
+ __config_file /etc/rc.conf.d/svscan --source - <<-EOT
+ svscan_enable="YES"
+ svscan_servicedir="$servicedir"
+ EOT
+ require="$require __package/$pkg __directory/$servicedir __config_file/etc/rc.conf.d/svscan" \
+ __process svscan --name ".*/svscan $servicedir" --start 'service svscan start'
+ ;;
+ *)
+ case $init in
+ init)
+ if [ -f "$__object/parameter/install-init-script" ]; then
__config_file /etc/init.d/svscan --mode 755 --source "$__type/files/init.d-svscan"
- require="$require __config_file/etc/init.d/svscan" __start_on_boot svscan
- require="$require __package/$pkg __directory/$servicedir __start_on_boot/svscan" \
- __process svscan --start 'service svscan start'
- ;;
- esac
+ REQUIREEXTRA="__config_file/etc/init.d/svscan"
+ fi
+ require="$require $REQUIREEXTRA" __start_on_boot svscan
+ require="$require __package/$pkg __directory/$servicedir __start_on_boot/svscan" \
+ __process svscan --name ".*/svscan $servicedir" --start 'service svscan start'
;;
- *)
- echo "Your init system ($init) is not supported by this type. Submit a patch at github.com/ungleich/cdist!"
- exit 1
- ;;
- esac
-fi
+ *)
+ echo "Your init system ($init) is not supported by this type. Submit a patch at github.com/ungleich/cdist!"
+ exit 1
+ ;;
+ esac
+ ;;
+esac
diff --git a/cdist/conf/type/__daemontools_service/explorer/svc b/cdist/conf/type/__daemontools_service/explorer/svc
old mode 100644
new mode 100755
index d33fcea4..9ba462f2
--- a/cdist/conf/type/__daemontools_service/explorer/svc
+++ b/cdist/conf/type/__daemontools_service/explorer/svc
@@ -1 +1,2 @@
+#!/bin/sh
command -v svc || true
diff --git a/cdist/conf/type/__daemontools_service/manifest b/cdist/conf/type/__daemontools_service/manifest
index 9e8e0bee..78bae285 100755
--- a/cdist/conf/type/__daemontools_service/manifest
+++ b/cdist/conf/type/__daemontools_service/manifest
@@ -25,14 +25,14 @@ badusage() {
[ -z "$run$runfile" ] && badusage
[ -n "$run" ] && [ -n "$runfile" ] && badusage
-__directory $servicedir/$name/log/main --parents
+__directory "$servicedir/$name/log/main" --parents
echo "$RUN_PREFIX$run" | require="__directory/$servicedir/$name/log/main" __config_file "$servicedir/$name/run" \
--onchange "svc -t '$servicedir/$name' 2>/dev/null" \
--mode 755 \
--source "${runfile:--}"
-echo "$RUN_PREFIX$logrun" | require="__directory/$servicedir/$name/log/main" __config_file $servicedir/$name/log/run \
+echo "$RUN_PREFIX$logrun" | require="__directory/$servicedir/$name/log/main" __config_file "$servicedir/$name/log/run" \
--onchange "svc -t '$servicedir/$name/log' 2>/dev/null" \
--mode 755 \
--source "-"
diff --git a/cdist/conf/type/__directory/explorer/stat b/cdist/conf/type/__directory/explorer/stat
index 41bc8b04..f817cb02 100755
--- a/cdist/conf/type/__directory/explorer/stat
+++ b/cdist/conf/type/__directory/explorer/stat
@@ -1,6 +1,7 @@
#!/bin/sh
#
# 2013 Steven Armstrong (steven-cdist armstrong.cc)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -20,31 +21,51 @@
destination="/$__object_id"
+fallback() {
+ # Patch the output together, manually
+
+ ls_line=$(ls -ldn "$destination")
+
+ uid=$(echo "$ls_line" | awk '{ print $3 }')
+ gid=$(echo "$ls_line" | awk '{ print $4 }')
+
+ owner=$(awk -F: -v uid="$uid" '$3 == uid { print $1; f=1 } END { if (!f) print "UNKNOWN" }' /etc/passwd)
+ group=$(awk -F: -v gid="$gid" '$3 == gid { print $1; f=1 } END { if (!f) print "UNKNOWN" }' /etc/group)
+
+ mode_text=$(echo "$ls_line" | awk '{ print $1 }')
+ mode=$(echo "$mode_text" | awk '{for(i=8;i>=0;--i){c=substr($1,10-i,1);k+=((c~/[rwxst]/)*2^i);if(!(i%3))k+=(tolower(c)~/[lst]/)*2^(9+i/3)}printf("%04o",k)}')
+
+ printf 'type: %s\nowner: %d %s\ngroup: %d %s\nmode: %s %s\n' \
+ "$("$__type_explorer/type")" \
+ "$uid" "$owner" \
+ "$gid" "$group" \
+ "$mode" "$mode_text"
+}
+
# nothing to work with, nothing we could do
[ -e "$destination" ] || exit 0
-os=$("$__explorer/os")
-case "$os" in
- "freebsd"|"netbsd"|"openbsd")
- # FIXME: should be something like this based on man page, but can not test
- stat -f "type: %ST
+command -v stat >/dev/null 2>&1 || {
+ fallback
+ exit
+}
+
+case $("$__explorer/os")
+in
+ freebsd|netbsd|openbsd|macosx)
+ stat -f 'type: %HT
owner: %Du %Su
group: %Dg %Sg
-mode: %Op %Sp
-" "$destination"
- ;;
- "macosx")
- stat -f "type: %HT
- owner: %Du %Su
- group: %Dg %Sg
- mode: %Lp %Sp
- " "$destination"
- ;;
+mode: %Mp%03Lp %Sp
+' "$destination" | awk '/^type/ { print tolower($0); next } { print }'
+ ;;
*)
- stat --printf="type: %F
+ # NOTE: Do not use --printf here as it is not supported by BusyBox stat.
+ # NOTE: BusyBox's stat might not support the "-c" option, in which case
+ # we fall through to the shell fallback.
+ stat -c 'type: %F
owner: %u %U
group: %g %G
-mode: %a %A
-" "$destination"
- ;;
+mode: %04a %A' "$destination" 2>/dev/null || fallback
+ ;;
esac
diff --git a/cdist/conf/type/__directory/gencode-remote b/cdist/conf/type/__directory/gencode-remote
index cced4624..d9c00b56 100755
--- a/cdist/conf/type/__directory/gencode-remote
+++ b/cdist/conf/type/__directory/gencode-remote
@@ -3,6 +3,7 @@
# 2011-2013 Nico Schottelius (nico-cdist at schottelius.org)
# 2013 Steven Armstrong (steven-cdist armstrong.cc)
# 2014 Daniel Heule (hda at sfs.biz)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -21,8 +22,8 @@
#
destination="/$__object_id"
-state_should="$(cat "$__object/parameter/state")"
-type="$(cat "$__object/explorer/type")"
+state_should=$(cat "$__object/parameter/state")
+type=$(cat "$__object/explorer/type")
stat_file="$__object/explorer/stat"
# variable to keep track if we have to set directory attributes
@@ -57,32 +58,36 @@ get_current_value() {
}
set_group() {
- echo chgrp $recursive \"$1\" \"$destination\"
- echo chgrp $recursive $1 >> "$__messages_out"
+ echo "chgrp $recursive '$1' '$destination'"
+ echo "chgrp $recursive '$1'" >> "$__messages_out"
}
set_owner() {
- echo chown $recursive \"$1\" \"$destination\"
- echo chown $recursive $1 >> "$__messages_out"
+ echo "chown $recursive '$1' '$destination'"
+ echo "chown $recursive '$1'" >> "$__messages_out"
}
set_mode() {
- echo chmod $recursive \"$1\" \"$destination\"
- echo chmod $recursive $1 >> "$__messages_out"
+ echo "chmod $recursive '$1' '$destination'"
+ echo "chmod $recursive '$1'" >> "$__messages_out"
}
case "$state_should" in
- present)
+ present|exists)
if [ "$type" != "directory" ]; then
set_attributes=1
if [ "$type" != "none" ]; then
# our destination is not a directory, remove whatever is there
# and then create our directory and set all attributes
- echo rm -f "\"$destination\""
+ echo "rm -f '$destination'"
echo "remove non directory" >> "$__messages_out"
fi
- echo "mkdir $mkdiropt \"$destination\""
+ echo "mkdir $mkdiropt '$destination'"
echo "create" >> "$__messages_out"
+ elif [ "$state_should" = 'exists' ]; then
+ # The type is directory and --state exists. We are done and do not
+ # check or set the attributes.
+ exit 0
fi
# Note: Mode - needs to happen last as a chown/chgrp can alter mode by
@@ -92,9 +97,11 @@ case "$state_should" in
value_should="$(cat "$__object/parameter/$attribute")"
value_is="$(get_current_value "$attribute" "$value_should")"
- # change 0xxx format to xxx format => same as stat returns
+ # format mode in four digits => same as stat returns
if [ "$attribute" = mode ]; then
- value_should="$(echo $value_should | sed 's/^0\(...\)/\1/')"
+ # Convert to four-digit octal number (printf interprets
+ # strings with leading 0s as octal!)
+ value_should=$(printf '%04o' "0${value_should}")
fi
if [ "$set_attributes" = 1 ] || [ "$value_should" != "$value_is" ]; then
@@ -103,9 +110,29 @@ case "$state_should" in
fi
done
;;
+ pre-exists)
+ case $type in
+ directory)
+ # all good
+ exit 0
+ ;;
+ none)
+ printf 'Directory "%s" does not exist\n' "$destination" >&2
+ exit 1
+ ;;
+ file|symlink)
+ printf 'File "%s" exists and is a %s, but should be a directory\n' "$destination" "$type" >&2
+ exit 1
+ ;;
+ *)
+ printf 'File or directory "%s" is in an unknown state\n' "$destination" >&2
+ exit 1
+ ;;
+ esac
+ ;;
absent)
if [ "$type" = "directory" ]; then
- echo rm -rf \"$destination\"
+ echo "rm -rf '$destination'"
echo remove >> "$__messages_out"
fi
;;
diff --git a/cdist/conf/type/__directory/man.rst b/cdist/conf/type/__directory/man.rst
index 74b00afe..7755334c 100644
--- a/cdist/conf/type/__directory/man.rst
+++ b/cdist/conf/type/__directory/man.rst
@@ -19,7 +19,18 @@ None.
OPTIONAL PARAMETERS
-------------------
state
- 'present' or 'absent', defaults to 'present'
+ 'present', 'absent', 'exists' or 'pre-exists', defaults to 'present' where:
+
+ present
+ the directory exists and the given attributes are set.
+ absent
+ the directory does not exist.
+ exists
+ the directory exists, but its attributes are not altered if it already
+ existed.
+ pre-exists
+ check that the directory exists and is indeed a directory, but do not
+ create or modify it.
group
Group to chgrp to.
@@ -36,7 +47,7 @@ BOOLEAN PARAMETERS
parents
Whether to create parents as well (mkdir -p behaviour).
Warning: all intermediate directory permissions default
- to whatever mkdir -p does.
+ to whatever mkdir -p does.
Usually this means root:root, 0700.
diff --git a/cdist/conf/type/__docker/man.rst b/cdist/conf/type/__docker/man.rst
index 70b92cc7..718543a8 100644
--- a/cdist/conf/type/__docker/man.rst
+++ b/cdist/conf/type/__docker/man.rst
@@ -3,12 +3,12 @@ cdist-type__docker(7)
NAME
----
-cdist-type__docker - install docker-engine
+cdist-type__docker - install Docker CE
DESCRIPTION
-----------
-Installs latest docker-engine package from dockerproject.org.
+Installs latest Docker Community Edition package.
REQUIRED PARAMETERS
@@ -18,16 +18,16 @@ None.
OPTIONAL PARAMETERS
-------------------
-None.
+state
+ 'present' or 'absent', defaults to 'present'
+version
+ The specific version to install. Defaults to the special value 'latest',
+ meaning the version the package manager will install by default.
BOOLEAN PARAMETERS
------------------
-experimental
- Install the experimental docker-engine package instead of the latest stable release.
-
-state
- 'present' or 'absent', defaults to 'present'
+None.
EXAMPLES
@@ -38,12 +38,11 @@ EXAMPLES
# Install docker
__docker
- # Install experimental
- __docker --experimental
-
# Remove docker
__docker --state absent
+ # Install specific version
+ __docker --state present --version 18.03.0.ce
AUTHORS
-------
diff --git a/cdist/conf/type/__docker/manifest b/cdist/conf/type/__docker/manifest
index 1b1b1fb7..6a57d85a 100755
--- a/cdist/conf/type/__docker/manifest
+++ b/cdist/conf/type/__docker/manifest
@@ -21,58 +21,84 @@
os=$(cat "$__global/explorer/os")
state=$(cat "$__object/parameter/state")
+version=$(cat "$__object/parameter/version")
case "$os" in
centos)
- component="main"
- if [ -f "$__object/parameter/experimental" ]; then
- component="experimental"
+ # shellcheck source=/dev/null
+ if (. "$__global/explorer/os_release" && [ "${VERSION_ID}" = "7" ]); then
+ __yum_repo docker-ce-stable \
+ --name 'Docker CE Stable' \
+ --baseurl "https://download.docker.com/linux/centos/7/\$basearch/stable" \
+ --enabled \
+ --gpgcheck 1 \
+ --gpgkey 'https://download.docker.com/linux/centos/gpg' \
+ --state "${state}"
+ if [ "$version" != "latest" ]; then
+ require="__yum_repo/docker-ce-stable" __package docker-ce --version "${version}" --state "${state}"
+ else
+ require="__yum_repo/docker-ce-stable" __package docker-ce --state "${state}"
+ fi
+ else
+ echo "CentOS version 7 is required!" >&2
+ exit 1
fi
- __yum_repo docker \
- --name 'Docker Repository' \
- --baseurl "https://yum.dockerproject.org/repo/$component/centos/\$releasever/" \
- --enabled \
- --gpgcheck 1 \
- --gpgkey 'https://yum.dockerproject.org/gpg' \
- --state ${state}
- require="__yum_repo/docker" __package docker-engine --state ${state}
;;
- ubuntu)
- component="main"
- if [ -f "$__object/parameter/experimental" ]; then
- component="experimental"
- fi
- __package apparmor --state ${state}
- __package ca-certificates --state ${state}
- __package apt-transport-https --state ${state}
- __apt_key docker --keyid 58118E89F3A912897C070ADBF76221572C52609D --state ${state}
- export CDIST_ORDER_DEPENDENCY=on
- __apt_source docker \
- --uri https://apt.dockerproject.org/repo \
- --distribution "ubuntu-$(cat "$__global/explorer/lsb_codename")" \
- --state ${state} \
- --component "$component"
- __package docker-engine --state ${state}
- unset CDIST_ORDER_DEPENDENCY
- ;;
- debian)
- component="main"
- if [ -f "$__object/parameter/experimental" ]; then
- component="experimental"
+ ubuntu|debian)
+ if [ "${state}" = "present" ]; then
+ __package apt-transport-https
+ __package ca-certificates
+ __package gnupg2
fi
+ __apt_key_uri docker --name "Docker Release (CE deb) " \
+ --uri "https://download.docker.com/linux/${os}/gpg" --state "${state}"
- __package apt-transport-https --state ${state}
- __package ca-certificates --state ${state}
- __package gnupg2 --state ${state}
- __apt_key docker --keyid 58118E89F3A912897C070ADBF76221572C52609D --state ${state}
- export CDIST_ORDER_DEPENDENCY=on
- __apt_source docker \
- --uri https://apt.dockerproject.org/repo \
- --distribution "debian-$(cat "$__global/explorer/lsb_codename")" \
- --state ${state} \
- --component "$component"
- __package docker-engine --state ${state}
- unset CDIST_ORDER_DEPENDENCY
+ require="__apt_key_uri/docker" __apt_source docker \
+ --uri "https://download.docker.com/linux/${os}" \
+ --distribution "$(cat "$__global/explorer/lsb_codename")" \
+ --state "${state}" \
+ --component "stable"
+ if [ "$version" != "latest" ]; then
+ require="__apt_source/docker" __package docker-ce --version "${version}" --state "${state}"
+ else
+ require="__apt_source/docker" __package docker-ce --state "${state}"
+ fi
+ ;;
+ devuan)
+ os_version="$(cat "$__global/explorer/os_version")"
+
+ case "$os_version" in
+ ascii)
+ distribution="stretch"
+ ;;
+ jessie)
+ distribution="jessie"
+ ;;
+ *)
+ echo "Your devuan release ($os_version) is currently not supported by this type (${__type##*/}).">&2
+ echo "Please contribute an implementation for it if you can." >&2
+ exit 1
+ ;;
+ esac
+
+ if [ "${state}" = "present" ]; then
+ __package apt-transport-https
+ __package ca-certificates
+ __package gnupg2
+ fi
+ __apt_key_uri docker --name "Docker Release (CE deb) " \
+ --uri "https://download.docker.com/linux/${os}/gpg" --state "${state}"
+
+ require="__apt_key_uri/docker" __apt_source docker \
+ --uri "https://download.docker.com/linux/${os}" \
+ --distribution "${distribution}" \
+ --state "${state}" \
+ --component "stable"
+ if [ "$version" != "latest" ]; then
+ require="__apt_source/docker" __package docker-ce --version "${version}" --state "${state}"
+ else
+ require="__apt_source/docker" __package docker-ce --state "${state}"
+ fi
;;
*)
diff --git a/cdist/conf/type/__docker/parameter/boolean b/cdist/conf/type/__docker/parameter/boolean
deleted file mode 100644
index 9839eb20..00000000
--- a/cdist/conf/type/__docker/parameter/boolean
+++ /dev/null
@@ -1 +0,0 @@
-experimental
diff --git a/cdist/conf/type/__docker/parameter/default/version b/cdist/conf/type/__docker/parameter/default/version
new file mode 100644
index 00000000..a0f9a4b4
--- /dev/null
+++ b/cdist/conf/type/__docker/parameter/default/version
@@ -0,0 +1 @@
+latest
diff --git a/cdist/conf/type/__docker/parameter/optional b/cdist/conf/type/__docker/parameter/optional
index ff72b5c7..4d595ed7 100644
--- a/cdist/conf/type/__docker/parameter/optional
+++ b/cdist/conf/type/__docker/parameter/optional
@@ -1 +1,2 @@
state
+version
diff --git a/cdist/conf/type/__docker_compose/gencode-remote b/cdist/conf/type/__docker_compose/gencode-remote
index 2b8267a9..77fc2fdf 100755
--- a/cdist/conf/type/__docker_compose/gencode-remote
+++ b/cdist/conf/type/__docker_compose/gencode-remote
@@ -22,10 +22,11 @@
version="$(cat "$__object/parameter/version")"
state="$(cat "$__object/parameter/state")"
-if [ ${state} = "present" ]; then
+if [ "${state}" = "present" ]; then
# Download docker-compose file
- echo 'curl -L "https://github.com/docker/compose/releases/download/'${version}'/docker-compose-$(uname -s)-$(uname -m)" -o /tmp/docker-compose'
- echo 'mv /tmp/docker-compose /usr/local/bin/docker-compose'
+ #shellcheck disable=SC2016
+ echo 'curl -L "https://github.com/docker/compose/releases/download/'"${version}"'/docker-compose-$(uname -s)-$(uname -m)" -o /tmp/docker-compose'
+ echo 'mv /tmp/docker-compose /usr/local/bin/docker-compose'
# Change permissions
echo 'chmod +x /usr/local/bin/docker-compose'
fi
diff --git a/cdist/conf/type/__docker_compose/manifest b/cdist/conf/type/__docker_compose/manifest
index c17f0f33..f7de3a76 100755
--- a/cdist/conf/type/__docker_compose/manifest
+++ b/cdist/conf/type/__docker_compose/manifest
@@ -22,10 +22,10 @@
state="$(cat "$__object/parameter/state")"
# Needed packages
-if [ ${state} = "present" ]; then
+if [ "${state}" = "present" ]; then
__docker
__package curl
-elif [ ${state} = "absent" ]; then
+elif [ "${state}" = "absent" ]; then
__file /usr/local/bin/docker-compose --state absent
else
echo "Unknown state: ${state}" >&2
diff --git a/cdist/conf/type/__install_reboot/manifest b/cdist/conf/type/__docker_config/explorer/config-data
similarity index 78%
rename from cdist/conf/type/__install_reboot/manifest
rename to cdist/conf/type/__docker_config/explorer/config-data
index 02689d82..b4bb0e11 100755
--- a/cdist/conf/type/__install_reboot/manifest
+++ b/cdist/conf/type/__docker_config/explorer/config-data
@@ -1,6 +1,6 @@
#!/bin/sh -e
#
-# 2011 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2018 Ľubomír Kučera
#
# This file is part of cdist.
#
@@ -18,6 +18,5 @@
# along with cdist. If not, see .
#
-# set defaults
-options="$(cat "$__object/parameter/options" 2>/dev/null \
- || echo "" | tee "$__object/parameter/options")"
+docker config inspect "${__object_id:?}" --format '{{json .Spec.Data}}' \
+ 2>/dev/null | tr -d '"' | base64 -d
diff --git a/cdist/conf/type/__install_umount/manifest b/cdist/conf/type/__docker_config/explorer/config-exists
similarity index 78%
rename from cdist/conf/type/__install_umount/manifest
rename to cdist/conf/type/__docker_config/explorer/config-exists
index 42cd19bf..58c207d4 100755
--- a/cdist/conf/type/__install_umount/manifest
+++ b/cdist/conf/type/__docker_config/explorer/config-exists
@@ -1,6 +1,6 @@
#!/bin/sh -e
#
-# 2011 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2018 Ľubomír Kučera
#
# This file is part of cdist.
#
@@ -18,6 +18,8 @@
# along with cdist. If not, see .
#
-# set defaults
-target="$(cat "$__object/parameter/target" 2>/dev/null \
- || echo "/target" | tee "$__object/parameter/target")"
+if docker config ls | grep -q " ${__object_id:?} "; then
+ echo yes
+else
+ echo no
+fi
diff --git a/cdist/conf/type/__docker_config/gencode-remote b/cdist/conf/type/__docker_config/gencode-remote
new file mode 100755
index 00000000..65497b7e
--- /dev/null
+++ b/cdist/conf/type/__docker_config/gencode-remote
@@ -0,0 +1,69 @@
+#!/bin/sh -e
+#
+# 2018 Ľubomír Kučera
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+config="${__object_id:?}"
+config_exists=$(cat "${__object:?}/explorer/config-exists")
+state=$(cat "${__object:?}/parameter/state")
+
+case "${state}" in
+ absent)
+ if [ "${config_exists}" != "yes" ]; then
+ exit 0
+ fi
+
+ echo "docker config rm \"${config}\""
+ ;;
+ present)
+ source=$(cat "${__object}/parameter/source")
+
+ if [ -z "${source}" ]; then
+ exit 0
+ fi
+
+ if [ "${source}" = "-" ]; then
+ source="${__object}/stdin"
+ fi
+
+ if [ "${config_exists}" = "yes" ]; then
+ if cmp -s "${source}" "${__object}/explorer/config-data"; then
+ exit 0
+ else
+ echo "docker config rm \"${config}\""
+ fi
+ fi
+
+ cat <<-EOF
+ source_file="\$(mktemp cdist.XXXXXXXXXX)"
+
+ base64 -d > "\${source_file}" << eof
+ $(base64 "${source}")
+ eof
+
+ docker config create "${config}" "\${source_file}"
+
+ rm "\${source_file}"
+ EOF
+ ;;
+ *)
+ echo "Unsupported state: ${state}" >&2
+
+ exit 1
+ ;;
+esac
diff --git a/cdist/conf/type/__docker_config/man.rst b/cdist/conf/type/__docker_config/man.rst
new file mode 100644
index 00000000..7c74c8af
--- /dev/null
+++ b/cdist/conf/type/__docker_config/man.rst
@@ -0,0 +1,55 @@
+cdist-type__docker_config(7)
+============================
+
+NAME
+----
+
+cdist-type__docker_config - Manage Docker configs
+
+DESCRIPTION
+-----------
+
+This type manages Docker configs.
+
+OPTIONAL PARAMETERS
+-------------------
+
+source
+ Path to the source file. If it is '-' (dash), read standard input.
+
+state
+ 'present' or 'absent', defaults to 'present' where:
+
+ present
+ if the config does not exist, it is created
+ absent
+ the config is removed
+
+CAVEATS
+-------
+
+Since Docker configs cannot be updated once created, this type tries removing
+and recreating the config if it changes. If the config is used by a service at
+the time of removing, then this type will fail.
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # Creates "foo" config from "bar" source file
+ __docker_config foo --source bar
+
+
+AUTHORS
+-------
+
+Ľubomír Kučera
+
+COPYING
+-------
+
+Copyright \(C) 2018 Ľubomír Kučera. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__docker_config/parameter/default/source b/cdist/conf/type/__docker_config/parameter/default/source
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__docker_config/parameter/default/state b/cdist/conf/type/__docker_config/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__docker_config/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__docker_config/parameter/optional b/cdist/conf/type/__docker_config/parameter/optional
new file mode 100644
index 00000000..d77f3048
--- /dev/null
+++ b/cdist/conf/type/__docker_config/parameter/optional
@@ -0,0 +1,2 @@
+source
+state
diff --git a/cdist/conf/type/__docker_secret/explorer/secret-exists b/cdist/conf/type/__docker_secret/explorer/secret-exists
new file mode 100755
index 00000000..1405f8bc
--- /dev/null
+++ b/cdist/conf/type/__docker_secret/explorer/secret-exists
@@ -0,0 +1,25 @@
+#!/bin/sh -e
+#
+# 2018 Ľubomír Kučera
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+if docker secret ls | grep -q " ${__object_id:?} "; then
+ echo yes
+else
+ echo no
+fi
diff --git a/cdist/conf/type/__docker_secret/gencode-remote b/cdist/conf/type/__docker_secret/gencode-remote
new file mode 100755
index 00000000..c75e91d9
--- /dev/null
+++ b/cdist/conf/type/__docker_secret/gencode-remote
@@ -0,0 +1,65 @@
+#!/bin/sh -e
+#
+# 2018 Ľubomír Kučera
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+secret="${__object_id:?}"
+secret_exists=$(cat "${__object:?}/explorer/secret-exists")
+state=$(cat "${__object:?}/parameter/state")
+
+case "${state}" in
+ absent)
+ if [ "${secret_exists}" != "yes" ]; then
+ exit 0
+ fi
+
+ echo "docker secret rm ${secret}"
+ ;;
+ present)
+ if [ "${secret_exists}" = "yes" ]; then
+ exit 0
+ fi
+
+ source=$(cat "${__object}/parameter/source")
+
+ if [ -z "${source}" ]; then
+ exit 0
+ fi
+
+ if [ "${source}" = "-" ]; then
+ source="${__object}/stdin"
+ fi
+
+ cat <<-EOF
+ source_file="\$(mktemp cdist.XXXXXXXXXX)"
+
+ base64 -d > "\${source_file}" << eof
+ $(base64 "${source}")
+ eof
+
+ docker secret create "${secret}" "\${source_file}"
+
+ rm "\${source_file}"
+ EOF
+ ;;
+ *)
+ echo "Unsupported state: ${state}" >&2
+
+ exit 1
+ ;;
+esac
diff --git a/cdist/conf/type/__docker_secret/man.rst b/cdist/conf/type/__docker_secret/man.rst
new file mode 100644
index 00000000..7fe69623
--- /dev/null
+++ b/cdist/conf/type/__docker_secret/man.rst
@@ -0,0 +1,54 @@
+cdist-type__docker_secret(7)
+============================
+
+NAME
+----
+
+cdist-type__docker_secret - Manage Docker secrets
+
+DESCRIPTION
+-----------
+
+This type manages Docker secrets.
+
+OPTIONAL PARAMETERS
+-------------------
+
+source
+ Path to the source file. If it is '-' (dash), read standard input.
+
+state
+ 'present' or 'absent', defaults to 'present' where:
+
+ present
+ if the secret does not exist, it is created
+ absent
+ the secret is removed
+
+CAVEATS
+-------
+
+Since Docker secrets cannot be updated once created, this type takes no action
+if the specified secret already exists.
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # Creates "foo" secret from "bar" source file
+ __docker_secret foo --source bar
+
+
+AUTHORS
+-------
+
+Ľubomír Kučera
+
+COPYING
+-------
+
+Copyright \(C) 2018 Ľubomír Kučera. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__docker_secret/parameter/default/source b/cdist/conf/type/__docker_secret/parameter/default/source
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__docker_secret/parameter/default/state b/cdist/conf/type/__docker_secret/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__docker_secret/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__docker_secret/parameter/optional b/cdist/conf/type/__docker_secret/parameter/optional
new file mode 100644
index 00000000..d77f3048
--- /dev/null
+++ b/cdist/conf/type/__docker_secret/parameter/optional
@@ -0,0 +1,2 @@
+source
+state
diff --git a/cdist/conf/type/__docker_stack/explorer/stack-exists b/cdist/conf/type/__docker_stack/explorer/stack-exists
new file mode 100755
index 00000000..4f511821
--- /dev/null
+++ b/cdist/conf/type/__docker_stack/explorer/stack-exists
@@ -0,0 +1,25 @@
+#!/bin/sh -e
+#
+# 2018 Ľubomír Kučera
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+if docker stack ls | grep -q "^${__object_id:?} "; then
+ echo 1
+else
+ echo 0
+fi
diff --git a/cdist/conf/type/__docker_stack/gencode-remote b/cdist/conf/type/__docker_stack/gencode-remote
new file mode 100755
index 00000000..586271d0
--- /dev/null
+++ b/cdist/conf/type/__docker_stack/gencode-remote
@@ -0,0 +1,63 @@
+#!/bin/sh -e
+#
+# 2018 Ľubomír Kučera
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+stack="${__object_id:?}"
+state=$(cat "${__object:?}/parameter/state")
+
+case "${state}" in
+ absent)
+ stack_exists=$(cat "${__object:?}/explorer/stack-exists")
+
+ if [ "${stack_exists}" -ne 1 ]; then
+ exit 0
+ fi
+
+ echo "docker stack rm ${stack}"
+ ;;
+ present)
+ compose_file=$(cat "${__object}/parameter/compose-file")
+
+ if [ -z "${compose_file}" ]; then
+ exit 0
+ fi
+
+ if [ "${compose_file}" = "-" ]; then
+ compose_file="${__object}/stdin"
+ fi
+
+ cat <<-EOF
+ compose_file="\$(mktemp cdist.XXXXXXXXXX)"
+
+ base64 -d > "\${compose_file}" << eof
+ $(base64 "${compose_file}")
+ eof
+
+ docker stack deploy --compose-file "\${compose_file}" \
+ --prune --with-registry-auth ${stack}
+
+ rm "\${compose_file}"
+ EOF
+ ;;
+ *)
+ echo "Unsupported state: ${state}" >&2
+
+ exit 1
+ ;;
+esac
diff --git a/cdist/conf/type/__docker_stack/man.rst b/cdist/conf/type/__docker_stack/man.rst
new file mode 100644
index 00000000..d0597c25
--- /dev/null
+++ b/cdist/conf/type/__docker_stack/man.rst
@@ -0,0 +1,54 @@
+cdist-type__docker_stack(7)
+===========================
+
+NAME
+----
+
+cdist-type__docker_stack - Manage Docker stacks
+
+DESCRIPTION
+-----------
+
+This type manages service stacks.
+
+.. note::
+ Since there is no easy way to tell whether a stack needs to be updated,
+ `docker stack deploy` is being run every time this type is invoked.
+ However, it does not mean this type is not idempotent. If Docker does not
+ detect changes, the existing stack will not be updated.
+
+OPTIONAL PARAMETERS
+-------------------
+
+compose-file
+ Path to the compose file. If it is '-' (dash), read standard input.
+
+state
+ 'present' or 'absent', defaults to 'present' where:
+
+ present
+ the stack is deployed
+ absent
+ the stack is removed
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # Deploys 'foo' stack defined in 'docker-compose.yml' compose file
+ __docker_stack foo --compose-file docker-compose.yml
+
+
+AUTHORS
+-------
+
+Ľubomír Kučera
+
+COPYING
+-------
+
+Copyright \(C) 2018 Ľubomír Kučera. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__docker_stack/parameter/default/compose-file b/cdist/conf/type/__docker_stack/parameter/default/compose-file
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__docker_stack/parameter/default/state b/cdist/conf/type/__docker_stack/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__docker_stack/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__docker_stack/parameter/optional b/cdist/conf/type/__docker_stack/parameter/optional
new file mode 100644
index 00000000..b3457bd3
--- /dev/null
+++ b/cdist/conf/type/__docker_stack/parameter/optional
@@ -0,0 +1,2 @@
+compose-file
+state
diff --git a/cdist/conf/type/__docker_swarm/explorer/swarm-state b/cdist/conf/type/__docker_swarm/explorer/swarm-state
new file mode 100755
index 00000000..2c9fd598
--- /dev/null
+++ b/cdist/conf/type/__docker_swarm/explorer/swarm-state
@@ -0,0 +1,21 @@
+#!/bin/sh -e
+#
+# 2018 Ľubomír Kučera
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+docker info 2>/dev/null | grep '^ *Swarm: ' | awk '{print $2}'
diff --git a/cdist/conf/type/__docker_swarm/gencode-remote b/cdist/conf/type/__docker_swarm/gencode-remote
new file mode 100755
index 00000000..4b199a02
--- /dev/null
+++ b/cdist/conf/type/__docker_swarm/gencode-remote
@@ -0,0 +1,46 @@
+#!/bin/sh -e
+#
+# 2018 Ľubomír Kučera
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+state=$(cat "${__object:?}/parameter/state")
+swarm_state="$(cat "${__object}/explorer/swarm-state")"
+
+if [ -z "${swarm_state}" ]; then
+ echo "Unable to determine Swarm state. Is compatible version of Docker installed?" >&2
+
+ exit 1
+fi
+
+case "${state}" in
+ absent)
+ if [ "${swarm_state}" = "active" ]; then
+ echo "docker swarm leave --force"
+ fi
+ ;;
+ present)
+ if [ "${swarm_state}" = "inactive" ]; then
+ echo "docker swarm init"
+ fi
+ ;;
+ *)
+ echo "Unsupported state: ${state}" >&2
+
+ exit 1
+ ;;
+esac
diff --git a/cdist/conf/type/__docker_swarm/man.rst b/cdist/conf/type/__docker_swarm/man.rst
new file mode 100644
index 00000000..4dc408f0
--- /dev/null
+++ b/cdist/conf/type/__docker_swarm/man.rst
@@ -0,0 +1,49 @@
+cdist-type__docker_swarm(7)
+===========================
+
+NAME
+----
+
+cdist-type__docker_swarm - Manage Swarm
+
+DESCRIPTION
+-----------
+
+This type can initialize Docker swarm mode. For more information about swarm
+mode, see `Swarm mode overview `_.
+
+OPTIONAL PARAMETERS
+-------------------
+
+state
+ 'present' or 'absent', defaults to 'present' where:
+
+ present
+ Swarm is initialized
+ absent
+ Swarm is left
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # Initializes a swarm
+ __docker_swarm
+
+ # Leaves a swarm
+ __docker_swarm --state absent
+
+
+AUTHORS
+-------
+
+Ľubomír Kučera
+
+COPYING
+-------
+
+Copyright \(C) 2018 Ľubomír Kučera. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__docker_swarm/parameter/default/state b/cdist/conf/type/__docker_swarm/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__docker_swarm/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__docker_swarm/parameter/optional b/cdist/conf/type/__docker_swarm/parameter/optional
new file mode 100644
index 00000000..ff72b5c7
--- /dev/null
+++ b/cdist/conf/type/__docker_swarm/parameter/optional
@@ -0,0 +1 @@
+state
diff --git a/cdist/conf/type/__docker_swarm/singleton b/cdist/conf/type/__docker_swarm/singleton
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__dot_file/explorer/home b/cdist/conf/type/__dot_file/explorer/home
index 132cfc71..08d941bf 100755
--- a/cdist/conf/type/__dot_file/explorer/home
+++ b/cdist/conf/type/__dot_file/explorer/home
@@ -19,7 +19,7 @@ set -eu
user="$(cat "${__object}/parameter/user")"
-if which getent >/dev/null 2>&1; then
+if command -v getent >/dev/null 2>&1; then
line=$(getent passwd "${user}")
else
line=$(grep "^${user}:" /etc/passwd)
diff --git a/cdist/conf/type/__dot_file/man.rst b/cdist/conf/type/__dot_file/man.rst
index ae65eb95..ba7621a1 100644
--- a/cdist/conf/type/__dot_file/man.rst
+++ b/cdist/conf/type/__dot_file/man.rst
@@ -25,6 +25,9 @@ user
OPTIONAL PARAMETERS
-------------------
+dirmode
+ forwarded to :strong:`__directory` type as mode
+
mode
forwarded to :strong:`__file` type
diff --git a/cdist/conf/type/__dot_file/manifest b/cdist/conf/type/__dot_file/manifest
index 5e4957e5..02dadf05 100755
--- a/cdist/conf/type/__dot_file/manifest
+++ b/cdist/conf/type/__dot_file/manifest
@@ -19,6 +19,7 @@ set -eu
user="$(cat "${__object}/parameter/user")"
home="$(cat "${__object}/explorer/home")"
primary_group="$(cat "${__object}/explorer/primary_group")"
+dirmode="$(cat "${__object}/parameter/dirmode")"
# Create parent directory. Type __directory has flag 'parents', but it
# will leave us with root-owned directory in user home, which is not
@@ -36,6 +37,7 @@ export CDIST_ORDER_DEPENDENCY
for dir ; do
__directory "${home}/${dir}" \
--group "${primary_group}" \
+ --mode "${dirmode}" \
--owner "${user}"
done
diff --git a/cdist/conf/type/__dot_file/parameter/default/dirmode b/cdist/conf/type/__dot_file/parameter/default/dirmode
new file mode 100644
index 00000000..e9745d1f
--- /dev/null
+++ b/cdist/conf/type/__dot_file/parameter/default/dirmode
@@ -0,0 +1 @@
+0700
diff --git a/cdist/conf/type/__dot_file/parameter/optional b/cdist/conf/type/__dot_file/parameter/optional
index ccab9fa6..9f7f83fb 100644
--- a/cdist/conf/type/__dot_file/parameter/optional
+++ b/cdist/conf/type/__dot_file/parameter/optional
@@ -1,3 +1,4 @@
state
mode
source
+dirmode
diff --git a/cdist/conf/type/__download/explorer/remote_cmd b/cdist/conf/type/__download/explorer/remote_cmd
new file mode 100755
index 00000000..e3e35b45
--- /dev/null
+++ b/cdist/conf/type/__download/explorer/remote_cmd
@@ -0,0 +1,19 @@
+#!/bin/sh -e
+
+if [ -f "$__object/parameter/cmd-get" ]
+then
+ cmd="$( cat "$__object/parameter/cmd-get" )"
+
+elif command -v curl > /dev/null
+then
+ cmd="curl -L -o - '%s'"
+
+elif command -v fetch > /dev/null
+then
+ cmd="fetch -o - '%s'"
+
+else
+ cmd="wget -O - '%s'"
+fi
+
+echo "$cmd"
diff --git a/cdist/conf/type/__download/explorer/state b/cdist/conf/type/__download/explorer/state
new file mode 100755
index 00000000..00362545
--- /dev/null
+++ b/cdist/conf/type/__download/explorer/state
@@ -0,0 +1,72 @@
+#!/bin/sh -e
+
+dst="/$__object_id"
+
+if [ ! -f "$dst" ]
+then
+ echo 'absent'
+ exit 0
+fi
+
+sum_should="$( cat "$__object/parameter/sum" )"
+
+if [ -f "$__object/parameter/cmd-sum" ]
+then
+ # shellcheck disable=SC2059
+ sum_is="$( eval "$( printf \
+ "$( cat "$__object/parameter/cmd-sum" )" \
+ "$dst" )" )"
+else
+ os="$( "$__explorer/os" )"
+
+ if echo "$sum_should" | grep -Eq '^[0-9]+\s[0-9]+$'
+ then
+ sum_is="$( cksum "$dst" | awk '{print $1" "$2}' )"
+
+ elif echo "$sum_should" | grep -Eiq '^md5:[a-f0-9]{32}$'
+ then
+ case "$os" in
+ freebsd)
+ sum_is="md5:$( md5 -q "$dst" )"
+ ;;
+ *)
+ sum_is="md5:$( md5sum "$dst" | awk '{print $1}' )"
+ ;;
+ esac
+
+ elif echo "$sum_should" | grep -Eiq '^sha1:[a-f0-9]{40}$'
+ then
+ case "$os" in
+ freebsd)
+ sum_is="sha1:$( sha1 -q "$dst" )"
+ ;;
+ *)
+ sum_is="sha1:$( sha1sum "$dst" | awk '{print $1}' )"
+ ;;
+ esac
+
+ elif echo "$sum_should" | grep -Eiq '^sha256:[a-f0-9]{64}$'
+ then
+ case "$os" in
+ freebsd)
+ sum_is="sha256:$( sha256 -q "$dst" )"
+ ;;
+ *)
+ sum_is="sha256:$( sha256sum "$dst" | awk '{print $1}' )"
+ ;;
+ esac
+ fi
+fi
+
+if [ -z "$sum_is" ]
+then
+ echo 'no checksum from target' >&2
+ exit 1
+fi
+
+if [ "$sum_is" = "$sum_should" ]
+then
+ echo 'present'
+else
+ echo 'mismatch'
+fi
diff --git a/cdist/conf/type/__download/gencode-local b/cdist/conf/type/__download/gencode-local
new file mode 100755
index 00000000..571d2c3c
--- /dev/null
+++ b/cdist/conf/type/__download/gencode-local
@@ -0,0 +1,58 @@
+#!/bin/sh -e
+
+download="$( cat "$__object/parameter/download" )"
+
+state_is="$( cat "$__object/explorer/state" )"
+
+if [ "$download" != 'local' ] || [ "$state_is" = 'present' ]
+then
+ exit 0
+fi
+
+url="$( cat "$__object/parameter/url" )"
+
+tmp="$( mktemp )"
+
+dst="/$__object_id"
+
+if [ -f "$__object/parameter/cmd-get" ]
+then
+ cmd="$( cat "$__object/parameter/cmd-get" )"
+
+elif command -v wget > /dev/null
+then
+ cmd="wget -O - '%s'"
+
+elif command -v curl > /dev/null
+then
+ cmd="curl -L -o - '%s'"
+
+elif command -v fetch > /dev/null
+then
+ cmd="fetch -o - '%s'"
+
+else
+ echo 'no usable locally installed utility for downloading' >&2
+ exit 1
+fi
+
+printf "$cmd > %s\n" \
+ "$url" \
+ "$tmp"
+
+if echo "$__target_host" | grep -Eq '^[0-9a-fA-F:]+$'
+then
+ target_host="[$__target_host]"
+else
+ target_host="$__target_host"
+fi
+
+printf '%s %s %s:%s\n' \
+ "$__remote_copy" \
+ "$tmp" \
+ "$target_host" \
+ "$dst"
+
+echo "rm -f '$tmp'"
+
+echo 'downloaded' > "$__messages_out"
diff --git a/cdist/conf/type/__download/gencode-remote b/cdist/conf/type/__download/gencode-remote
new file mode 100755
index 00000000..029a0801
--- /dev/null
+++ b/cdist/conf/type/__download/gencode-remote
@@ -0,0 +1,25 @@
+#!/bin/sh -e
+
+download="$( cat "$__object/parameter/download" )"
+
+state_is="$( cat "$__object/explorer/state" )"
+
+if [ "$download" = 'remote' ] && [ "$state_is" != 'present' ]
+then
+ cmd="$( cat "$__object/explorer/remote_cmd" )"
+
+ url="$( cat "$__object/parameter/url" )"
+
+ dst="/$__object_id"
+
+ printf "$cmd > %s\n" \
+ "$url" \
+ "$dst"
+
+ echo 'downloaded' > "$__messages_out"
+fi
+
+if [ -f "$__object/parameter/onchange" ] && [ "$state_is" != "present" ]
+then
+ cat "$__object/parameter/onchange"
+fi
diff --git a/cdist/conf/type/__download/man.rst b/cdist/conf/type/__download/man.rst
new file mode 100644
index 00000000..54503470
--- /dev/null
+++ b/cdist/conf/type/__download/man.rst
@@ -0,0 +1,87 @@
+cdist-type__download(7)
+=======================
+
+NAME
+----
+cdist-type__download - Download a file
+
+
+DESCRIPTION
+-----------
+Destination (``$__object_id``) in target host must be persistent storage
+in order to calculate checksum and decide if file must be (re-)downloaded.
+
+By default type will try to use ``wget``, ``curl`` or ``fetch``.
+If download happens in target (see ``--download``) then type will
+fallback to (and install) ``wget``.
+
+If download happens in local machine, then environment variables like
+``{http,https,ftp}_proxy`` etc can be used on cdist execution
+(``http_proxy=foo cdist config ...``).
+
+
+REQUIRED PARAMETERS
+-------------------
+url
+ File's URL.
+
+sum
+ Checksum of file going to be downloaded.
+ By default output of ``cksum`` without filename is expected.
+ Other hash formats supported with prefixes: ``md5:``, ``sha1:`` and ``sha256:``.
+
+
+OPTIONAL PARAMETERS
+-------------------
+download
+ If ``local`` (default), then download file to local storage and copy
+ it to target host. If ``remote``, then download happens in target.
+
+cmd-get
+ Command used for downloading.
+ Command must output to ``stdout``.
+ Parameter will be used for ``printf`` and must include only one
+ format specification ``%s`` which will become URL.
+ For example: ``wget -O - '%s'``.
+
+cmd-sum
+ Command used for checksum calculation.
+ Command output and ``--sum`` parameter must match.
+ Parameter will be used for ``printf`` and must include only one
+ format specification ``%s`` which will become destination.
+ For example: ``md5sum '%s' | awk '{print $1}'``.
+
+onchange
+ Execute this command after download.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ __directory /opt/cpma
+
+ require='__directory/opt/cpma' \
+ __download /opt/cpma/cnq3.zip \
+ --url https://cdn.playmorepromode.com/files/cnq3/cnq3-1.51.zip \
+ --sum md5:46da3021ca9eace277115ec9106c5b46
+
+ require='__download/opt/cpma/cnq3.zip' \
+ __unpack /opt/cpma/cnq3.zip \
+ --backup-destination \
+ --preserve-archive \
+ --destination /opt/cpma/server
+
+
+AUTHORS
+-------
+Ander Punnar
+
+
+COPYING
+-------
+Copyright \(C) 2020 Ander Punnar. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__download/manifest b/cdist/conf/type/__download/manifest
new file mode 100755
index 00000000..7ec8d86d
--- /dev/null
+++ b/cdist/conf/type/__download/manifest
@@ -0,0 +1,6 @@
+#!/bin/sh -e
+
+if grep -Eq '^wget' "$__object/explorer/remote_cmd"
+then
+ __package wget
+fi
diff --git a/cdist/conf/type/__download/parameter/default/download b/cdist/conf/type/__download/parameter/default/download
new file mode 100644
index 00000000..40830374
--- /dev/null
+++ b/cdist/conf/type/__download/parameter/default/download
@@ -0,0 +1 @@
+local
diff --git a/cdist/conf/type/__download/parameter/optional b/cdist/conf/type/__download/parameter/optional
new file mode 100644
index 00000000..838e2fbf
--- /dev/null
+++ b/cdist/conf/type/__download/parameter/optional
@@ -0,0 +1,4 @@
+cmd-get
+cmd-sum
+download
+onchange
diff --git a/cdist/conf/type/__download/parameter/required b/cdist/conf/type/__download/parameter/required
new file mode 100644
index 00000000..6ea4c38f
--- /dev/null
+++ b/cdist/conf/type/__download/parameter/required
@@ -0,0 +1,2 @@
+url
+sum
diff --git a/cdist/conf/type/__dpkg_architecture/explorer/architecture b/cdist/conf/type/__dpkg_architecture/explorer/architecture
new file mode 100755
index 00000000..03e7e386
--- /dev/null
+++ b/cdist/conf/type/__dpkg_architecture/explorer/architecture
@@ -0,0 +1,26 @@
+#!/bin/sh -e
+# __dpkg_architecture/explorer/architecture
+#
+# 2020 Matthias Stecher
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+# Get the main architecture of this machine
+
+
+# print or die in the gencode-remote
+dpkg --print-architecture || true
diff --git a/cdist/conf/type/__dpkg_architecture/explorer/foreign-architectures b/cdist/conf/type/__dpkg_architecture/explorer/foreign-architectures
new file mode 100755
index 00000000..a150d307
--- /dev/null
+++ b/cdist/conf/type/__dpkg_architecture/explorer/foreign-architectures
@@ -0,0 +1,26 @@
+#!/bin/sh -e
+# __dpkg_architecture/explorer/foreign-architectures
+#
+# 2020 Matthias Stecher
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+# Print all additional architectures
+
+
+# print or die in the gencode-remote
+dpkg --print-foreign-architectures || true
diff --git a/cdist/conf/type/__dpkg_architecture/gencode-remote b/cdist/conf/type/__dpkg_architecture/gencode-remote
new file mode 100755
index 00000000..47fb24e7
--- /dev/null
+++ b/cdist/conf/type/__dpkg_architecture/gencode-remote
@@ -0,0 +1,82 @@
+#!/bin/sh -e
+# __dpkg_architecture/gencode-remote
+#
+# 2020 Matthias Stecher
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+
+# Get parameter and explorer
+state_should="$(cat "$__object/parameter/state")"
+arch_wanted="$__object_id"
+main_arch="$(cat "$__object/explorer/architecture")"
+
+# Exit here if dpkg do not work (empty explorer)
+if [ -z "$main_arch" ]; then
+ echo "dpkg is not available or unable to detect a architecture!" >&2
+ exit 1
+fi
+
+
+# Check if requested architecture is the main one
+if [ "$arch_wanted" = "$main_arch" ]; then
+ # higher than present; we can not remove it
+ state_is="present"
+ caution="yes"
+
+# Check if the architecture not already used
+elif grep -qFx "$arch_wanted" "$__object/explorer/foreign-architectures"; then
+ state_is="present"
+
+# arch does not exist
+else
+ state_is="absent"
+fi
+
+
+# Check what to do
+if [ "$state_is" != "$state_should" ]; then
+ case "$state_should" in
+ present)
+ # print add code
+ printf "dpkg --add-architecture '%s'\n" "$arch_wanted"
+ # updating the index to make the new architecture available
+ echo "apt update"
+
+ echo added >> "$__messages_out"
+ ;;
+
+ absent)
+ if [ "$caution" ]; then
+ printf "can not remove the main arch '%s' of the system!\n" "$main_arch" >&2
+ exit 1
+ fi
+
+ # removing all existing packages for the architecture
+ printf "apt purge '.*:%s'\n" "$arch_wanted"
+ # print remove code
+ printf "dpkg --remove-architecture '%s'\n" "$arch_wanted"
+
+ echo removed >> "$__messages_out"
+ ;;
+
+ *)
+ printf "state '%s' is unknown!\n" "$state_should" >&2
+ exit 1
+ ;;
+ esac
+fi
diff --git a/cdist/conf/type/__dpkg_architecture/man.rst b/cdist/conf/type/__dpkg_architecture/man.rst
new file mode 100644
index 00000000..fa196229
--- /dev/null
+++ b/cdist/conf/type/__dpkg_architecture/man.rst
@@ -0,0 +1,103 @@
+cdist-type__dpkg_architecture(7)
+================================
+
+NAME
+----
+cdist-type__dpkg_architecture - Handles foreign architectures on debian-like
+systems managed by `dpkg`
+
+
+DESCRIPTION
+-----------
+This type handles foreign architectures on systems managed by
+:strong:`dpkg`\ (1). The object id is the name of the architecture accepted by
+`dpkg`, which should be added or removed.
+
+If the architecture is not setup on the system, it adds a new architecture as a
+new foreign architecture in `dpkg`. Then, it updates the apt package index to
+make packages from the new architecture available.
+
+If the architecture should be removed, it will remove it if it is not the base
+architecture on where the system was installed on. Before it, it will purge
+every package based on the "to be removed" architecture via `apt` to be able to
+remove the selected architecture.
+
+
+REQUIRED PARAMETERS
+-------------------
+None.
+
+
+OPTIONAL PARAMETERS
+-------------------
+state
+ ``present`` or ``absent``. Defaults to ``present``.
+
+
+MESSAGES
+--------
+added
+ Added the specified architecture
+
+removed
+ Removed the specified architecture
+
+
+ABORTS
+------
+Aborts in the following cases:
+
+If :strong:`dpkg`\ (1) is not available. It will abort with a proper error
+message.
+
+If the architecture is the same as the base architecture the system is build
+upon it (returned by ``dpkg --print-architecture``) and it should be removed.
+
+It will fail if it can not execute :strong:`apt`\ (8). It is assumed that it is
+already installed.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # add i386 (32 bit) architecture
+ __dpkg_architecture i386
+
+ # remove it again :)
+ __dpkg_architecture i386 --state absent
+
+
+SEE ALSO
+--------
+`Multiarch on Debian systems `_
+
+`How to setup multiarch on Debian `_
+
+:strong:`dpkg`\ (1)
+:strong:`cdist-type__package_dpkg`\ (7)
+:strong:`cdist-type__package_apt`\ (7)
+
+Useful commands:
+
+.. code-block:: sh
+
+ # base architecture installed on this system
+ dpkg --print-architecture
+
+ # extra architectures added
+ dpkg --print-foreign-architectures
+
+
+AUTHORS
+-------
+Matthias Stecher
+
+
+COPYING
+-------
+Copyright \(C) 2020 Matthias Stecher. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+ublished by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__dpkg_architecture/nonparallel b/cdist/conf/type/__dpkg_architecture/nonparallel
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__dpkg_architecture/parameter/default/state b/cdist/conf/type/__dpkg_architecture/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__dpkg_architecture/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__dpkg_architecture/parameter/optional b/cdist/conf/type/__dpkg_architecture/parameter/optional
new file mode 100644
index 00000000..ff72b5c7
--- /dev/null
+++ b/cdist/conf/type/__dpkg_architecture/parameter/optional
@@ -0,0 +1 @@
+state
diff --git a/cdist/conf/type/__file/explorer/stat b/cdist/conf/type/__file/explorer/stat
index 8a917556..29b3c8a3 100755
--- a/cdist/conf/type/__file/explorer/stat
+++ b/cdist/conf/type/__file/explorer/stat
@@ -1,6 +1,8 @@
#!/bin/sh
#
# 2013 Steven Armstrong (steven-cdist armstrong.cc)
+# 2019 Nico Schottelius (nico-cdist at schottelius.org)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -20,37 +22,63 @@
destination="/$__object_id"
+fallback() {
+ # Fallback: Patch the output together, manually.
+
+ ls_line=$(ls -ldn "$destination")
+
+ uid=$(echo "$ls_line" | awk '{ print $3 }')
+ gid=$(echo "$ls_line" | awk '{ print $4 }')
+
+ owner=$(awk -F: -v uid="$uid" '$3 == uid { print $1; f=1 } END { if (!f) print "UNKNOWN" }' /etc/passwd)
+ group=$(awk -F: -v gid="$gid" '$3 == gid { print $1; f=1 } END { if (!f) print "UNKNOWN" }' /etc/group)
+
+ mode_text=$(echo "$ls_line" | awk '{ print $1 }')
+ mode=$(echo "$mode_text" | awk '{for(i=8;i>=0;--i){c=substr($1,10-i,1);k+=((c~/[rwxst]/)*2^i);if(!(i%3))k+=(tolower(c)~/[lst]/)*2^(9+i/3)}printf("%04o",k)}')
+
+ size=$(echo "$ls_line" | awk '{ print $5 }')
+ links=$(echo "$ls_line" | awk '{ print $2 }')
+
+ printf 'type: %s\nowner: %d %s\ngroup: %d %s\nmode: %s %s\nsize: %d\nlinks: %d\n' \
+ "$("$__type_explorer/type")" \
+ "$uid" "$owner" \
+ "$gid" "$group" \
+ "$mode" "$mode_text" \
+ "$size" \
+ "$links"
+}
+
+
# nothing to work with, nothing we could do
[ -e "$destination" ] || exit 0
-os=$("$__explorer/os")
-case "$os" in
- "freebsd"|"netbsd"|"openbsd")
- # FIXME: should be something like this based on man page, but can not test
- stat -f "type: %ST
+
+command -v stat >/dev/null 2>&1 || {
+ fallback
+ exit
+}
+
+
+case $("$__explorer/os")
+in
+ freebsd|netbsd|openbsd|macosx)
+ stat -f 'type: %HT
owner: %Du %Su
group: %Dg %Sg
-mode: %Op %Sp
+mode: %Mp%03Lp %Sp
size: %Dz
links: %Dl
-" "$destination"
- ;;
- "macosx")
- stat -f "type: %HT
-owner: %Du %Su
-group: %Dg %Sg
-mode: %Lp %Sp
-size: %Dz
-links: %Dl
-" "$destination"
- ;;
+' "$destination" | awk '/^type/ { print tolower($0); next } { print }'
+ ;;
*)
- stat --printf="type: %F
+ # NOTE: Do not use --printf here as it is not supported by BusyBox stat.
+ # NOTE: BusyBox's stat might not support the "-c" option, in which case
+ # we fall through to the shell fallback.
+ stat -c 'type: %F
owner: %u %U
group: %g %G
-mode: %a %A
+mode: %04a %A
size: %s
-links: %h
-" "$destination"
- ;;
+links: %h' "$destination" 2>/dev/null || fallback
+ ;;
esac
diff --git a/cdist/conf/type/__file/gencode-local b/cdist/conf/type/__file/gencode-local
index aec4e43e..231b6927 100755
--- a/cdist/conf/type/__file/gencode-local
+++ b/cdist/conf/type/__file/gencode-local
@@ -23,11 +23,37 @@ destination="/$__object_id"
state_should="$(cat "$__object/parameter/state")"
type="$(cat "$__object/explorer/type")"
-[ "$state_should" = "exists" -a "$type" = "file" ] && exit 0 # nothing to do
+[ "$state_should" = "exists" ] && [ "$type" = "file" ] && exit 0 # nothing to do
+
+if [ "$state_should" = "pre-exists" ]; then
+ if [ -f "$__object/parameter/source" ]; then
+ echo "--source cannot be used with --state pre-exists"
+ exit 1
+ fi
+
+ case $type in
+ file)
+ # nothing to do
+ exit 0
+ ;;
+ none)
+ printf 'File "%s" does not exist\n' "$destination" >&2
+ exit 1
+ ;;
+ directory|symlink)
+ printf 'File "%s" exists and is a %s, but should be a regular file\n' "$destination" "$type" >&2
+ exit 1
+ ;;
+ *)
+ printf 'File or directory "%s" is in an unknown state\n' "$destination" >&2
+ exit 1
+ ;;
+ esac
+fi
upload_file=
create_file=
-if [ "$state_should" = "present" -o "$state_should" = "exists" ]; then
+if [ "$state_should" = "present" ] || [ "$state_should" = "exists" ]; then
if [ ! -f "$__object/parameter/source" ]; then
remote_stat="$(cat "$__object/explorer/stat")"
if [ -z "$remote_stat" ]; then
@@ -56,7 +82,7 @@ if [ "$state_should" = "present" -o "$state_should" = "exists" ]; then
fi
fi
fi
- if [ "$create_file" -o "$upload_file" ]; then
+ if [ "$create_file" ] || [ "$upload_file" ]; then
# tell gencode-remote that we created or uploaded a file and that it must
# set all attributes no matter what the explorer retreived
mkdir "$__object/files"
@@ -70,7 +96,7 @@ DONE
if [ "$upload_file" ]; then
echo upload >> "$__messages_out"
# IPv6 fix
- if $(echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$')
+ if echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$'
then
my_target_host="[${__target_host}]"
else
diff --git a/cdist/conf/type/__file/gencode-remote b/cdist/conf/type/__file/gencode-remote
index fe1e2212..f7a528fd 100755
--- a/cdist/conf/type/__file/gencode-remote
+++ b/cdist/conf/type/__file/gencode-remote
@@ -23,7 +23,7 @@ destination="/$__object_id"
state_should="$(cat "$__object/parameter/state")"
type="$(cat "$__object/explorer/type")"
stat_file="$__object/explorer/stat"
-
+fire_onchange=''
get_current_value() {
if [ -s "$stat_file" ]; then
@@ -43,52 +43,70 @@ get_current_value() {
}
set_group() {
- echo chgrp \"$1\" \"$destination\"
- echo chgrp $1 >> "$__messages_out"
+ echo "chgrp '$1' '$destination'"
+ echo "chgrp '$1'" >> "$__messages_out"
+ fire_onchange=1
}
set_owner() {
- echo chown \"$1\" \"$destination\"
- echo chown $1 >> "$__messages_out"
+ echo "chown '$1' '$destination'"
+ echo "chown '$1'" >> "$__messages_out"
+ fire_onchange=1
}
set_mode() {
- echo chmod \"$1\" \"$destination\"
- echo chmod $1 >> "$__messages_out"
+ echo "chmod '$1' '$destination'"
+ echo "chmod '$1'" >> "$__messages_out"
+ fire_onchange=1
}
-set_attributes=
case "$state_should" in
present|exists)
- # Note: Mode - needs to happen last as a chown/chgrp can alter mode by
- # clearing S_ISUID and S_ISGID bits (see chown(2))
- for attribute in group owner mode; do
- if [ -f "$__object/parameter/$attribute" ]; then
- value_should="$(cat "$__object/parameter/$attribute")"
+ # Note: Mode - needs to happen last as a chown/chgrp can alter mode by
+ # clearing S_ISUID and S_ISGID bits (see chown(2))
+ for attribute in group owner mode; do
+ if [ -f "$__object/parameter/$attribute" ]; then
+ value_should="$(cat "$__object/parameter/$attribute")"
- # change 0xxx format to xxx format => same as stat returns
- if [ "$attribute" = mode ]; then
- value_should="$(echo $value_should | sed 's/^0\(...\)/\1/')"
- fi
-
- value_is="$(get_current_value "$attribute" "$value_should")"
- if [ -f "$__object/files/set-attributes" -o "$value_should" != "$value_is" ]; then
- "set_$attribute" "$value_should"
+ # format mode in four digits => same as stat returns
+ if [ "$attribute" = mode ]; then
+ # Convert to four-digit octal number (printf interprets
+ # strings with leading 0s as octal!)
+ value_should=$(printf '%04o' "0${value_should}")
+ fi
+
+ value_is="$(get_current_value "$attribute" "$value_should")"
+ if [ -f "$__object/files/set-attributes" ] || [ "$value_should" != "$value_is" ]; then
+ "set_$attribute" "$value_should"
+ fi
fi
+ done
+ if [ -f "$__object/files/set-attributes" ]; then
+ # set-attributes is created if file is created or uploaded in gencode-local
+ fire_onchange=1
fi
- done
-
;;
absent)
if [ "$type" = "file" ]; then
- echo rm -f \"$destination\"
+ echo "rm -f '$destination'"
echo remove >> "$__messages_out"
+ fire_onchange=1
fi
;;
+ pre-exists)
+ :
+ ;;
+
*)
echo "Unknown state: $state_should" >&2
exit 1
;;
esac
+
+if [ -f "$__object/parameter/onchange" ]; then
+ if [ -n "$fire_onchange" ]; then
+ cat "$__object/parameter/onchange"
+ fi
+fi
diff --git a/cdist/conf/type/__file/man.rst b/cdist/conf/type/__file/man.rst
index 7d9b413b..2f3b9e69 100644
--- a/cdist/conf/type/__file/man.rst
+++ b/cdist/conf/type/__file/man.rst
@@ -23,6 +23,10 @@ symlink
directory
replace it with the source file
+One exception is that when state is pre-exists, an error is raised if
+the file would have been created otherwise (e.g. it is not present or
+not a regular file).
+
In any case, make sure that the file attributes are as specified.
@@ -33,7 +37,7 @@ None.
OPTIONAL PARAMETERS
-------------------
state
- 'present', 'absent' or 'exists', defaults to 'present' where:
+ 'present', 'absent', 'exists' or 'pre-exists', defaults to 'present' where:
present
the file is exactly the one from source
@@ -41,21 +45,27 @@ state
the file does not exist
exists
the file from source but only if it doesn't already exist
+ pre-exists
+ check that the file exists and is a regular file, but do not
+ create or modify it
group
- Group to chgrp to.
+ Group to chgrp to. Defaults to ``root``.
mode
- Unix permissions, suitable for chmod.
+ Unix permissions, suitable for chmod. Defaults to a very secure ``0600``.
owner
- User to chown to.
+ User to chown to. Defaults to ``root``.
source
If supplied, copy this file from the host running cdist to the target.
If not supplied, an empty file or directory will be created.
If source is '-' (dash), take what was written to stdin as the file content.
+onchange
+ The code to run if file is modified.
+
MESSAGES
--------
chgrp
@@ -93,6 +103,8 @@ EXAMPLES
__file /home/frodo/.bashrc --source "/etc/skel/.bashrc" \
--state exists \
--owner frodo --mode 0600
+ # Check that the file is present, show an error when it is not
+ __file /etc/somefile --state pre-exists
# Take file content from stdin
__file /tmp/whatever --owner root --group root --mode 644 --source - << DONE
Here goes the content for /tmp/whatever
diff --git a/cdist/conf/type/__file/parameter/optional b/cdist/conf/type/__file/parameter/optional
index c696d592..9b98352c 100644
--- a/cdist/conf/type/__file/parameter/optional
+++ b/cdist/conf/type/__file/parameter/optional
@@ -3,3 +3,4 @@ group
mode
owner
source
+onchange
diff --git a/cdist/conf/type/__filesystem/explorer/lsblk b/cdist/conf/type/__filesystem/explorer/lsblk
index 9ae544ac..9be3c575 100644
--- a/cdist/conf/type/__filesystem/explorer/lsblk
+++ b/cdist/conf/type/__filesystem/explorer/lsblk
@@ -18,16 +18,16 @@
# along with cdist. If not, see .
#
-os=$("$__explorer/os")
+os=$("${__explorer:?}/os")
-if [ -f "$__object/parameter/device" ]; then
+if [ -f "${__object:?}/parameter/device" ]; then
blkdev="$(cat "$__object/parameter/device")"
else
- blkdev="$__object_id"
+ blkdev="${__object_id:?}"
fi
case "$os" in
- centos|fedora|redhat|suse|gentoo)
+ alpine|centos|fedora|redhat|suse|gentoo)
if [ ! -x "$(command -v lsblk)" ]; then
echo "lsblk is required for __filesystem type" >&2
exit 1
diff --git a/cdist/conf/type/__firewalld_rule/explorer/rule b/cdist/conf/type/__firewalld_rule/explorer/rule
index 5a0e0265..0234e5b6 100644
--- a/cdist/conf/type/__firewalld_rule/explorer/rule
+++ b/cdist/conf/type/__firewalld_rule/explorer/rule
@@ -25,7 +25,7 @@ chain="$(cat "$__object/parameter/chain")"
priority="$(cat "$__object/parameter/priority")"
rule="$(cat "$__object/parameter/rule")"
-if firewall-cmd --permanent --direct --query-rule "$protocol" "$table" "$chain" "$priority" $rule >/dev/null; then
+if firewall-cmd --permanent --direct --query-rule "$protocol" "$table" "$chain" "$priority" "$rule" >/dev/null; then
echo present
else
echo absent
diff --git a/cdist/conf/type/__firewalld_rule/gencode-remote b/cdist/conf/type/__firewalld_rule/gencode-remote
index 4c824d39..bd6d13e5 100755
--- a/cdist/conf/type/__firewalld_rule/gencode-remote
+++ b/cdist/conf/type/__firewalld_rule/gencode-remote
@@ -19,7 +19,6 @@
#
#
-name="$__object_id"
state_should="$(cat "$__object/parameter/state")"
state_is="$(cat "$__object/explorer/rule")"
@@ -33,13 +32,13 @@ rule="$(cat "$__object/parameter/rule")"
case "$state_should" in
present)
- echo firewall-cmd --quiet --permanent --direct --add-rule \"$protocol\" \"$table\" \"$chain\" \"$priority\" $rule
- echo firewall-cmd --quiet --direct --add-rule \"$protocol\" \"$table\" \"$chain\" \"$priority\" $rule
+ echo "firewall-cmd --quiet --permanent --direct --add-rule '$protocol' '$table' '$chain' '$priority' $rule"
+ echo "firewall-cmd --quiet --direct --add-rule '$protocol' '$table' '$chain' '$priority' $rule"
;;
absent)
- echo firewall-cmd --quiet --permanent --direct --remove-rule \"$protocol\" \"$table\" \"$chain\" \"$priority\" $rule
- echo firewall-cmd --quiet --direct --remove-rule \"$protocol\" \"$table\" \"$chain\" \"$priority\" $rule
+ echo "firewall-cmd --quiet --permanent --direct --remove-rule '$protocol' '$table' '$chain' '$priority' $rule"
+ echo "firewall-cmd --quiet --direct --remove-rule '$protocol' '$table' '$chain' '$priority' $rule"
;;
*)
echo "Unknown state $state_should" >&2
diff --git a/cdist/conf/type/__git/explorer/group b/cdist/conf/type/__git/explorer/group
index 1308c710..3ddf9656 100644
--- a/cdist/conf/type/__git/explorer/group
+++ b/cdist/conf/type/__git/explorer/group
@@ -2,4 +2,4 @@
destination="/$__object_id/.git"
-stat --print "%G" ${destination} 2>/dev/null || exit 0
+stat --print "%G" "${destination}" 2>/dev/null || exit 0
diff --git a/cdist/conf/type/__git/explorer/owner b/cdist/conf/type/__git/explorer/owner
index 8c36b035..4c3cd431 100644
--- a/cdist/conf/type/__git/explorer/owner
+++ b/cdist/conf/type/__git/explorer/owner
@@ -2,4 +2,4 @@
destination="/$__object_id/.git"
-stat --print "%U" ${destination} 2>/dev/null || exit 0
+stat --print "%U" "${destination}" 2>/dev/null || exit 0
diff --git a/cdist/conf/type/__git/gencode-remote b/cdist/conf/type/__git/gencode-remote
index d0d0d4ed..ab22655f 100755
--- a/cdist/conf/type/__git/gencode-remote
+++ b/cdist/conf/type/__git/gencode-remote
@@ -19,43 +19,46 @@
#
#
-state_is="$(cat "$__object/explorer/state")"
-owner_is="$(cat "$__object/explorer/owner")"
-group_is="$(cat "$__object/explorer/group")"
+state_is=$(cat "$__object/explorer/state")
+owner_is=$(cat "$__object/explorer/owner")
+group_is=$(cat "$__object/explorer/group")
-state_should="$(cat "$__object/parameter/state")"
+state_should=$(cat "$__object/parameter/state")
-branch="$(cat "$__object/parameter/branch")"
+branch=$(cat "$__object/parameter/branch")
-source="$(cat "$__object/parameter/source")"
+source=$(cat "$__object/parameter/source")
destination="/$__object_id"
-owner="$(cat "$__object/parameter/owner")"
-group="$(cat "$__object/parameter/group")"
-mode="$(cat "$__object/parameter/mode")"
+owner=$(cat "$__object/parameter/owner")
+group=$(cat "$__object/parameter/group")
+mode=$(cat "$__object/parameter/mode")
-[ "$state_should" = "$state_is" -a \
- "$owner" = "$owner_is" -a \
- "$group" = "$group_is" -a \
- -n "$mode" ] && exit 0
+[ -f "$__object/parameter/recursive" ] && recursive='--recurse-submodules' || recursive=''
+[ -f "$__object/parameter/shallow" ] && shallow='--depth 1 --shallow-submodules' || shallow=''
+
+[ "$state_should" = "$state_is" ] \
+ && [ "$owner" = "$owner_is" ] \
+ && [ "$group" = "$group_is" ] \
+ && [ -n "$mode" ] && exit 0
case $state_should in
present)
-
if [ "$state_should" != "$state_is" ]; then
- echo git clone --quiet --branch "$branch" "$source" "$destination"
+ echo git clone --quiet "$recursive" "$shallow" --branch "$branch" "$source" "$destination"
fi
- if [ \( -n "$owner" -a "$owner_is" != "$owner" \) -o \
- \( -n "$group" -a "$group_is" != "$group" \) ]; then
+ if { [ -n "$owner" ] && [ "$owner_is" != "$owner" ]; } || \
+ { [ -n "$group" ] && [ "$group_is" != "$group" ]; }; then
echo chown -R "${owner}:${group}" "$destination"
fi
if [ -n "$mode" ]; then
echo chmod -R "$mode" "$destination"
fi
;;
- # Handled in manifest
+
absent)
+ # Handled in manifest
;;
*)
diff --git a/cdist/conf/type/__git/man.rst b/cdist/conf/type/__git/man.rst
index 17e9c623..d3e15f25 100644
--- a/cdist/conf/type/__git/man.rst
+++ b/cdist/conf/type/__git/man.rst
@@ -35,6 +35,12 @@ mode
owner
User to chown to.
+recursive
+ Passes the --recurse-submodules flag to git when cloning the repository.
+
+shallow
+ Sets --depth=1 and --shallow-submodules for cloning repositories with big history.
+
EXAMPLES
--------
@@ -44,7 +50,7 @@ EXAMPLES
__git /home/services/dokuwiki --source git://github.com/splitbrain/dokuwiki.git
# Checkout cdist, stay on branch 2.1
- __git /home/nico/cdist --source git://github.com/ungleich/cdist.git --branch 2.1
+ __git /home/nico/cdist --source git@code.ungleich.ch:ungleich-public/cdist.git --branch 2.1
AUTHORS
diff --git a/cdist/conf/type/__git/parameter/boolean b/cdist/conf/type/__git/parameter/boolean
new file mode 100644
index 00000000..d600d4ca
--- /dev/null
+++ b/cdist/conf/type/__git/parameter/boolean
@@ -0,0 +1,2 @@
+recursive
+shallow
diff --git a/cdist/conf/type/__go_get/explorer/go-executable b/cdist/conf/type/__go_get/explorer/go-executable
old mode 100644
new mode 100755
index 4c84ce07..87182282
--- a/cdist/conf/type/__go_get/explorer/go-executable
+++ b/cdist/conf/type/__go_get/explorer/go-executable
@@ -1,3 +1,6 @@
+#!/bin/sh
+# shellcheck disable=SC1091
[ -f /etc/environment ] && . /etc/environment
+# shellcheck disable=SC1091
[ -f /etc/profile ] && . /etc/profile
go version 2>/dev/null || true
diff --git a/cdist/conf/type/__golang_from_vendor/gencode-remote b/cdist/conf/type/__golang_from_vendor/gencode-remote
index 1654978b..5200e9e3 100755
--- a/cdist/conf/type/__golang_from_vendor/gencode-remote
+++ b/cdist/conf/type/__golang_from_vendor/gencode-remote
@@ -2,7 +2,7 @@
version=$(cat "$__object/parameter/version")
-kernel_name=$(cat "$__global/explorer/kernel_name" | tr '[:upper:]' '[:lower:]')
+kernel_name=$(tr '[:upper:]' '[:lower:]' < "$__global/explorer/kernel_name")
machine=$(cat "$__global/explorer/machine")
case $machine in
x86_64|amd64)
diff --git a/cdist/conf/type/__golang_from_vendor/manifest b/cdist/conf/type/__golang_from_vendor/manifest
index cf164524..ad39ddfb 100755
--- a/cdist/conf/type/__golang_from_vendor/manifest
+++ b/cdist/conf/type/__golang_from_vendor/manifest
@@ -1,3 +1,4 @@
#!/bin/sh -e
+# shellcheck disable=SC2016
__line go_in_path --line 'export PATH=/usr/local/go/bin:$PATH' --file /etc/profile
diff --git a/cdist/conf/type/__grafana_dashboard/manifest b/cdist/conf/type/__grafana_dashboard/manifest
index 898770dd..d145c4c3 100755
--- a/cdist/conf/type/__grafana_dashboard/manifest
+++ b/cdist/conf/type/__grafana_dashboard/manifest
@@ -1,34 +1,46 @@
#!/bin/sh -e
-os=$(cat $__global/explorer/os)
-os_version=$(cat $__global/explorer/os_version)
+os=$(cat "$__global/explorer/os")
+os_version=$(cat "$__global/explorer/os_version")
+require=""
case $os in
debian|devuan)
case $os_version in
8*|jessie)
- __apt_key_uri grafana \
- --name 'Grafana Release Signing Key' \
- --uri https://packagecloud.io/gpg.key
-
- require="__apt_key_uri/grafana" __apt_source grafana \
- --uri https://packagecloud.io/grafana/stable/debian/ \
- --distribution jessie \
- --component main
-
- __package apt-transport-https
-
- require="__apt_source/grafana __package/apt-transport-https" __package grafana
- require="__package/grafana" __start_on_boot grafana-server
- ;;
+ # Differntation not needed anymore
+ apt_source_distribution=stable
+ ;;
+ 9*|ascii/ceres|ascii)
+ # Differntation not needed anymore
+ apt_source_distribution=stable
+ ;;
+ 10*)
+ # Differntation not needed anymore
+ apt_source_distribution=stable
+ ;;
*)
- echo "Don't know how to install Grafana on $os $os_version. Send us a pull request!"
+ echo "Don't know how to install Grafana on $os $os_version. Send us a pull request!" >&2
exit 1
- ;;
+ ;;
esac
- ;;
+
+ __apt_key_uri grafana \
+ --name 'Grafana Release Signing Key' \
+ --uri https://packages.grafana.com/gpg.key
+
+ require="$require __apt_key_uri/grafana" __apt_source grafana \
+ --uri https://packages.grafana.com/oss/deb \
+ --distribution $apt_source_distribution \
+ --component main
+ __package apt-transport-https
+ require="$require __apt_source/grafana" __apt_update_index
+ require="$require __package/apt-transport-https __apt_update_index" __package grafana
+ require="$require __package/grafana" __start_on_boot grafana-server
+ require="$require __start_on_boot/grafana-server" __process grafana-server --start "service grafana-server start"
+ ;;
*)
- echo "Don't know how to install Grafana on $os. Send us a pull request!"
+ echo "Don't know how to install Grafana on $os. Send us a pull request!" >&2
exit 1
- ;;
+ ;;
esac
diff --git a/cdist/conf/type/__group/explorer/group b/cdist/conf/type/__group/explorer/group
index 07f73a91..dc673f61 100755
--- a/cdist/conf/type/__group/explorer/group
+++ b/cdist/conf/type/__group/explorer/group
@@ -1,6 +1,7 @@
#!/bin/sh
#
# 2011-2015 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -21,7 +22,21 @@
# Get an existing groups group entry.
#
+not_supported() {
+ echo "Your operating system ($("$__explorer/os")) is currently not supported." >&2
+ echo "Cannot extract group information." >&2
+ echo "Please contribute an implementation for it if you can." >&2
+ exit 1
+}
+
name=$__object_id
-getent group "$name" || true
-
+if command -v getent >/dev/null
+then
+ getent group "$name" || true
+elif [ -f /etc/group ]
+then
+ grep "^${name}:" /etc/group || true
+else
+ not_supported
+fi
diff --git a/cdist/conf/type/__group/explorer/gshadow b/cdist/conf/type/__group/explorer/gshadow
index 2e2ab29d..05841d69 100755
--- a/cdist/conf/type/__group/explorer/gshadow
+++ b/cdist/conf/type/__group/explorer/gshadow
@@ -1,6 +1,7 @@
#!/bin/sh
#
# 2011-2015 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -22,13 +23,28 @@
#
name=$__object_id
-os="$($__explorer/os)"
+os=$("$__explorer/os")
-case "$os" in
- "freebsd"|"netbsd")
- echo "$os does not have getent gshadow"
- exit 0
- ;;
+not_supported() {
+ echo "Your operating system ($os) is currently not supported." >&2
+ echo "Cannot extract group information." >&2
+ echo "Please contribute an implementation for it if you can." >&2
+ exit 1
+}
+
+case $os in
+ "freebsd"|"netbsd")
+ echo "$os does not have getent gshadow" >&2
+ exit 0
+ ;;
esac
-getent gshadow "$name" || true
+if command -v getent >/dev/null
+then
+ getent gshadow "$name" || true
+elif [ -f /etc/gshadow ]
+then
+ grep "^${name}:" /etc/gshadow || true
+else
+ not_supported
+fi
diff --git a/cdist/conf/type/__group/gencode-remote b/cdist/conf/type/__group/gencode-remote
index 5847cb66..ff63e218 100755
--- a/cdist/conf/type/__group/gencode-remote
+++ b/cdist/conf/type/__group/gencode-remote
@@ -30,9 +30,9 @@ state="$(cat "$__object/parameter/state")"
# Use short option names for portability
shorten_property() {
case "$1" in
- gid) echo "-g";;
- password) echo "-p";;
- system) echo "-r";;
+ gid) echo " -g";;
+ password) echo " -p";;
+ system) echo " -r";;
esac
}
@@ -40,11 +40,9 @@ shorten_property() {
if [ "$state" = "present" ]; then
case "$os" in
freebsd)
- supported_add_properties="gid"
supported_change_properties="gid"
;;
*)
- supported_add_properties="gid password system"
supported_change_properties="gid password"
;;
esac
@@ -63,8 +61,8 @@ if [ "$state" = "present" ]; then
;;
esac
if [ "$new_value" != "$current_value" ]; then
- set -- "$@" "$(shorten_property $property)" \'$new_value\'
- echo change $property $new_value $current_value >> "$__messages_out"
+ set -- "$@" "$(shorten_property "$property")" \'"$new_value"\'
+ echo "change $property $new_value $current_value" >> "$__messages_out"
fi
fi
done
@@ -83,14 +81,14 @@ if [ "$state" = "present" ]; then
new_value="$(cat "$__object/parameter/$property")"
if [ -z "$new_value" ]; then
# Boolean parameters have no value
- set -- "$@" "$(shorten_property $property)"
+ set -- "$@" "$(shorten_property "$property")"
else
- set -- "$@" "$(shorten_property $property)" \'$new_value\'
+ set -- "$@" "$(shorten_property "$property")" \'"$new_value"\'
fi
fi
done
if [ "$os" = "freebsd" ]; then
- echo pw groupadd "$@" "$name"
+ echo pw groupadd "$name" "$@"
else
echo groupadd "$@" "$name"
fi
diff --git a/cdist/conf/type/__hostname/explorer/has_hostnamectl b/cdist/conf/type/__hostname/explorer/has_hostnamectl
index 9040023d..2f531f30 100755
--- a/cdist/conf/type/__hostname/explorer/has_hostnamectl
+++ b/cdist/conf/type/__hostname/explorer/has_hostnamectl
@@ -21,4 +21,4 @@
# Check whether system has hostnamectl
#
-command -v hostnamectl || true
+command -v hostnamectl 2>/dev/null || true
diff --git a/cdist/conf/type/__hostname/explorer/max_len b/cdist/conf/type/__hostname/explorer/max_len
new file mode 100644
index 00000000..fb863949
--- /dev/null
+++ b/cdist/conf/type/__hostname/explorer/max_len
@@ -0,0 +1,10 @@
+#!/bin/sh -e
+
+command -v getconf >/dev/null || exit 0
+
+val=$(getconf HOST_NAME_MAX 2>/dev/null) || exit 0
+
+if test -n "${val}" -a "${val}" != 'undefined'
+then
+ echo "${val}"
+fi
diff --git a/cdist/conf/type/__hostname/gencode-remote b/cdist/conf/type/__hostname/gencode-remote
index dbffad61..c1a97ac8 100755
--- a/cdist/conf/type/__hostname/gencode-remote
+++ b/cdist/conf/type/__hostname/gencode-remote
@@ -2,6 +2,7 @@
#
# 2014-2017 Steven Armstrong (steven-cdist at armstrong.cc)
# 2014 Nico Schottelius (nico-cdist at schottelius.org)
+# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -19,60 +20,86 @@
# along with cdist. If not, see .
#
-if [ -f "$__object/parameter/name" ]; then
- name_should="$(cat "$__object/parameter/name")"
+os=$(cat "${__global:?}/explorer/os")
+name_running=$(cat "${__global:?}/explorer/hostname")
+has_hostnamectl=$(cat "${__object:?}/explorer/has_hostnamectl")
+
+
+if test -s "${__object:?}/parameter/name"
+then
+ name_should=$(cat "${__object:?}/parameter/name")
else
- name_should="$(echo "${__target_host%%.*}")"
+ case ${os}
+ in
+ # RedHat-derivatives and BSDs
+ (centos|fedora|redhat|scientific|freebsd|macosx|netbsd|openbsd)
+ # Hostname is FQDN
+ name_should=${__target_host:?}
+ ;;
+ (*)
+ # Hostname is only first component of FQDN
+ name_should=${__target_host:?}
+ name_should=${name_should%%.*}
+ ;;
+ esac
fi
-os=$(cat "$__global/explorer/os")
-name_running=$(cat "$__global/explorer/hostname")
-name_config=$(cat "$__object/explorer/hostname_file")
-name_sysconfig=$(cat "$__object/explorer/hostname_sysconfig")
-has_hostnamectl=$(cat "$__object/explorer/has_hostnamectl")
################################################################################
-# If everything is ok -> exit
+# Check if the (running) hostname is already correct
#
-case "$os" in
- archlinux|debian|suse|ubuntu|devuan|coreos)
- if [ "$name_config" = "$name_should" -a "$name_running" = "$name_should" ]; then
- exit 0
- fi
- ;;
- scientific|centos|openbsd)
- if [ "$name_sysconfig" = "$name_should" -a "$name_running" = "$name_should" ]; then
- exit 0
- fi
- ;;
- *)
- echo "Unsupported os: $os" >&2
- exit 1
- ;;
-esac
+test "${name_running}" != "${name_should}" || exit 0
+
################################################################################
# Setup hostname
#
-echo changed >> "$__messages_out"
+echo 'changed' >>"${__messages_out:?}"
-# Use the good old way to set the hostname even on machines running systemd.
-case "$os" in
- archlinux|debian|ubuntu|devuan|centos|coreos)
- echo "printf '%s\n' '$name_should' > /etc/hostname"
- echo "hostname -F /etc/hostname"
- ;;
- openbsd)
- echo "hostname '$name_should'"
- ;;
- suse)
- echo "hostname '$name_should'"
- echo "printf '%s\n' '$name_should' > /etc/HOSTNAME"
- ;;
+# Use the good old way to set the hostname.
+case ${os}
+in
+ (alpine|debian|devuan|ubuntu)
+ echo 'hostname -F /etc/hostname'
+ ;;
+ (archlinux)
+ echo 'command -v hostnamectl >/dev/null 2>&1' \
+ "&& hostnamectl set-hostname '${name_should}'" \
+ "|| hostname '${name_should}'"
+ ;;
+ (centos|fedora|redhat|scientific|freebsd|netbsd|openbsd|gentoo|void)
+ echo "hostname '${name_should}'"
+ ;;
+ (openwrt)
+ echo "echo '${name_should}' >/proc/sys/kernel/hostname"
+ ;;
+ (macosx)
+ echo "scutil --set HostName '${name_should}'"
+ ;;
+ (solaris)
+ echo "uname -S '${name_should}'"
+ ;;
+ (slackware|suse)
+ # We do not read from /etc/HOSTNAME, because the running
+ # hostname is the first component only while the file contains
+ # the FQDN.
+ echo "hostname '${name_should}'"
+ ;;
+ (*)
+ # Fall back to set the hostname using hostnamectl, if available.
+ if test -n "${has_hostnamectl}"
+ then
+ # Don't use hostnamectl as the primary means to set the hostname for
+ # systemd systems, because it cannot be trusted to work reliably and
+ # exit with non-zero when it fails (e.g. hostname too long,
+ # D-Bus failure, etc.).
+
+ echo "hostnamectl set-hostname \"\$(cat /etc/hostname)\""
+ echo "test \"\$(hostname)\" = \"\$(cat /etc/hostname)\"" \
+ " || hostname -F /etc/hostname"
+ else
+ printf "echo 'Unsupported OS: %s' >&2\n" "${os}"
+ printf 'exit 1\n'
+ fi
+ ;;
esac
-
-if [ "$has_hostnamectl" ]; then
- # Allow hostnamectl set-hostname to fail silently.
- # Who the fuck invented a tool that needs dbus to set the hostname anyway ...
- echo "hostnamectl set-hostname '$name_should' || true"
-fi
diff --git a/cdist/conf/type/__hostname/man.rst b/cdist/conf/type/__hostname/man.rst
index d23a3b8a..72aefbab 100644
--- a/cdist/conf/type/__hostname/man.rst
+++ b/cdist/conf/type/__hostname/man.rst
@@ -8,7 +8,10 @@ cdist-type__hostname - Set the hostname
DESCRIPTION
-----------
-Set's the hostname on various operating systems.
+Sets the hostname on various operating systems.
+
+**Tip:** For advice on choosing a hostname, see
+`RFC 1178 `_.
REQUIRED PARAMETERS
@@ -18,7 +21,7 @@ None.
OPTIONAL PARAMETERS
-------------------
name
- The hostname to set. Defaults to the first segment of __target_host
+ The hostname to set. Defaults to the first segment of __target_host
(${__target_host%%.*})
diff --git a/cdist/conf/type/__hostname/manifest b/cdist/conf/type/__hostname/manifest
index 4836c501..b80aa2ef 100755
--- a/cdist/conf/type/__hostname/manifest
+++ b/cdist/conf/type/__hostname/manifest
@@ -2,6 +2,7 @@
#
# 2012 Steven Armstrong (steven-cdist at armstrong.cc)
# 2014 Nico Schottelius (nico-cdist at schottelius.org)
+# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -19,43 +20,176 @@
# along with cdist. If not, see .
#
-os=$(cat "$__global/explorer/os")
-if [ -f "$__object/parameter/name" ]; then
- name_should="$(cat "$__object/parameter/name")"
+set_hostname_systemd() {
+ echo "$1" | __file /etc/hostname --source -
+}
+
+os=$(cat "${__global:?}/explorer/os")
+
+max_len=$(cat "${__object:?}/explorer/max_len")
+has_hostnamectl=$(cat "${__object:?}/explorer/has_hostnamectl")
+
+if test -s "${__object:?}/parameter/name"
+then
+ name_should=$(cat "${__object:?}/parameter/name")
else
- case "$os" in
- openbsd)
- name_should="$(echo "${__target_host}")"
- ;;
- *)
- name_should="$(echo "${__target_host%%.*}")"
- ;;
+ case ${os}
+ in
+ # RedHat-derivatives and BSDs
+ (centos|fedora|redhat|scientific|freebsd|netbsd|openbsd|slackware|suse)
+ # Hostname is FQDN
+ name_should=${__target_host:?}
+ ;;
+ *)
+ # Hostname is only first component of FQDN on all other systems.
+ name_should=${__target_host:?}
+ name_should=${name_should%%.*}
+ ;;
esac
fi
+if test -n "${max_len}" && test "$(printf '%s' "${name_should}" | wc -c)" -gt "${max_len}"
+then
+ printf "Host name too long. Up to %u characters allowed.\n" "${max_len}" >&2
+ exit 1
+fi
-not_supported() {
- echo "Your operating system ($os) is currently not supported by this type (${__type##*/})." >&2
- echo "Please contribute an implementation for it if you can." >&2
- exit 1
-}
+case ${os}
+in
+ (alpine|debian|devuan|ubuntu|void)
+ echo "${name_should}" | __file /etc/hostname --source -
+ ;;
+ (archlinux)
+ if test -n "${has_hostnamectl}"
+ then
+ set_hostname_systemd "${name_should}"
+ else
+ echo 'Ancient ArchLinux variants without hostnamectl are not supported.' >&2
+ exit 1
+ # Only for ancient ArchLinux, write to /etc/rc.conf on pre-systemd
+ # versions. There are some versions which use /etc/hostname but not
+ # systemd. It is unclear which ones these are.
-case "$os" in
- archlinux|debian|suse|ubuntu|devuan|coreos)
+ # __key_value '/etc/rc.conf:HOSTNAME' \
+ # --file /etc/rc.conf \
+ # --delimiter '=' --exact_delimiter \
+ # --key 'HOSTNAME' \
+ # --value "\"$name_should\""
+ fi
+ ;;
+ (centos|fedora|redhat|scientific)
+ if test -z "${has_hostnamectl}"
+ then
+ # Only write to /etc/sysconfig/network on non-systemd versions.
+ # On systemd-based versions this entry is ignored.
+ __key_value '/etc/sysconfig/network:HOSTNAME' \
+ --file /etc/sysconfig/network \
+ --delimiter '=' --exact_delimiter \
+ --key HOSTNAME \
+ --value "\"${name_should}\""
+ else
+ set_hostname_systemd "${name_should}"
+ fi
+ ;;
+ (gentoo)
+ # Only write to /etc/conf.d/hostname on OpenRC-based installations.
+ # On systemd use hostnamectl(1) in gencode-remote.
+ if test -z "${has_hostnamectl}"
+ then
+ __key_value '/etc/conf.d/hostname:hostname' \
+ --file /etc/conf.d/hostname \
+ --delimiter '=' --exact_delimiter \
+ --key 'hostname' \
+ --value "\"${name_should}\""
+ else
+ set_hostname_systemd "$name_should"
+ fi
+ ;;
+ (freebsd)
+ __key_value '/etc/rc.conf:hostname' \
+ --file /etc/rc.conf \
+ --delimiter '=' --exact_delimiter \
+ --key 'hostname' \
+ --value "\"${name_should}\""
+ ;;
+ (macosx)
# handled in gencode-remote
- :
- ;;
- scientific|centos)
- __key_value sysconfig-hostname \
- --file /etc/sysconfig/network \
- --delimiter '=' \
- --key HOSTNAME \
- --value "$name_should" --exact_delimiter
- ;;
- openbsd)
- echo "$name_should" | __file /etc/myname --source -
- ;;
- *)
- not_supported
- ;;
+ ;;
+ (netbsd)
+ __key_value '/etc/rc.conf:hostname' \
+ --file /etc/rc.conf \
+ --delimiter '=' --exact_delimiter \
+ --key 'hostname' \
+ --value "\"${name_should}\""
+
+ # To avoid confusion, ensure that the hostname is only stored once.
+ __file /etc/myname --state absent
+ ;;
+ (openbsd)
+ echo "${name_should}" | __file /etc/myname --source -
+ ;;
+ (openwrt)
+ __uci system.@system[0].hostname --value "${name_should}"
+ # --transaction hostname
+ ;;
+ (slackware)
+ # We write the FQDN into /etc/HOSTNAME. But /etc/rc.d/rc.M will only
+ # read the first component from this file and set it as the running
+ # hostname on boot.
+ echo "${name_should}" | __file /etc/HOSTNAME --source -
+ ;;
+ (solaris)
+ echo "${name_should}" | __file /etc/nodename --source -
+ ;;
+ (suse)
+ if test -s "${__global:?}/explorer/os_release"
+ then
+ # shellcheck source=/dev/null
+ os_version=$(. "${__global:?}/explorer/os_release" && echo "${VERSION}")
+ else
+ os_version=$(sed -n 's/^VERSION\ *=\ *//p' "${__global:?}/explorer/os_version")
+ fi
+ os_major=$(expr "${os_version}" : '\([0-9]\{1,\}\)')
+
+ # Classic SuSE stores the FQDN in /etc/HOSTNAME, while
+ # systemd does not. The running hostname is the first
+ # component in both cases.
+ # In versions before 15.x, the FQDN is stored in /etc/hostname.
+ if test -n "${has_hostnamectl}" \
+ && test "${os_major}" -ge 15 \
+ && test "${os_major}" -ne 42
+ then
+ # strip away everything but the first part from $name_should
+ name_should=${name_should%%.*}
+ fi
+
+ # Modern SuSE provides /etc/HOSTNAME as a symlink for
+ # backwards-compatibility. Unfortunately it cannot be used
+ # here as __file does not follow the symlink.
+ # Therefore, we use the presence of the hostnamectl binary as
+ # an indication of which file to use. This unfortunately does
+ # not work correctly on openSUSE 12.x which provides
+ # hostnamectl but not /etc/hostname.
+
+ if test -n "${has_hostnamectl}" -a "${os_major}" -gt 12
+ then
+ hostname_file=/etc/hostname
+ else
+ hostname_file=/etc/HOSTNAME
+ fi
+
+ echo "${name_should}" | __file "${hostname_file}" --source -
+ ;;
+ (*)
+ # On other operating systems we fall back to systemd's
+ # hostnamectl if available…
+ if test -n "${has_hostnamectl}"
+ then
+ set_hostname_systemd "${name_should}"
+ else
+ echo "Your operating system (${os}) is currently not supported by this type (${__type##*/})." >&2
+ echo "Please contribute an implementation for it if you can." >&2
+ exit 1
+ fi
+ ;;
esac
diff --git a/cdist/conf/type/__hosts/man.rst b/cdist/conf/type/__hosts/man.rst
index bece7967..1ac706cb 100644
--- a/cdist/conf/type/__hosts/man.rst
+++ b/cdist/conf/type/__hosts/man.rst
@@ -25,6 +25,10 @@ ip
state is ``present``, this parameter is mandatory, if state is
``absent``, this parameter is silently ignored.
+alias
+ An alias for the hostname.
+ This parameter can be specified multiple times (once per alias).
+
EXAMPLES
--------
@@ -36,6 +40,8 @@ EXAMPLES
# previously configured via __hosts.
__hosts happy --state absent
+ __hosts srv1.example.com --ip 192.168.0.42 --alias srv1
+
SEE ALSO
--------
@@ -43,13 +49,14 @@ SEE ALSO
AUTHORS
-------
-
-Dmitry Bogatov
+| Dmitry Bogatov
+| Dennis Camera
COPYING
-------
-Copyright (C) 2015,2016 Dmitry Bogatov. Free use of this software is granted
-under the terms of the GNU General Public License version 3 or later
-(GPLv3+).
+Copyright \(C) 2015-2016 Dmitry Bogatov, 2019 Dennis Camera.
+You can redistribute it and/or modify it under the terms of the GNU General
+Public License as published by the Free Software Foundation, either version 3 of
+the License, or (at your option) any later version.
diff --git a/cdist/conf/type/__hosts/manifest b/cdist/conf/type/__hosts/manifest
index c536b83b..8103ebd5 100755
--- a/cdist/conf/type/__hosts/manifest
+++ b/cdist/conf/type/__hosts/manifest
@@ -1,29 +1,42 @@
#!/bin/sh -e
-# Copyright (C) 2015 Bogatov Dmitry
#
-# This program is free software: you can redistribute it and/or modify
+# Copyright (C) 2015 Bogatov Dmitry
+# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
-# This program is distributed in the hope that it will be useful,
+# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-set -ue
+#
-hostname="$__object_id"
-state="$(cat "$__object/parameter/state")"
-marker="# __hosts/$hostname"
+set -e
-set -- "__hosts/$hostname" --file /etc/hosts --state "$state"
+hostname=$__object_id
+state=$(cat "${__object}/parameter/state")
+marker="# __hosts/${hostname}"
-if [ "$state" = absent ] ; then
- __line "$@" --regex "$marker"
+if test "${state}" != 'absent'
+then
+ ip=$(cat "${__object}/parameter/ip")
+ if test -s "${__object}/parameter/alias"
+ then
+ aliases=$(while read -r a; do printf '\t%s' "$a"; done <"$__object/parameter/alias")
+ fi
+
+ set -- --line "$(printf '%s\t%s%s %s' \
+ "${ip}" "${hostname}" "${aliases}" "${marker}")"
else
- ip="$(cat "$__object/parameter/ip")"
- __line "$@" --line "$ip $hostname $marker"
+ set -- --regex "$(echo "${marker}" | sed -e 's/\./\\./')$"
fi
+
+__line "/etc/hosts:${hostname}" --file /etc/hosts --state "${state}" "$@"
diff --git a/cdist/conf/type/__hosts/parameter/optional_multiple b/cdist/conf/type/__hosts/parameter/optional_multiple
new file mode 100644
index 00000000..d077ed80
--- /dev/null
+++ b/cdist/conf/type/__hosts/parameter/optional_multiple
@@ -0,0 +1 @@
+alias
diff --git a/cdist/conf/type/__hwclock/explorer/adjtime_mode b/cdist/conf/type/__hwclock/explorer/adjtime_mode
new file mode 100755
index 00000000..2b27bedc
--- /dev/null
+++ b/cdist/conf/type/__hwclock/explorer/adjtime_mode
@@ -0,0 +1,28 @@
+#!/bin/sh -e
+#
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+# Prints the clock mode read from the /etc/adjtime file, if present.
+#
+
+# not all operating systems use an adjfile
+test -f /etc/adjtime || exit 0
+
+# 3rd line is clock mode
+# adjtime(5) https://man7.org/linux/man-pages/man5/adjtime.5.html
+sed -n 3p /etc/adjtime
diff --git a/cdist/conf/type/__pf_apply/explorer/rcvar b/cdist/conf/type/__hwclock/explorer/timedatectl_localrtc
similarity index 63%
rename from cdist/conf/type/__pf_apply/explorer/rcvar
rename to cdist/conf/type/__hwclock/explorer/timedatectl_localrtc
index 20e9dfcc..8239122e 100755
--- a/cdist/conf/type/__pf_apply/explorer/rcvar
+++ b/cdist/conf/type/__hwclock/explorer/timedatectl_localrtc
@@ -1,6 +1,6 @@
-#!/bin/sh
+#!/bin/sh -e
#
-# 2012 Jake Guffey (jake.guffey at eprotex.com)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -17,20 +17,11 @@
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see .
#
-#
-# Get the location of the pf ruleset on the target host.
+# Prints the LocalRTC property using timedatectl on systemd-based systems.
#
-# Debug
-#exec >&2
-#set -x
-
-# Check /etc/rc.conf for pf's configuration file name. Default to /etc/pf.conf
-
-RC="/etc/rc.conf"
-PFCONF="$(grep '^pf_rules=' ${RC} | cut -d= -f2 | sed 's/"//g')"
-echo ${PFCONF:-"/etc/pf.conf"}
-
-# Debug
-#set +x
+command -v timedatectl >/dev/null 2>&1 || exit 0
+# NOTE: Older versions of timedatectl do not support `timedatectl show'
+timedatectl --no-pager status \
+| awk -F': ' '$1 ~ "RTC in local TZ$" { sub(/[ \t]*$/, "", $2); print $2 }'
diff --git a/cdist/conf/type/__hwclock/gencode-remote b/cdist/conf/type/__hwclock/gencode-remote
new file mode 100755
index 00000000..5995fb23
--- /dev/null
+++ b/cdist/conf/type/__hwclock/gencode-remote
@@ -0,0 +1,62 @@
+#!/bin/sh -e
+#
+# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+mode=$(cat "${__object:?}/parameter/mode")
+
+timedatectl_localrtc=$(cat "${__object:?}/explorer/timedatectl_localrtc")
+adjtime_mode=$(cat "${__object:?}/explorer/adjtime_mode")
+
+
+case ${mode}
+in
+ (localtime)
+ adjtime_str=LOCAL
+ local_rtc_str=yes
+ ;;
+ (UTC|utc)
+ adjtime_str=UTC
+ local_rtc_str=no
+ ;;
+ (*)
+ printf 'Invalid value for --mode: %s\n' "${mode}" >&2
+ printf 'Acceptable values are: localtime, utc.\n' >&2
+ exit 1
+esac
+
+
+if test -n "${timedatectl_localrtc}"
+then
+ # systemd
+ timedatectl_should=${local_rtc_str}
+ if test "${timedatectl_localrtc}" != "${timedatectl_should}"
+ then
+ printf 'timedatectl set-local-rtc %s\n' "${timedatectl_should}"
+ fi
+elif test -n "${adjtime_mode}"
+then
+ # others (update /etc/adjtime if present)
+ if test "${adjtime_mode}" != "${adjtime_str}"
+ then
+ # Update /etc/adjtime (3rd line is clock mode)
+ # adjtime(5) https://man7.org/linux/man-pages/man5/adjtime.5.html
+ # FIXME: Should maybe add third line if adjfile only contains two lines
+ printf "sed -i '3c\\\\\\n%s\\n' /etc/adjtime\\n" "${adjtime_str}"
+ fi
+fi
diff --git a/cdist/conf/type/__hwclock/man.rst b/cdist/conf/type/__hwclock/man.rst
new file mode 100644
index 00000000..65eb648f
--- /dev/null
+++ b/cdist/conf/type/__hwclock/man.rst
@@ -0,0 +1,63 @@
+cdist-type__hwclock(7)
+======================
+
+NAME
+----
+cdist-type__hwclock - Manage the hardware real time clock.
+
+
+DESCRIPTION
+-----------
+This type can be used to control how the hardware clock is used by the operating
+system.
+
+
+REQUIRED PARAMETERS
+-------------------
+mode
+ What mode the hardware clock is in.
+
+ Acceptable values:
+
+ localtime
+ The hardware clock is set to local time (common for systems also running
+ Windows.)
+ UTC
+ The hardware clock is set to UTC (common on UNIX systems.)
+
+
+OPTIONAL PARAMETERS
+-------------------
+None.
+
+
+BOOLEAN PARAMETERS
+------------------
+None.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # Make the operating system treat the time read from the hwclock as UTC.
+ __hwclock --mode UTC
+
+
+SEE ALSO
+--------
+:strong:`hwclock`\ (8)
+
+
+AUTHORS
+-------
+Dennis Camera
+
+
+COPYING
+-------
+Copyright \(C) 2020 Dennis Camera. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__hwclock/manifest b/cdist/conf/type/__hwclock/manifest
new file mode 100755
index 00000000..7d9ab88f
--- /dev/null
+++ b/cdist/conf/type/__hwclock/manifest
@@ -0,0 +1,222 @@
+#!/bin/sh -e
+#
+# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+# TODO: Consider supporting BADYEAR
+
+os=$(cat "${__global:?}/explorer/os")
+mode=$(cat "${__object:?}/parameter/mode")
+
+has_systemd_timedatectl=$(test -s "${__object:?}/explorer/timedatectl_localrtc" && echo true || echo false)
+
+
+case ${mode}
+in
+ (localtime)
+ local_clock=true
+ ;;
+ (UTC|utc)
+ local_clock=false
+ ;;
+ (*)
+ printf 'Invalid value for --mode: %s\n' "${mode}" >&2
+ printf 'Acceptable values are: UTC, localtime.\n' >&2
+ exit 1
+esac
+
+
+case ${os}
+in
+ (alpine|gentoo)
+ if ! $has_systemd_timedatectl
+ then
+ # NOTE: Gentoo also supports systemd, in which case /etc/conf.d is
+ # not used. So we check for systemd presence here and only
+ # update /etc/conf.d if systemd is not installed.
+ # https://wiki.gentoo.org/wiki/System_time#Hardware_clock
+
+ export CDIST_ORDER_DEPENDENCY=true
+ __file /etc/conf.d/hwclock --state present \
+ --owner root --group root --mode 0644
+ __key_value /etc/conf.d/hwclock:clock \
+ --file /etc/conf.d/hwclock \
+ --key clock \
+ --delimiter '=' --exact_delimiter \
+ --value "\"$($local_clock && echo local || echo UTC)\""
+ unset CDIST_ORDER_DEPENDENCY
+ fi
+ ;;
+ (centos|fedora|redhat|scientific)
+ os_version=$(cat "${__global:?}/explorer/os_version")
+ os_major=$(expr "${os_version}" : '.* release \([0-9]*\)')
+ case ${os}
+ in
+ (centos|scientific)
+ update_sysconfig=$(test "${os_major}" -lt 6 && echo true || echo false)
+ ;;
+ (fedora)
+ update_sysconfig=$(test "${os_major}" -lt 10 && echo true || echo false)
+ ;;
+ (redhat|*)
+ case ${os_version}
+ in
+ ('Red Hat Enterprise Linux'*)
+ update_sysconfig=$(test "${os_major}" -lt 6 && echo true || echo false)
+ ;;
+ ('Red Hat Linux'*)
+ update_sysconfig=true
+ ;;
+ (*)
+ printf 'Could not determine Red Hat distribution.\n' >&2
+ printf "Please contribute an implementation for it if you can.\n" >&2
+ exit 1
+ ;;
+ esac
+ ;;
+ esac
+
+ if ${update_sysconfig:?}
+ then
+ export CDIST_ORDER_DEPENDENCY=true
+ __file /etc/sysconfig/clock --state present \
+ --owner root --group root --mode 0644
+ __key_value /etc/sysconfig/clock:UTC \
+ --file /etc/sysconfig/clock \
+ --key UTC \
+ --delimiter '=' --exact_delimiter \
+ --value "$($local_clock && echo false || echo true)"
+ unset CDIST_ORDER_DEPENDENCY
+ fi
+ ;;
+ (debian|devuan|ubuntu)
+ os_major=$(sed 's/[^0-9].*$//' "${__global:?}/explorer/os_version")
+
+ case ${os}
+ in
+ (debian)
+ if test "${os_major}" -ge 7
+ then
+ update_rcS=false
+ elif test "${os_major}" -ge 3
+ then
+ update_rcS=true
+ else
+ # Debian 2.2 should be supportable using rcS.
+ # Debian 2.1 uses the ancient GMT key.
+ # Debian 1.3 does not have rcS.
+ printf "Your operating system (Debian %s) is currently not supported by this type (%s)\n" \
+ "$(cat "${__global:?}/explorer/os_version")" "${__type##*/}" >&2
+ printf "Please contribute an implementation for it if you can.\n" >&2
+ exit 1
+ fi
+ ;;
+ (devuan)
+ update_rcS=false
+ ;;
+ (ubuntu)
+ update_rcS=$(test "${os_major}" -lt 16 && echo true || echo false)
+ ;;
+ esac
+
+ if ${update_rcS}
+ then
+ export CDIST_ORDER_DEPENDENCY=true
+ __file /etc/default/rcS --state present \
+ --owner root --group root --mode 0644
+ __key_value /etc/default/rcS:UTC \
+ --file /etc/default/rcS \
+ --key UTC \
+ --delimiter '=' --exact_delimiter \
+ --value "$($local_clock && echo no || echo yes)"
+ unset CDIST_ORDER_DEPENDENCY
+ fi
+ ;;
+ (freebsd)
+ # cf. adjkerntz(8)
+ __file /etc/wall_cmos_clock \
+ --state "$($local_clock && echo present || echo absent)" \
+ --owner root --group wheel --mode 0444
+ ;;
+ (netbsd)
+ # https://wiki.netbsd.org/guide/boot/#index9h2
+ __key_value /etc/rc.conf:rtclocaltime \
+ --file /etc/rc.conf \
+ --key rtclocaltime \
+ --delimiter '=' --exact_delimiter \
+ --value "$($local_clock && echo YES || echo NO)"
+ ;;
+ (slackware)
+ __file /etc/hardwareclock --owner root --group root --mode 0644 \
+ --source - <<-EOF
+ # /etc/hardwareclock
+ #
+ # Tells how the hardware clock time is stored.
+ # This file is managed by cdist.
+
+ $($local_clock && echo localtime || echo UTC)
+ EOF
+ ;;
+ (suse)
+ if test -s "${__global:?}/explorer/os_release"
+ then
+ # shellcheck source=/dev/null
+ os_version=$(. "${__global:?}/explorer/os_release" && echo "${VERSION}")
+ else
+ os_version=$(sed -n 's/^VERSION\ *=\ *//p' "${__global:?}/explorer/os_version")
+ fi
+ os_major=$(expr "${os_version}" : '\([0-9]\{1,\}\)')
+
+ # TODO: Consider using `yast2 timezone set hwclock' instead
+ if expr "${os_major}" \< 12
+ then
+ # Starting with SuSE 12 (first systemd-based version)
+ # /etc/sysconfig/clock does not contain the HWCLOCK line
+ # anymore.
+ # With SuSE 13, it has been reduced to TIMEZONE configuration.
+ __key_value /etc/sysconfig/clock:HWCLOCK \
+ --file /etc/sysconfig/clock \
+ --delimiter '=' --exact_delimiter \
+ --key HWCLOCK \
+ --value "$($local_clock && echo '"--localtime"' || echo '"-u"')"
+ fi
+ ;;
+ (void)
+ export CDIST_ORDER_DEPENDENCY=true
+ __file /etc/rc.conf \
+ --owner root --group root --mode 0644 \
+ --state present
+ __key_value /etc/rc.conf:HARDWARECLOCK \
+ --file /etc/rc.conf \
+ --delimiter '=' --exact_delimiter \
+ --key HARDWARECLOCK \
+ --value "\"$($local_clock && echo localtime || echo UTC)\""
+ unset CDIST_ORDER_DEPENDENCY
+ ;;
+ (*)
+ if ! $has_systemd_timedatectl
+ then
+ printf "Your operating system (%s) is currently not supported by this type (%s)\n" "$os" "${__type##*/}" >&2
+ printf "Please contribute an implementation for it if you can.\n" >&2
+ exit 1
+ fi
+ ;;
+esac
+
+# NOTE: timedatectl set-local-rtc for systemd is in gencode-remote
+# NOTE: /etc/adjtime is also updated in gencode-remote
diff --git a/cdist/conf/type/__hwclock/parameter/required b/cdist/conf/type/__hwclock/parameter/required
new file mode 100644
index 00000000..17ab372f
--- /dev/null
+++ b/cdist/conf/type/__hwclock/parameter/required
@@ -0,0 +1 @@
+mode
diff --git a/cdist/conf/type/__hwclock/singleton b/cdist/conf/type/__hwclock/singleton
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__install_bootloader_grub/gencode-remote b/cdist/conf/type/__install_bootloader_grub/gencode-remote
index 6e6e5e85..1caebbbf 100755
--- a/cdist/conf/type/__install_bootloader_grub/gencode-remote
+++ b/cdist/conf/type/__install_bootloader_grub/gencode-remote
@@ -28,7 +28,7 @@ install_script="$__object/files/install_script"
# Link file descriptor #6 with stdout
exec 6>&1
# Link stdout with $install_script
-exec > $install_script
+exec > "$install_script"
# Generate script to install bootloader on distro
printf '#!/bin/sh -l\n'
diff --git a/cdist/conf/type/__install_chroot_umount/manifest b/cdist/conf/type/__install_chroot_umount/manifest
new file mode 120000
index 00000000..f17af67a
--- /dev/null
+++ b/cdist/conf/type/__install_chroot_umount/manifest
@@ -0,0 +1 @@
+../__chroot_umount/manifest
\ No newline at end of file
diff --git a/cdist/conf/type/__install_config/files/remote/copy b/cdist/conf/type/__install_config/files/remote/copy
index 15c901f9..fa7fa9b7 100755
--- a/cdist/conf/type/__install_config/files/remote/copy
+++ b/cdist/conf/type/__install_config/files/remote/copy
@@ -37,10 +37,12 @@ code="$(echo "$@" | sed "s|$target_host:|$target_host:$chroot|g")"
log "target_host: $target_host"
log "chroot: $chroot"
-log "@: $@"
+log "@: $*"
log "code: $code"
# copy files into chroot
+# __default_remote_copy and code should be split
+# shellcheck disable=SC2086
$__default_remote_copy $code
log "-----"
diff --git a/cdist/conf/type/__install_config/files/remote/exec b/cdist/conf/type/__install_config/files/remote/exec
index 5b25e41e..c2057ebf 100755
--- a/cdist/conf/type/__install_config/files/remote/exec
+++ b/cdist/conf/type/__install_config/files/remote/exec
@@ -36,14 +36,17 @@ shift
# escape ' with '"'"'
code="$(echo "$@" | sed -e "s/'/'\"'\"'/g")"
+# shellcheck disable=SC2089
code="chroot $chroot sh -e -c '$code'"
log "target_host: $target_host"
log "chroot: $chroot"
-log "@: $@"
+log "@: $*"
log "code: $code"
# Run the code
-$__default_remote_exec $target_host $code
+# __default_remote_exec and code should be split
+# shellcheck disable=SC2086,SC2090
+$__default_remote_exec "$target_host" $code
log "-----"
diff --git a/cdist/conf/type/__install_config/gencode-local b/cdist/conf/type/__install_config/gencode-local
index 8f24cf2e..dd4f2a78 100755
--- a/cdist/conf/type/__install_config/gencode-local
+++ b/cdist/conf/type/__install_config/gencode-local
@@ -1,6 +1,6 @@
#!/bin/sh -e
#
-# 2011-2017 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2011-2018 Steven Armstrong (steven-cdist at armstrong.cc)
#
# This file is part of cdist.
#
@@ -23,6 +23,7 @@ remote_exec="$__type/files/remote/exec"
remote_copy="$__type/files/remote/copy"
cat << DONE
+export __cdist_install_config=yes
export __cdist_log_level=$__cdist_log_level
export __default_remote_exec="$__remote_exec"
export __default_remote_copy="$__remote_copy"
diff --git a/cdist/conf/type/__install_coreos/gencode-remote b/cdist/conf/type/__install_coreos/gencode-remote
new file mode 100755
index 00000000..f550b5a5
--- /dev/null
+++ b/cdist/conf/type/__install_coreos/gencode-remote
@@ -0,0 +1,19 @@
+#!/bin/sh -e
+
+device=$(cat "${__object:?}/parameter/device")
+ignition=$(cat "${__object}/parameter/ignition")
+
+cat < "\${ignition_file}" << eof
+$(base64 "${ignition}")
+eof
+
+coreos-install -d "${device}" \
+ \$(if [ -s "\${ignition_file}" ]; then
+ printf -- "-i \${ignition_file}\\n"
+ fi)
+
+rm "\${ignition_file}"
+EOF
diff --git a/cdist/conf/type/__install_coreos/install b/cdist/conf/type/__install_coreos/install
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__install_coreos/man.rst b/cdist/conf/type/__install_coreos/man.rst
new file mode 100644
index 00000000..314f9f2a
--- /dev/null
+++ b/cdist/conf/type/__install_coreos/man.rst
@@ -0,0 +1,50 @@
+cdist-type__install_coreos(7)
+=============================
+
+NAME
+----
+
+cdist-type__install_coreos - Install CoreOS
+
+DESCRIPTION
+-----------
+
+This type installs CoreOS to a given device using coreos-install_, which is
+present in CoreOS ISO by default.
+
+.. _coreos-install: https://raw.githubusercontent.com/coreos/init/master/bin/coreos-install
+
+REQUIRED PARAMETERS
+-------------------
+
+device
+ A device CoreOS will be installed to.
+
+OPTIONAL PARAMETERS
+-------------------
+
+ignition
+ Path to ignition config.
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ __install_coreos \
+ --device /dev/sda \
+ --ignition ignition.json
+
+
+AUTHORS
+-------
+
+Ľubomír Kučera
+
+COPYING
+-------
+
+Copyright \(C) 2018 Ľubomír Kučera. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__install_coreos/parameter/default/ignition b/cdist/conf/type/__install_coreos/parameter/default/ignition
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__install_coreos/parameter/optional b/cdist/conf/type/__install_coreos/parameter/optional
new file mode 100644
index 00000000..df284caa
--- /dev/null
+++ b/cdist/conf/type/__install_coreos/parameter/optional
@@ -0,0 +1 @@
+ignition
diff --git a/cdist/conf/type/__install_coreos/parameter/required b/cdist/conf/type/__install_coreos/parameter/required
new file mode 100644
index 00000000..f89ee6a8
--- /dev/null
+++ b/cdist/conf/type/__install_coreos/parameter/required
@@ -0,0 +1 @@
+device
diff --git a/cdist/conf/type/__install_coreos/singleton b/cdist/conf/type/__install_coreos/singleton
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__install_directory/man.rst b/cdist/conf/type/__install_directory/man.rst
deleted file mode 120000
index 1ad7fa84..00000000
--- a/cdist/conf/type/__install_directory/man.rst
+++ /dev/null
@@ -1 +0,0 @@
-../__directory/man.rst
\ No newline at end of file
diff --git a/cdist/conf/type/__install_directory/man.rst b/cdist/conf/type/__install_directory/man.rst
new file mode 100644
index 00000000..c402cbad
--- /dev/null
+++ b/cdist/conf/type/__install_directory/man.rst
@@ -0,0 +1,101 @@
+cdist-type__install_directory(7)
+================================
+
+NAME
+----
+cdist-type__install_directory - Manage a directory with install command
+
+
+DESCRIPTION
+-----------
+This cdist type allows you to create or remove directories on the target.
+
+
+REQUIRED PARAMETERS
+-------------------
+None.
+
+
+OPTIONAL PARAMETERS
+-------------------
+state
+ 'present' or 'absent', defaults to 'present'
+
+group
+ Group to chgrp to.
+
+mode
+ Unix permissions, suitable for chmod.
+
+owner
+ User to chown to.
+
+
+BOOLEAN PARAMETERS
+------------------
+parents
+ Whether to create parents as well (mkdir -p behaviour).
+ Warning: all intermediate directory permissions default
+ to whatever mkdir -p does.
+
+ Usually this means root:root, 0700.
+
+recursive
+ If supplied the chgrp and chown call will run recursively.
+ This does *not* influence the behaviour of chmod.
+
+MESSAGES
+--------
+chgrp
+ Changed group membership
+chown
+ Changed owner
+chmod
+ Changed mode
+create
+ Empty directory was created
+remove
+ Directory exists, but state is absent, directory will be removed by generated code.
+remove non directory
+ Something other than a directory with the same name exists and was removed prior to create.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # A silly example
+ __install_directory /tmp/foobar
+
+ # Remove a directory
+ __install_directory /tmp/foobar --state absent
+
+ # Ensure /etc exists correctly
+ __install_directory /etc --owner root --group root --mode 0755
+
+ # Create nfs service directory, including parents
+ __install_directory /home/services/nfs --parents
+
+ # Change permissions recursively
+ __install_directory /home/services --recursive --owner root --group root
+
+ # Setup a temp directory
+ __install_directory /local --mode 1777
+
+ # Take it all
+ __install_directory /home/services/kvm --recursive --parents \
+ --owner root --group root --mode 0755 --state present
+
+
+AUTHORS
+-------
+Nico Schottelius
+
+
+COPYING
+-------
+Copyright \(C) 2011 Nico Schottelius. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__install_file/man.rst b/cdist/conf/type/__install_file/man.rst
index c5409167..977ed77c 100644
--- a/cdist/conf/type/__install_file/man.rst
+++ b/cdist/conf/type/__install_file/man.rst
@@ -23,6 +23,10 @@ symlink
directory
replace it with the source file
+One exception is that when state is pre-exists, an error is raised if
+the file would have been created otherwise (e.g. it is not present or
+not a regular file).
+
In any case, make sure that the file attributes are as specified.
@@ -33,7 +37,7 @@ None.
OPTIONAL PARAMETERS
-------------------
state
- 'present', 'absent' or 'exists', defaults to 'present' where:
+ 'present', 'absent', 'exists' or 'pre-exists', defaults to 'present' where:
present
the file is exactly the one from source
@@ -41,6 +45,9 @@ state
the file does not exist
exists
the file from source but only if it doesn't already exist
+ pre-exists
+ check that the file exists and is a regular file, but do not
+ create or modify it
group
Group to chgrp to.
@@ -56,6 +63,9 @@ source
If not supplied, an empty file or directory will be created.
If source is '-' (dash), take what was written to stdin as the file content.
+onchange
+ The code to run if file is modified.
+
MESSAGES
--------
chgrp
@@ -93,6 +103,8 @@ EXAMPLES
__install_file /home/frodo/.bashrc --source "/etc/skel/.bashrc" \
--state exists \
--owner frodo --mode 0600
+ # Check that the file is present, show an error when it is not
+ __install_file /etc/somefile --state pre-exists
# Take file content from stdin
__install_file /tmp/whatever --owner root --group root --mode 644 --source - << DONE
Here goes the content for /tmp/whatever
diff --git a/cdist/conf/type/__install_generate_fstab/gencode-local b/cdist/conf/type/__install_generate_fstab/gencode-local
index 5cc7d877..80455aaa 100755
--- a/cdist/conf/type/__install_generate_fstab/gencode-local
+++ b/cdist/conf/type/__install_generate_fstab/gencode-local
@@ -23,12 +23,13 @@ cat "$__type/files/fstab.header" > "$destination"
mkdir "$__object/files"
# get current UUID's from target_host
-$__remote_exec $__target_host blkid > "$__object/files/blkid"
+$__remote_exec "$__target_host" blkid > "$__object/files/blkid"
-for object in $(find "$__global/object/__install_mount" -type d -name "$__cdist_object_marker"); do
+find "$__global/object/__install_mount" -type d -name "$__cdist_object_marker" |
+while IFS= read -r object
+do
device="$(cat "$object/parameter/device")"
dir="$(cat "$object/parameter/dir")"
- prefix="$(cat "$object/parameter/prefix")"
type="$(cat "$object/parameter/type")"
if [ -f "$object/parameter/options" ]; then
options="$(cat "$object/parameter/options")"
@@ -54,7 +55,7 @@ for object in $(find "$__global/object/__install_mount" -type d -name "$__cdist_
;;
esac
if [ -f "$__object/parameter/uuid" ]; then
- uuid="$(grep -w $device "$__object/files/blkid" | awk '{print $2}')"
+ uuid="$(grep -w "$device" "$__object/files/blkid" | awk '{print $2}')"
if [ -n "$uuid" ]; then
echo "# $dir was on $device during installation" >> "$destination"
device="$uuid"
diff --git a/cdist/conf/type/__install_mkfs/manifest b/cdist/conf/type/__install_mkfs/manifest
index eb65757f..b0a21dae 100755
--- a/cdist/conf/type/__install_mkfs/manifest
+++ b/cdist/conf/type/__install_mkfs/manifest
@@ -19,13 +19,7 @@
#
# set defaults
-if [ -f "$__object/parameter/device" ]; then
- device="(cat "$__object/parameter/device")"
-else
+if [ ! -f "$__object/parameter/device" ]; then
device="/$__object_id"
echo "$device" > "$__object/parameter/device"
fi
-
-type="(cat "$__object/parameter/type")"
-
-options="(cat "$__object/parameter/options")"
diff --git a/cdist/conf/type/__install_mount/gencode-remote b/cdist/conf/type/__install_mount/gencode-remote
index ce96279a..4415f0ff 100755
--- a/cdist/conf/type/__install_mount/gencode-remote
+++ b/cdist/conf/type/__install_mount/gencode-remote
@@ -20,7 +20,9 @@
get_type_from_mkfs() {
_device="$1"
- for mkfs_object in $(find "$__global/object/__install_mkfs" -type d -name "$__cdist_object_marker"); do
+ find "$__global/object/__install_mkfs" -type d -name "$__cdist_object_marker" |
+ while IFS= read -r mkfs_object
+ do
mkfs_device="$(cat "$mkfs_object/parameter/device")"
if [ "$_device" = "$mkfs_device" ]; then
cat "$mkfs_object/parameter/type"
diff --git a/cdist/conf/type/__install_partition_msdos_apply/files/lib.sh b/cdist/conf/type/__install_partition_msdos_apply/files/lib.sh
index cddc575d..2db9a441 100644
--- a/cdist/conf/type/__install_partition_msdos_apply/files/lib.sh
+++ b/cdist/conf/type/__install_partition_msdos_apply/files/lib.sh
@@ -1,18 +1,20 @@
+#!/bin/sh
+
die() {
- echo "[__install_partition_msdos_apply] $@" >&2
+ echo "[__install_partition_msdos_apply] $*" >&2
exit 1
}
debug() {
- #echo "[__install_partition_msdos_apply] $@" >&2
+ #echo "[__install_partition_msdos_apply] $*" >&2
:
}
fdisk_command() {
- local device="$1"
- local cmd="$2"
+ device="$1"
+ cmd="$2"
debug fdisk_command "running fdisk command '${cmd}' on device ${device}"
- printf "${cmd}\nw\n" | fdisk -c -u "$device"
+ printf '%s\nw\n' "${cmd}" | fdisk -c -u "$device"
ret=$?
# give disk some time
sleep 1
@@ -20,49 +22,49 @@ fdisk_command() {
}
create_disklabel() {
- local device=$1
+ device=$1
debug create_disklabel "creating new msdos disklabel"
- fdisk_command ${device} "o"
+ fdisk_command "${device}" "o"
return $?
}
toggle_bootable() {
- local device="$1"
- local minor="$2"
- fdisk_command ${device} "a\n${minor}\n"
+ device="$1"
+ minor="$2"
+ fdisk_command "${device}" "a\\n${minor}\\n"
return $?
}
create_partition() {
- local device="$1"
- local minor="$2"
- local size="$3"
- local type="$4"
- local primary_count="$5"
+ device="$1"
+ minor="$2"
+ size="$3"
+ type="$4"
+ primary_count="$5"
- if [ "$type" = "extended" -o "$type" = "5" ]; then
+ if [ "$type" = "extended" ] || [ "$type" = "5" ]; then
# Extended partition
- primary_extended="e\n"
- first_minor="${minor}\n"
+ primary_extended='e\n'
+ first_minor="${minor}\\n"
[ "${minor}" = "4" ] && first_minor=""
- type_minor="${minor}\n"
+ type_minor="${minor}\\n"
[ "${minor}" = "1" ] && type_minor=""
type="5"
elif [ "${minor}" -lt "5" ]; then
- primary_extended="p\n"
- first_minor="${minor}\n"
+ primary_extended='p\n'
+ first_minor="${minor}\\n"
[ "${minor}" = "4" ] && first_minor=""
- type_minor="${minor}\n"
+ type_minor="${minor}\\n"
[ "${minor}" = "1" ] && type_minor=""
else
# Logical partitions
- first_minor="${minor}\n"
- type_minor="${minor}\n"
- primary_extended="l\n"
+ first_minor="${minor}\\n"
+ type_minor="${minor}\\n"
+ primary_extended='l\n'
[ "$primary_count" -gt "3" ] && primary_extended=""
fi
[ -n "${size}" ] && size="+${size}M"
- fdisk_command ${device} "n\n${primary_extended}${first_minor}\n${size}\nt\n${type_minor}${type}\n"
+ fdisk_command "${device}" "n\\n${primary_extended}${first_minor}\\n${size}\\nt\\n${type_minor}${type}\\n"
return $?
}
diff --git a/cdist/conf/type/__install_partition_msdos_apply/gencode-remote b/cdist/conf/type/__install_partition_msdos_apply/gencode-remote
index 090a5d86..a0b46b2d 100755
--- a/cdist/conf/type/__install_partition_msdos_apply/gencode-remote
+++ b/cdist/conf/type/__install_partition_msdos_apply/gencode-remote
@@ -21,35 +21,35 @@
#set -x
die() {
- echo "[__install_partition_msdos_apply] $@" >&2
+ echo "[__install_partition_msdos_apply] $*" >&2
exit 1
}
debug() {
- #echo "[__install_partition_msdos_apply] $@" >&2
+ #echo "[__install_partition_msdos_apply] $*" >&2
:
}
# Convert a size specifier 1G 100M or 50% into the corresponding numeric MB.
size_to_mb() {
- local size=$1
- local available_size="$2"
+ size=$1
+ available_size="$2"
- local number_suffix="$(echo ${size} | sed -e 's:\.[0-9]\+::' -e 's:\([0-9]\+\)\([KkMmGg%]\)[Bb]\?:\1|\2:')"
- local number="$(echo ${number_suffix} | cut -d '|' -f1)"
- local suffix="$(echo ${number_suffix} | cut -d '|' -f2)"
+ number_suffix="$(echo "${size}" | sed -e 's:\.[0-9]\+::' -e 's:\([0-9]\+\)\([KkMmGg%]\)[Bb]\?:\1|\2:')"
+ number="$(echo "${number_suffix}" | cut -d '|' -f1)"
+ suffix="$(echo "${number_suffix}" | cut -d '|' -f2)"
case "$suffix" in
K|k)
- size="$(( $number / 1024 ))"
+ size="$(( number / 1024 ))"
;;
M|m)
size="$number"
;;
G|g)
- size="$(( $number * 1024 ))"
+ size="$(( number * 1024 ))"
;;
%)
- size="$(( $available_size * $number / 100 ))"
+ size="$(( available_size * number / 100 ))"
;;
*)
size="-1"
@@ -59,13 +59,15 @@ size_to_mb() {
get_objects() {
objects_file=$(mktemp)
- for object in $(find "$__global/object/__install_partition_msdos" -type d -name "$__cdist_object_marker"); do
+ find "$__global/object/__install_partition_msdos" -type d -name "$__cdist_object_marker" |
+ while IFS= read -r object
+ do
object_device="$(cat "$object/parameter/device")"
object_minor="$(cat "$object/parameter/minor")"
- echo "$object_device $object_minor $object" >> $objects_file
+ echo "$object_device $object_minor $object" >> "$objects_file"
done
- sort -k 1,2 $objects_file | cut -d' ' -f 3
- rm $objects_file
+ sort -k 1,2 "$objects_file" | cut -d' ' -f 3
+ rm "$objects_file"
unset objects_file
unset object
unset object_device
@@ -85,9 +87,9 @@ primary_count=0
for object in $objects; do
device="$(cat "$object/parameter/device")"
if [ "$current_device" != "$device" ]; then
- echo "create_disklabel \"$device\" || die 'Failed to create disklabel for $device'"
+ echo "create_disklabel '$device' || die 'Failed to create disklabel for $device'"
current_device="$device"
- device_name=$(echo ${device} | sed -e 's:^/dev/::;s:/:\\/:g')
+ device_name=$(echo "${device}" | sed -e 's:^/dev/::;s:/:\\/:g')
available_device_size=$(( $(awk "/${device_name}\$/ { print \$3; }" "$partitions") / 1024))
# make sure we don't go past the end of the drive
available_device_size=$((available_device_size - 2))
@@ -108,7 +110,7 @@ for object in $objects; do
if [ "${minor}" -lt "5" ]; then
# Primary partitions
- primary_count=$(( $primary_count + 1 ))
+ primary_count=$(( primary_count + 1 ))
available_size=$available_device_size
else
# Logical partitions
@@ -121,13 +123,13 @@ for object in $objects; do
available_size=0
else
partition_size=$(size_to_mb "$size" "$available_size")
- available_size="$(( $available_size - $partition_size ))"
+ available_size="$(( available_size - partition_size ))"
fi
if [ "${minor}" -lt "5" ]; then
# Primary partitions
available_device_size=$available_size
- if [ "$type" = "extended" -o "$type" = "5" ]; then
+ if [ "$type" = "extended" ] || [ "$type" = "5" ]; then
# Extended partition
available_extended_size=$partition_size
fi
diff --git a/cdist/conf/type/__install_reboot/gencode-remote b/cdist/conf/type/__install_reboot/gencode-remote
index 00c04523..9a6322c1 100755
--- a/cdist/conf/type/__install_reboot/gencode-remote
+++ b/cdist/conf/type/__install_reboot/gencode-remote
@@ -18,8 +18,6 @@
# along with cdist. If not, see .
#
-options="$(cat "$__object/parameter/options")"
-
#echo "reboot $options"
cat << DONE
echo 1 > /proc/sys/kernel/sysrq
diff --git a/cdist/conf/type/__install_reboot/man.rst b/cdist/conf/type/__install_reboot/man.rst
index ecf78ca7..9a53b37a 100644
--- a/cdist/conf/type/__install_reboot/man.rst
+++ b/cdist/conf/type/__install_reboot/man.rst
@@ -18,8 +18,7 @@ None
OPTIONAL PARAMETERS
-------------------
-options
- options to pass to the reboot command. e.g. -f
+None
EXAMPLES
diff --git a/cdist/conf/type/__install_reset_disk/gencode-remote b/cdist/conf/type/__install_reset_disk/gencode-remote
index 947dd472..ac9ae6cf 100755
--- a/cdist/conf/type/__install_reset_disk/gencode-remote
+++ b/cdist/conf/type/__install_reset_disk/gencode-remote
@@ -67,5 +67,5 @@ fi
# erase partition table
dd if=/dev/zero of=$disk bs=512 count=1
-printf 'w\n' | fdisk -u -c $disk || true
+printf 'w\\n' | fdisk -u -c $disk || true
DONE
diff --git a/cdist/conf/type/__install_stage/man.rst b/cdist/conf/type/__install_stage/man.rst
index e33e1e90..fd764693 100644
--- a/cdist/conf/type/__install_stage/man.rst
+++ b/cdist/conf/type/__install_stage/man.rst
@@ -17,9 +17,9 @@ REQUIRED PARAMETERS
uri
The uri from which to fetch the tarball.
Can be anything understood by curl, e.g:
- | http://path/to/stage.tgz
- | tftp:///path/to/stage.tgz
- | file:///local/path/stage.tgz
+ | http://path/to/stage.tgz
+ | tftp:///path/to/stage.tgz
+ | file:///local/path/stage.tgz
OPTIONAL PARAMETERS
diff --git a/cdist/conf/type/__install_umount/parameter/default/target b/cdist/conf/type/__install_umount/parameter/default/target
new file mode 100644
index 00000000..ea8c4bf7
--- /dev/null
+++ b/cdist/conf/type/__install_umount/parameter/default/target
@@ -0,0 +1 @@
+/target
diff --git a/cdist/conf/type/__install_umount/parameter/optional b/cdist/conf/type/__install_umount/parameter/optional
new file mode 100644
index 00000000..eb5a316c
--- /dev/null
+++ b/cdist/conf/type/__install_umount/parameter/optional
@@ -0,0 +1 @@
+target
diff --git a/cdist/conf/type/__iptables_apply/files/init-script b/cdist/conf/type/__iptables_apply/files/init-script
index 2247dcf5..e42017ae 100644
--- a/cdist/conf/type/__iptables_apply/files/init-script
+++ b/cdist/conf/type/__iptables_apply/files/init-script
@@ -1,7 +1,4 @@
#!/bin/sh
-# Nico Schottelius
-# Zürisee, Mon Sep 2 18:38:27 CEST 2013
-#
### BEGIN INIT INFO
# Provides: iptables
# Required-Start: $local_fs $remote_fs
@@ -14,32 +11,72 @@
# and saves/restores previous status
### END INIT INFO
+# Originally written by:
+# Nico Schottelius
+# Zürisee, Mon Sep 2 18:38:27 CEST 2013
+#
+# 2013 Nico Schottelius (nico-cdist at schottelius.org)
+# 2020 Matthias Stecher (matthiasstecher at gmx.de)
+#
+# This file is distributed with cdist and licenced under the
+# GNU GPLv3+ WITHOUT ANY WARRANTY.
+
+
+# Read files and execute the content with the given commands
+#
+# Arguments:
+# 1: Directory
+# 2..n: Commands which should be used to execute the file content
+gothrough() {
+ cd "$1" || return
+ shift
+
+ # iterate through all rules and continue if it's not a file
+ for rule in *; do
+ [ -f "$rule" ] || continue
+ echo "Appling iptables rule $rule ..."
+
+ # execute it with all commands specificed
+ ruleparam="$(cat "$rule")"
+ for cmd in "$@"; do
+ # Command and Rule should be split.
+ # shellcheck disable=SC2046
+ command $cmd $ruleparam
+ done
+ done
+}
+
+# Shortcut for iptables command to do IPv4 and v6
+# only applies to the "reset" target
+iptables() {
+ command iptables "$@"
+ command ip6tables "$@"
+}
basedir=/etc/iptables.d
-status="${basedir}/.pre-start"
+status4="${basedir}/.pre-start"
+status6="${basedir}/.pre-start6"
case $1 in
start)
# Save status
- iptables-save > "$status"
+ iptables-save > "$status4"
+ ip6tables-save > "$status6"
# Apply our ruleset
- cd "$basedir"
- count="$(ls -1 | wc -l)"
-
- # Only do something if there are rules
- if [ "$count" -ge 1 ]; then
- for rule in *; do
- echo "Applying iptables rule $rule ..."
- iptables $(cat "$rule")
- done
- fi
+ gothrough "$basedir" iptables
+ #gothrough "$basedir/v4" iptables # conflicts with $basedir
+ gothrough "$basedir/v6" ip6tables
+ gothrough "$basedir/all" iptables ip6tables
;;
stop)
# Restore from status before, if there is something to restore
- if [ -f "$status" ]; then
- iptables-restore < "$status"
+ if [ -f "$status4" ]; then
+ iptables-restore < "$status4"
+ fi
+ if [ -f "$status6" ]; then
+ ip6tables-restore < "$status6"
fi
;;
restart)
diff --git a/cdist/conf/type/__iptables_apply/man.rst b/cdist/conf/type/__iptables_apply/man.rst
index 76e1f6bf..3bef92cc 100644
--- a/cdist/conf/type/__iptables_apply/man.rst
+++ b/cdist/conf/type/__iptables_apply/man.rst
@@ -10,7 +10,24 @@ DESCRIPTION
-----------
This cdist type deploys an init script that triggers
the configured rules and also re-applies them on
-configuration.
+configuration. Rules are written from __iptables_rule
+into the folder ``/etc/iptables.d/``.
+
+It reads all rules from the base folder as rules for IPv4.
+Rules in the subfolder ``v6/`` are IPv6 rules. Rules in
+the subfolder ``all/`` are applied to both rule tables. All
+files contain the arguments for a single ``iptables`` and/or
+``ip6tables`` command.
+
+Rules are applied in the following order:
+1. All IPv4 rules
+2. All IPv6 rules
+2. All rules that should be applied to both tables
+
+The order of the rules that will be applied are definite
+from the result the shell glob returns, which should be
+alphabetical. If rules must be applied in a special order,
+prefix them with a number like ``02-some-rule``.
REQUIRED PARAMETERS
@@ -24,7 +41,7 @@ None
EXAMPLES
--------
-None (__iptables_apply is used by __iptables_rule)
+None (__iptables_apply is used by __iptables_rule automatically)
SEE ALSO
@@ -35,11 +52,13 @@ SEE ALSO
AUTHORS
-------
Nico Schottelius
+Matthias Stecher
COPYING
-------
-Copyright \(C) 2013 Nico Schottelius. You can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
+Copyright \(C) 2013 Nico Schottelius.
+Copyright \(C) 2020 Matthias Stecher.
+You can redistribute it and/or modify it under the terms of the GNU
+General Public License as published by the Free Software Foundation,
+either version 3 of the License, or (at your option) any later version.
diff --git a/cdist/conf/type/__iptables_rule/man.rst b/cdist/conf/type/__iptables_rule/man.rst
index 92d8859f..afb71e01 100644
--- a/cdist/conf/type/__iptables_rule/man.rst
+++ b/cdist/conf/type/__iptables_rule/man.rst
@@ -11,6 +11,10 @@ DESCRIPTION
This cdist type allows you to manage iptable rules
in a distribution independent manner.
+See :strong:`cdist-type__iptables_apply`\ (7) for the
+execution order of these rules. It will be executed
+automaticly to apply all rules non-volaite.
+
REQUIRED PARAMETERS
-------------------
@@ -25,6 +29,24 @@ state
'present' or 'absent', defaults to 'present'
+BOOLEAN PARAMETERS
+------------------
+All rules without any of these parameters will be treated like ``--v4`` because
+of backward compatibility.
+
+v4
+ Explicitly set it as rule for IPv4. If IPv6 is set, too, it will be
+ threaten like ``--all``. Will be the default if nothing else is set.
+
+v6
+ Explicitly set it as rule for IPv6. If IPv4 is set, too, it will be
+ threaten like ``--all``.
+
+all
+ Set the rule for both IPv4 and IPv6. It will be saved separately from the
+ other rules.
+
+
EXAMPLES
--------
@@ -48,6 +70,16 @@ EXAMPLES
--state absent
+ # IPv4-only rule for ICMPv4
+ __iptables_rule icmp-v4 --v4 --rule "-A INPUT -p icmp -j ACCEPT"
+ # IPv6-only rule for ICMPv6
+ __iptables_rule icmp-v6 --v6 --rule "-A INPUT -p icmpv6 -j ACCEPT"
+
+ # doing something for the dual stack
+ __iptables_rule fwd-eth0-eth1 --v4 --v6 --rule "-A INPUT -i eth0 -o eth1 -j ACCEPT"
+ __iptables_rule fwd-eth1-eth0 --all --rule "-A -o eth1 -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT"
+
+
SEE ALSO
--------
:strong:`cdist-type__iptables_apply`\ (7), :strong:`iptables`\ (8)
@@ -56,11 +88,13 @@ SEE ALSO
AUTHORS
-------
Nico Schottelius
+Matthias Stecher
COPYING
-------
-Copyright \(C) 2013 Nico Schottelius. You can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
+Copyright \(C) 2013 Nico Schottelius.
+Copyright \(C) 2020 Matthias Stecher.
+You can redistribute it and/or modify it under the terms of the GNU
+General Public License as published by the Free Software Foundation,
+either version 3 of the License, or (at your option) any later version.
diff --git a/cdist/conf/type/__iptables_rule/manifest b/cdist/conf/type/__iptables_rule/manifest
index ed78787f..d4394c25 100755
--- a/cdist/conf/type/__iptables_rule/manifest
+++ b/cdist/conf/type/__iptables_rule/manifest
@@ -1,6 +1,7 @@
#!/bin/sh -e
#
# 2013 Nico Schottelius (nico-cdist at schottelius.org)
+# 2020 Matthias Stecher (matthiasstecher at gmx.de)
#
# This file is part of cdist.
#
@@ -24,12 +25,36 @@ base_dir=/etc/iptables.d
name="$__object_id"
state="$(cat "$__object/parameter/state")"
+if [ -f "$__object/parameter/v4" ]; then
+ only_v4="yes"
+ # $specific_dir is $base_dir
+fi
+if [ -f "$__object/parameter/v6" ]; then
+ only_v6="yes"
+ specific_dir="$base_dir/v6"
+fi
+# If rules should be set for both protocols
+if { [ "$only_v4" = "yes" ] && [ "$only_v6" = "yes" ]; } ||
+ [ -f "$__object/parameter/all" ]; then
+
+ # all to a specific directory
+ specific_dir="$base_dir/all"
+fi
+
+# set rule directory based on if it's the base or subdirectory
+rule_dir="${specific_dir:-$base_dir}"
+
################################################################################
# Basic setup
#
__directory "$base_dir" --state present
+# sub-directory if required
+if [ "$specific_dir" ]; then
+ require="__directory/$base_dir" __directory "$specific_dir" --state present
+fi
+
# Have apply do the real job
require="$__object_name" __iptables_apply
@@ -37,6 +62,15 @@ require="$__object_name" __iptables_apply
# The rule
#
-require="__directory/$base_dir" __file "$base_dir/${name}" \
- --source "$__object/parameter/rule" \
- --state "$state"
+for dir in "$base_dir" "$base_dir/v6" "$base_dir/all"; do
+ # defaults to absent except the directory that should contain the file
+ if [ "$rule_dir" = "$dir" ]; then
+ curr_state="$state"
+ else
+ curr_state="absent"
+ fi
+
+ require="__directory/$rule_dir" __file "$dir/$name" \
+ --source "$__object/parameter/rule" \
+ --state "$curr_state"
+done
diff --git a/cdist/conf/type/__iptables_rule/parameter/boolean b/cdist/conf/type/__iptables_rule/parameter/boolean
new file mode 100644
index 00000000..76882272
--- /dev/null
+++ b/cdist/conf/type/__iptables_rule/parameter/boolean
@@ -0,0 +1,3 @@
+all
+v4
+v6
diff --git a/cdist/conf/type/__issue/manifest b/cdist/conf/type/__issue/manifest
index 06eb120a..0f0b3d83 100755
--- a/cdist/conf/type/__issue/manifest
+++ b/cdist/conf/type/__issue/manifest
@@ -25,6 +25,9 @@ os="$(cat "$__global/explorer/os")"
if [ -f "$__object/parameter/source" ]; then
source="$(cat "$__object/parameter/source")"
+ if [ "$source" = "-" ]; then
+ source="${__object}/stdin"
+ fi
else
case "$os" in
archlinux|redhat)
diff --git a/cdist/conf/type/__jail/manifest b/cdist/conf/type/__jail/manifest
index c3d9dfbe..fad6a3a1 100755
--- a/cdist/conf/type/__jail/manifest
+++ b/cdist/conf/type/__jail/manifest
@@ -35,16 +35,15 @@ fi
jaildir="$(cat "$__object/parameter/jaildir")"
-__directory ${jaildir} --parents
+__directory "${jaildir}" --parents
-set -- "$@" "$__object_id" "--state" "$state"
+set -- "$@" "$__object_id"
cd "$__object/parameter"
-for property in $(ls .); do
+for property in *; do
set -- "$@" "--$property" "$(cat "$property")"
done
-ver="$(cat "$__global/explorer/os_version")"
-if [ -n "$(echo "$ver" | grep '^10\.' )" ]; then # Version is 10.x
+if grep -q '^10\.' "$(cat "$__global/explorer/os_version")" ; then # Version is 10.x
__jail_freebsd10 "$@"
else
__jail_freebsd9 "$@"
diff --git a/cdist/conf/type/__jail_freebsd10/explorer/status b/cdist/conf/type/__jail_freebsd10/explorer/status
index 1ceba212..c8039f21 100755
--- a/cdist/conf/type/__jail_freebsd10/explorer/status
+++ b/cdist/conf/type/__jail_freebsd10/explorer/status
@@ -39,7 +39,7 @@ fi
# backslash-escaped $jaildir
sjaildir="$(echo ${jaildir} | sed 's#/#\\/#g')"
-jls_output="$(jls | grep "[ ]${sjaildir}\/${name}\$")" || true
+jls_output="$(jls | grep "[ ]${sjaildir}\\/${name}\$")" || true
if [ -n "${jls_output}" ]; then
echo "STARTED"
diff --git a/cdist/conf/type/__jail_freebsd10/gencode-local b/cdist/conf/type/__jail_freebsd10/gencode-local
index b2016f86..f163cad3 100755
--- a/cdist/conf/type/__jail_freebsd10/gencode-local
+++ b/cdist/conf/type/__jail_freebsd10/gencode-local
@@ -44,7 +44,7 @@ basepresent="$(cat "$__object/explorer/basepresent")"
if [ "$state" = "present" ]; then
if [ "$basepresent" = "NONE" ]; then
# IPv6 fix
- if $(echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$')
+ if echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$'
then
my_target_host="[${__target_host}]"
else
diff --git a/cdist/conf/type/__jail_freebsd10/gencode-remote b/cdist/conf/type/__jail_freebsd10/gencode-remote
index 76241e0e..4f376c25 100755
--- a/cdist/conf/type/__jail_freebsd10/gencode-remote
+++ b/cdist/conf/type/__jail_freebsd10/gencode-remote
@@ -36,7 +36,7 @@ state="$(cat "$__object/parameter/state")"
started="true"
# If the user wants the jail gone, it implies it shouldn't be started.
-[ -f "$__object/parameter/stopped" -o "$state" = "absent" ] && started="false"
+{ [ -f "$__object/parameter/stopped" ] || [ "$state" = "absent" ]; } && started="false"
if [ -f "$__object/parameter/ip" ]; then
ip="$(cat "$__object/parameter/ip")"
@@ -45,7 +45,7 @@ else
# when $state=present, it's required. Enforce this.
if [ "$state" = "present" ]; then
exec >&2
- echo "If --state is 'present,' --ip must be given\!"
+ printf 'If --state is "present", --ip must be given\!\n'
exit 1
fi
fi
@@ -66,7 +66,7 @@ devfsruleset="$(cat "$__object/parameter/devfs-ruleset")"
# devfs_ruleset being defined without devfs_enable being true
# is pointless. Treat this as an error.
-if [ -n "$devfsruleset" -a "$devfsenable" = "false" ]; then
+if [ -n "$devfsruleset" ] && [ "$devfsenable" = "false" ]; then
exec >&2
echo "Can't have --devfs-ruleset defined with --devfs-disable"
exit 1
@@ -83,12 +83,12 @@ present="$(cat "$__object/explorer/present")"
status="$(cat "$__object/explorer/status")"
# Handle ip="addr, addr" format
-if [ $(expr "${ip}" : ".*, .*") -gt "0" ]; then
+if [ "$(expr "${ip}" : ".*, .*")" -gt "0" ]; then
SAVE_IFS="$IFS"
IFS=", "
for cur_ip in ${ip}; do
# Just get the last IP address for SSH to listen on
- mgmt_ip=$(echo "${ip}" | cut '-d ' -f1) # In case using "ip netmask" format rather than CIDR
+ mgmt_ip=$(echo "${cur_ip}" | cut '-d ' -f1) # In case using "ip netmask" format rather than CIDR
done
IFS="$SAVE_IFS"
else
@@ -114,19 +114,19 @@ startJail() {
deleteJail() {
# Unmount the jail's mountpoints if necessary
cat <=1 rw mount is mounted still
- for DIR in "${output}"; do
- umount -F "/etc/fstab.${name}" "\$(echo "${DIR}" | awk '{print $3}')"
+ for DIR in "\${output}"; do
+ umount -F "/etc/fstab.${name}" "\$(echo "${DIR}" | awk '{print \$3}')"
done
fi
- output="\$(mount | grep "\/${name} (")" || true
+ output="\$(mount | grep "\\/${name} (")" || true
if [ -n "\${output}" ]; then # ro mount is mounted still
- umount -F "/etc/fstab.${name}" "\$(echo "${output}" | awk '{print $3}')"
+ umount -F "/etc/fstab.${name}" "\$(echo "\${output}" | awk '{print \$3}')"
fi
EOF
# Remove the jail's rw mountpoints
@@ -275,9 +275,9 @@ cat <&1 >/dev/null # Close the FD==fail...
@@ -290,7 +290,7 @@ add include \\\$devfsrules_unhide_basic
add include \\\$devfsrules_unhide_login
END
fi
- devfsruleset_num=\$(grep "\[${devfsruleset}=" /etc/devfs.rules | sed -n 's/\[.*=\([0-9]*\)\]/\1/pg')
+ devfsruleset_num=\$(grep "\\[${devfsruleset}=" /etc/devfs.rules | sed -n 's/\\[.*=\\([0-9]*\\)\\]/\\1/pg')
if [ -n "\$devfsruleset_num" ]; then
jaildata="\$jaildata
devfs_ruleset=\"\${devfsruleset_num}\";"
@@ -298,8 +298,8 @@ END
fi
EOF
-
- echo "printf \"%s\\n%s\n%s\n\" \"\$jailheader\" \"\$jaildata\" \"\$jailtrailer\" >>\"\$jailfile\""
+ # shellcheck disable=SC2028
+ echo "printf \"%s\\n%s\\n%s\\n\" \"\$jailheader\" \"\$jaildata\" \"\$jailtrailer\" >>\"\$jailfile\""
# Add $name to jail_list if $onboot=yes
if [ "$onboot" = "yes" ]; then
diff --git a/cdist/conf/type/__jail_freebsd9/explorer/status b/cdist/conf/type/__jail_freebsd9/explorer/status
index 1ceba212..c8039f21 100755
--- a/cdist/conf/type/__jail_freebsd9/explorer/status
+++ b/cdist/conf/type/__jail_freebsd9/explorer/status
@@ -39,7 +39,7 @@ fi
# backslash-escaped $jaildir
sjaildir="$(echo ${jaildir} | sed 's#/#\\/#g')"
-jls_output="$(jls | grep "[ ]${sjaildir}\/${name}\$")" || true
+jls_output="$(jls | grep "[ ]${sjaildir}\\/${name}\$")" || true
if [ -n "${jls_output}" ]; then
echo "STARTED"
diff --git a/cdist/conf/type/__jail_freebsd9/gencode-local b/cdist/conf/type/__jail_freebsd9/gencode-local
index 1ab7ff1a..bbdc9fcc 100755
--- a/cdist/conf/type/__jail_freebsd9/gencode-local
+++ b/cdist/conf/type/__jail_freebsd9/gencode-local
@@ -40,7 +40,7 @@ basepresent="$(cat "$__object/explorer/basepresent")"
if [ "$state" = "present" ]; then
if [ "$basepresent" = "NONE" ]; then
# IPv6 fix
- if $(echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$')
+ if echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$'
then
my_target_host="[${__target_host}]"
else
diff --git a/cdist/conf/type/__jail_freebsd9/gencode-remote b/cdist/conf/type/__jail_freebsd9/gencode-remote
index 63b48e5c..68229d3e 100755
--- a/cdist/conf/type/__jail_freebsd9/gencode-remote
+++ b/cdist/conf/type/__jail_freebsd9/gencode-remote
@@ -36,7 +36,7 @@ state="$(cat "$__object/parameter/state")"
started="true"
# If the user wants the jail gone, it implies it shouldn't be started.
-[ -f "$__object/parameter/stopped" -o "$state" = "absent" ] && started="false"
+{ [ -f "$__object/parameter/stopped" ] || [ "$state" = "absent" ]; } && started="false"
if [ -f "$__object/parameter/ip" ]; then
ip="$(cat "$__object/parameter/ip")"
@@ -45,7 +45,7 @@ else
# when $state=present, it's required. Enforce this.
if [ "$state" = "present" ]; then
exec >&2
- echo "If --state is 'present,' --ip must be given\!"
+ printf 'If --state is "present", --ip must be given\!\n'
exit 1
fi
fi
@@ -70,7 +70,7 @@ devfsruleset="$(cat "$__object/parameter/devfs-ruleset")"
# devfs_ruleset being defined without devfs_enable being true
# is pointless. Treat this as an error.
-if [ -n "$devfsruleset" -a "$devfsenable" = "false" ]; then
+if [ -n "$devfsruleset" ] && [ "$devfsenable" = "false" ]; then
exec >&2
echo "Can't have --devfs-ruleset defined with --devfs-disable"
exit 1
@@ -86,14 +86,14 @@ present="$(cat "$__object/explorer/present")"
status="$(cat "$__object/explorer/status")"
# Handle ip="iface|addr, iface|addr" format
-if [ $(expr "${ip}" : ".*|.*") -gt "0" ]; then
+if [ "$(expr "${ip}" : ".*|.*")" -gt "0" ]; then
# If we have multiple IPs defined, $interface doesn't make sense because ip="iface|addr, iface|addr" implies it
interface=""
SAVE_IFS="$IFS"
IFS=", "
for cur_ip in ${ip}; do
# Just get the last IP address for SSH to listen on
- mgmt_ip=$(echo "${ip}" | sed -E -e 's/^.*\|(.*)\/[0-9]+$/\1/')
+ mgmt_ip=$(echo "${cur_ip}" | sed -E -e 's/^.*\|(.*)\/[0-9]+$/\1/')
done
IFS="$SAVE_IFS"
else
@@ -119,19 +119,19 @@ startJail() {
deleteJail() {
# Unmount the jail's mountpoints if necessary
cat <=1 rw mount is mounted still
- for DIR in "${output}"; do
- umount -F "/etc/fstab.${name}" "\$(echo "${DIR}" | awk '{print $3}')"
+ for DIR in "\${output}"; do
+ umount -F "/etc/fstab.${name}" "\$(echo "${DIR}" | awk '{print \$3}')"
done
fi
- output="\$(mount | grep "\/${name} (")" || true
+ output="\$(mount | grep "\\/${name} (")" || true
if [ -n "\${output}" ]; then # ro mount is mounted still
- umount -F "/etc/fstab.${name}" "\$(echo "${output}" | awk '{print $3}')"
+ umount -F "/etc/fstab.${name}" "\$(echo "\${output}" | awk '{print \$3}')"
fi
EOF
# Remove the jail's rw mountpoints
@@ -279,9 +279,9 @@ END
if [ ! -f /etc/devfs.rules ]; then
touch /etc/devfs.rules
fi
- if [ -z "\$(grep '\[jailrules=' /etc/devfs.rules)" ]; then # The default ruleset doesn't exist
+ if [ -z "\$(grep '\\[jailrules=' /etc/devfs.rules)" ]; then # The default ruleset doesn't exist
# Get the highest-numbered ruleset
- highest="\$(sed -n 's/\[.*=\([0-9]*\)\]/\1/pg' /etc/devfs.rules | sort -u | tail -n 1)" || true
+ highest="\$(sed -n 's/\\[.*=\\([0-9]*\\)\\]/\\1/pg' /etc/devfs.rules | sort -u | tail -n 1)" || true
# increment by 1
let num="\${highest}+1" 2>&- >&-
# add default ruleset
diff --git a/cdist/conf/type/__key_value/explorer/state b/cdist/conf/type/__key_value/explorer/state
index b990733d..d24600af 100755
--- a/cdist/conf/type/__key_value/explorer/state
+++ b/cdist/conf/type/__key_value/explorer/state
@@ -19,9 +19,9 @@
# along with cdist. If not, see .
#
-export key="$(cat "$__object/parameter/key" 2>/dev/null \
+key="$(cat "$__object/parameter/key" 2>/dev/null \
|| echo "$__object_id")"
-export state="$(cat "$__object/parameter/state")"
+state="$(cat "$__object/parameter/state")"
file="$(cat "$__object/parameter/file")"
@@ -30,16 +30,19 @@ if [ ! -f "$file" ]; then
exit
fi
-export delimiter="$(cat "$__object/parameter/delimiter")"
-export value="$(cat "$__object/parameter/value" 2>/dev/null \
+delimiter="$(cat "$__object/parameter/delimiter")"
+value="$(cat "$__object/parameter/value" 2>/dev/null \
|| echo "__CDIST_NOTSET__")"
if [ -f "$__object/parameter/exact_delimiter" ]; then
- export exact_delimiter=1
+ exact_delimiter=1
else
- export exact_delimiter=0
+ exact_delimiter=0
fi
+export key state delimiter value exact_delimiter
-awk -f - "$file" <<"AWK_EOF"
+awk_bin=$(PATH=$(getconf PATH 2>/dev/null) && command -v awk || echo awk)
+
+"${awk_bin}" -f - "$file" <<"AWK_EOF"
BEGIN {
state=ENVIRON["state"]
key=ENVIRON["key"]
diff --git a/cdist/conf/type/__key_value/files/remote_script.sh b/cdist/conf/type/__key_value/files/remote_script.sh
index 52b3f2de..faf080cb 100644
--- a/cdist/conf/type/__key_value/files/remote_script.sh
+++ b/cdist/conf/type/__key_value/files/remote_script.sh
@@ -1,19 +1,21 @@
#!/bin/sh
-export key="$(cat "$__object/parameter/key" 2>/dev/null \
+key="$(cat "$__object/parameter/key" 2>/dev/null \
|| echo "$__object_id")"
-export state="$(cat "$__object/parameter/state")"
+state="$(cat "$__object/parameter/state")"
file="$(cat "$__object/parameter/file")"
-export delimiter="$(cat "$__object/parameter/delimiter")"
-export value="$(cat "$__object/parameter/value" 2>/dev/null \
+delimiter="$(cat "$__object/parameter/delimiter")"
+value="$(cat "$__object/parameter/value" 2>/dev/null \
|| echo "__CDIST_NOTSET__")"
+export key state delimiter value
if [ -f "$__object/parameter/exact_delimiter" ]; then
- export exact_delimiter=1
+ exact_delimiter=1
else
- export exact_delimiter=0
+ exact_delimiter=0
fi
+export exact_delimiter
tmpfile=$(mktemp "${file}.cdist.XXXXXXXXXX")
# preserve ownership and permissions by copying existing file over tmpfile
@@ -22,7 +24,10 @@ if [ -f "$file" ]; then
else
touch "$file"
fi
-awk -f - "$file" >"$tmpfile" <<"AWK_EOF"
+
+awk_bin=$(PATH=$(getconf PATH 2>/dev/null) && command -v awk || echo awk)
+
+"${awk_bin}" -f - "$file" >"$tmpfile" <<"AWK_EOF"
BEGIN {
# import variables in a secure way ..
state=ENVIRON["state"]
diff --git a/cdist/conf/type/__key_value/gencode-remote b/cdist/conf/type/__key_value/gencode-remote
index 7a60f94b..1174400e 100755
--- a/cdist/conf/type/__key_value/gencode-remote
+++ b/cdist/conf/type/__key_value/gencode-remote
@@ -23,13 +23,14 @@
state_should="$(cat "$__object/parameter/state")"
state_is="$(cat "$__object/explorer/state")"
+fire_onchange=''
-if [ "$state_is" = "$state_should" ]; then
+if [ "$state_is" = "$state_should" ]; then
exit 0
fi
# here we check only if the states are valid,
-# emmit messages and
+# emit messages and
# let awk do the work ...
case "$state_should" in
absent)
@@ -39,6 +40,7 @@ case "$state_should" in
;;
wrongformat|wrongvalue|present)
echo "remove" >> "$__messages_out"
+ fire_onchange=1
;;
*)
echo "Unknown explorer state: $state_is" >&2
@@ -50,12 +52,15 @@ case "$state_should" in
case "$state_is" in
nosuchfile)
echo "create" >> "$__messages_out"
+ fire_onchange=1
;;
absent)
echo "insert" >> "$__messages_out"
+ fire_onchange=1
;;
wrongformated|wrongvalue)
echo "change" >> "$__messages_out"
+ fire_onchange=1
;;
present)
# nothing to do
@@ -67,9 +72,13 @@ case "$state_should" in
esac
;;
*)
- echo "Unknown state: $state_should" >&2
- exit 1
+ echo "Unknown state: $state_should" >&2
+ exit 1
;;
esac
cat "$__type/files/remote_script.sh"
+
+if [ -n "$fire_onchange" ]; then
+ cat "$__object/parameter/onchange"
+fi
diff --git a/cdist/conf/type/__key_value/man.rst b/cdist/conf/type/__key_value/man.rst
index f069d989..34e4aab2 100644
--- a/cdist/conf/type/__key_value/man.rst
+++ b/cdist/conf/type/__key_value/man.rst
@@ -34,6 +34,8 @@ comment
but only if the key or value must be changed.
You need to ensure yourself that the line is prefixed with the correct
comment sign. (for example # or ; or wathever ..)
+onchange
+ The code to run if the key or value changes (i.e. is inserted, removed or replaced).
BOOLEAN PARAMETERS
diff --git a/cdist/conf/type/__key_value/manifest b/cdist/conf/type/__key_value/manifest
index c7801c89..5a91f60c 100755
--- a/cdist/conf/type/__key_value/manifest
+++ b/cdist/conf/type/__key_value/manifest
@@ -21,7 +21,7 @@
state_should="$(cat "$__object/parameter/state")"
-if [ "$state_should" = "present" -a ! -f "$__object/parameter/value" ]; then
+if [ "$state_should" = "present" ] && [ ! -f "$__object/parameter/value" ]; then
echo "Missing required parameter 'value'" >&2
exit 1
fi
diff --git a/cdist/conf/type/__key_value/parameter/default/onchange b/cdist/conf/type/__key_value/parameter/default/onchange
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__key_value/parameter/optional b/cdist/conf/type/__key_value/parameter/optional
index 666be2ae..d4b8cac0 100644
--- a/cdist/conf/type/__key_value/parameter/optional
+++ b/cdist/conf/type/__key_value/parameter/optional
@@ -2,3 +2,4 @@ key
value
state
comment
+onchange
diff --git a/cdist/conf/type/__letsencrypt_cert/explorer/certbot-path b/cdist/conf/type/__letsencrypt_cert/explorer/certbot-path
new file mode 100755
index 00000000..3c6076df
--- /dev/null
+++ b/cdist/conf/type/__letsencrypt_cert/explorer/certbot-path
@@ -0,0 +1,3 @@
+#!/bin/sh -e
+
+command -v certbot 2>/dev/null || true
diff --git a/cdist/conf/type/__letsencrypt_cert/explorer/certificate-domains b/cdist/conf/type/__letsencrypt_cert/explorer/certificate-domains
new file mode 100755
index 00000000..db605b63
--- /dev/null
+++ b/cdist/conf/type/__letsencrypt_cert/explorer/certificate-domains
@@ -0,0 +1,8 @@
+#!/bin/sh -e
+
+certbot_path=$("${__type_explorer}/certbot-path")
+if [ -n "${certbot_path}" ]
+then
+ certbot certificates --cert-name "${__object_id:?}" | grep ' Domains: ' | \
+ cut -d ' ' -f 6- | tr ' ' '\n'
+fi
diff --git a/cdist/conf/type/__letsencrypt_cert/explorer/certificate-exists b/cdist/conf/type/__letsencrypt_cert/explorer/certificate-exists
new file mode 100755
index 00000000..4e6f44db
--- /dev/null
+++ b/cdist/conf/type/__letsencrypt_cert/explorer/certificate-exists
@@ -0,0 +1,13 @@
+#!/bin/sh -e
+
+certbot_path=$("${__type_explorer}/certbot-path")
+if [ -n "${certbot_path}" ]
+then
+ if certbot certificates | grep -q " Certificate Name: ${__object_id:?}$"; then
+ echo yes
+ else
+ echo no
+ fi
+else
+ echo no
+fi
diff --git a/cdist/conf/type/__letsencrypt_cert/explorer/certificate-is-test b/cdist/conf/type/__letsencrypt_cert/explorer/certificate-is-test
new file mode 100755
index 00000000..9b445059
--- /dev/null
+++ b/cdist/conf/type/__letsencrypt_cert/explorer/certificate-is-test
@@ -0,0 +1,14 @@
+#!/bin/sh -e
+
+certbot_path=$("${__type_explorer}/certbot-path")
+if [ -n "${certbot_path}" ]
+then
+ if certbot certificates --cert-name "${__object_id:?}" | \
+ grep -q 'INVALID: TEST_CERT'; then
+ echo yes
+ else
+ echo no
+ fi
+else
+ echo no
+fi
diff --git a/cdist/conf/type/__letsencrypt_cert/explorer/exists b/cdist/conf/type/__letsencrypt_cert/explorer/exists
deleted file mode 100644
index cb967663..00000000
--- a/cdist/conf/type/__letsencrypt_cert/explorer/exists
+++ /dev/null
@@ -1,5 +0,0 @@
-domain=$__object_id
-
-if [ -f "/etc/letsencrypt/live/$domain/fullchain.pem" ]; then
- echo yes
-fi
diff --git a/cdist/conf/type/__letsencrypt_cert/files/gen_hook.sh b/cdist/conf/type/__letsencrypt_cert/files/gen_hook.sh
new file mode 100644
index 00000000..81ea4856
--- /dev/null
+++ b/cdist/conf/type/__letsencrypt_cert/files/gen_hook.sh
@@ -0,0 +1,84 @@
+#!/bin/sh -e
+
+# It is expected that this defines hook_contents
+
+# Reasonable defaults
+hook_source="${__object}/parameter/${hook}-hook"
+hook_state="absent"
+hook_contents_head="#!/bin/sh -e"
+hook_contents_logic=""
+hook_contents_tail=""
+
+# Backwards compatibility
+# Remove this when renew-hook is removed
+# Falling back to renew-hook if deploy-hook is not passed
+if [ "${hook}" = "deploy" ] && [ ! -f "${hook_source}" ]; then
+ hook_source="${__object}/parameter/renew-hook"
+fi
+if [ "${state}" = "present" ] && \
+ [ -f "${hook_source}" ]; then
+ # This hook is to be installed, let's generate it with some
+ # safety boilerplate
+ # Since certbot runs all hooks for all renewal processes
+ # (at each state for deploy, pre, post), it is up to us to
+ # differentiate whether or not the hook must run
+ hook_state="present"
+ hook_contents_head="$(cat <> /dev/stderr
+ exit 1
+ ;;
+ esac
+
+ hook_contents_tail="$(cat <> "${__messages_out:?}"
+ ;;
+ present)
+ domain_param_file="${__object}/parameter/domain"
+ requested_domains=$(mktemp "${TMPDIR:-/tmp}/domain.cdist.XXXXXXXXXX")
+ if [ -f "${domain_param_file}" ]; then
+ cp "${domain_param_file}" "${requested_domains}"
+ else
+ echo "$__object_id" >> "${requested_domains}"
+ fi
+
+ staging=no
+ if [ -f "${__object}/parameter/staging" ]; then
+ staging=yes
+ fi
+
+ if [ "${certificate_exists}" = "yes" ]; then
+ existing_domains="${__object}/explorer/certificate-domains"
+ certificate_is_test=$(cat "${__object}/explorer/certificate-is-test")
+
+ sort -uo "${requested_domains}" "${requested_domains}"
+ sort -uo "${existing_domains}" "${existing_domains}"
+
+ if [ -z "$(comm -23 "${requested_domains}" "${existing_domains}")" ] && \
+ [ "${certificate_is_test}" = "${staging}" ]; then
+ exit 0
+ fi
+ fi
+
+ admin_email="$(cat "$__object/parameter/admin-email")"
+ webroot="$(cat "$__object/parameter/webroot")"
+
+ cat <<-EOF
+ certbot certonly \
+ --agree-tos \
+ --cert-name '${name}' \
+ --email '${admin_email}' \
+ --expand \
+ --non-interactive \
+ --quiet \
+ $(if [ "${staging}" = "yes" ]; then
+ echo "--staging"
+ elif [ "${certificate_is_test}" != "${staging}" ]; then
+ echo "--force-renewal"
+ fi) \
+ $(if [ -z "${webroot}" ]; then
+ echo "--standalone"
+ else
+ echo "--webroot --webroot-path '${webroot}'"
+ fi) \
+ $(while read -r domain; do
+ echo "--domain '${domain}' \\"
+ done < "${requested_domains}")
+ EOF
+ rm -f "${requested_domains}"
+
+ if [ "${certificate_exists}" = "no" ]; then
+ echo create >> "${__messages_out}"
+ else
+ echo change >> "${__messages_out}"
+ fi
+ ;;
+ *)
+ echo "Unsupported state: ${state}" >&2
+
+ exit 1
+ ;;
+esac
diff --git a/cdist/conf/type/__letsencrypt_cert/man.rst b/cdist/conf/type/__letsencrypt_cert/man.rst
index 14dbac7b..43be8424 100644
--- a/cdist/conf/type/__letsencrypt_cert/man.rst
+++ b/cdist/conf/type/__letsencrypt_cert/man.rst
@@ -1,47 +1,171 @@
cdist-type__letsencrypt_cert(7)
===============================
+
NAME
----
+
cdist-type__letsencrypt_cert - Get an SSL certificate from Let's Encrypt
DESCRIPTION
-----------
-Automatically obtain a Let's Encrypt SSL certificate. Uses certbot's webroot
-method. You must set up your web server to work with webroot.
+
+Automatically obtain a Let's Encrypt SSL certificate using Certbot.
+
+This type attempts to setup automatic renewals always. In many Linux
+distributions, that is the case out of the box, see:
+https://certbot.eff.org/docs/using.html#automated-renewals
+
+For Alpine Linux and Arch Linux, we setup a system-wide cronjob that
+attempts to renew certificates daily.
+
+If you are using FreeBSD, we configure periodic(8) as recommended by
+the port mantainer, so there will be a weekly attempt at renewal.
+
+If your OS is not mentioned here or on Certbot's docs as having
+support for automated renewals, please make sure you check your OS
+and possibly patch this type so the system-wide cronjob is installed.
REQUIRED PARAMETERS
-------------------
-webroot
- The path to your webroot, as set up in your webserver config.
+
+object id
+ A cert name. If domain parameter is not specified then it is used
+ as a domain to be included in the certificate.
admin-email
- Where to send Let's Encrypt emails like "certificate needs renewal".
+ Where to send Let's Encrypt emails like "certificate needs renewal".
OPTIONAL PARAMETERS
-------------------
-None.
+
+state
+ 'present' or 'absent', defaults to 'present' where:
+
+ present
+ if the certificate does not exist, it will be obtained
+ absent
+ the certificate will be removed
+
+webroot
+ The path to your webroot, as set up in your webserver config. If this
+ parameter is not present, Certbot will be run in standalone mode.
+
+
+OPTIONAL MULTIPLE PARAMETERS
+----------------------------
+
+domain
+ Domains to be included in the certificate. When specified then object id
+ is not used as a domain.
+
+deploy-hook
+ Command to be executed only when the certificate associated with this
+ ``$__object_id`` is issued or renewed.
+ You can specify it multiple times, but any failure will prevent further
+ commands from being executed.
+
+ For this command, the
+ shell variable ``$RENEWED_LINEAGE`` will point to the
+ config live subdirectory (for example,
+ ``/etc/letsencrypt/live/${__object_id}``) containing the
+ new certificates and keys; the shell variable
+ ``$RENEWED_DOMAINS`` will contain a space-delimited list
+ of renewed certificate domains (for example,
+ ``example.com www.example.com``)
+
+pre-hook
+ Command to be run in a shell before obtaining any
+ certificates.
+ You can specify it multiple times, but any failure will prevent further
+ commands from being executed.
+
+ Note these run regardless of which certificate is attempted, you may want to
+ manage these system-wide hooks with ``__file`` in
+ ``/etc/letsencrypt/renewal-hooks/pre/``.
+
+ Intended primarily for renewal, where it
+ can be used to temporarily shut down a webserver that
+ might conflict with the standalone plugin. This will
+ only be called if a certificate is actually to be
+ obtained/renewed.
+
+post-hook
+ Command to be run in a shell after attempting to
+ obtain/renew certificates.
+ You can specify it multiple times, but any failure will prevent further
+ commands from being executed.
+
+ Note these run regardless of which certificate was attempted, you may want to
+ manage these system-wide hooks with ``__file`` in
+ ``/etc/letsencrypt/renewal-hooks/post/``.
+
+ Can be used to deploy
+ renewed certificates, or to restart any servers that
+ were stopped by --pre-hook. This is only run if an
+ attempt was made to obtain/renew a certificate.
+
+
+BOOLEAN PARAMETERS
+------------------
+
+staging
+ Obtain a test certificate from a staging server.
+
+
+MESSAGES
+--------
+
+change
+ Certificate was changed.
+
+create
+ Certificate was created.
+
+remove
+ Certificate was removed.
+
EXAMPLES
--------
.. code-block:: sh
- __letsencrypt_cert example.com --webroot /data/letsencrypt/root
+ # use object id as domain
+ __letsencrypt_cert example.com \
+ --admin-email root@example.com \
+ --deploy-hook "service nginx reload" \
+ --webroot /data/letsencrypt/root
+.. code-block:: sh
+
+ # domain parameter is specified so object id is not used as domain
+ # and example.com needs to be included again with domain parameter
+ __letsencrypt_cert example.com \
+ --admin-email root@example.com \
+ --domain example.com \
+ --domain foo.example.com \
+ --domain bar.example.com \
+ --deploy-hook "service nginx reload" \
+ --webroot /data/letsencrypt/root
AUTHORS
-------
-Nico Schottelius
-Kamila Součková
+
+| Nico Schottelius
+| Kamila Součková
+| Darko Poljak
+| Ľubomír Kučera
+| Evilham
COPYING
-------
-Copyright \(C) 2017 Nico Schottelius, Kamila Součková. You can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
+
+Copyright \(C) 2017-2021 Nico Schottelius, Kamila Součková, Darko Poljak and
+Ľubomír Kučera. You can redistribute it and/or modify it under the terms of
+the GNU General Public License as published by the Free Software Foundation,
+either version 3 of the License, or (at your option) any later version.
diff --git a/cdist/conf/type/__letsencrypt_cert/manifest b/cdist/conf/type/__letsencrypt_cert/manifest
index c9a688ca..1df3574a 100644
--- a/cdist/conf/type/__letsencrypt_cert/manifest
+++ b/cdist/conf/type/__letsencrypt_cert/manifest
@@ -1,72 +1,158 @@
-os=$(cat "$__global/explorer/os")
-os_version=$(cat "$__global/explorer/os_version")
+#!/bin/sh
+certbot_fullpath="$(cat "${__object:?}/explorer/certbot-path")"
+state=$(cat "${__object}/parameter/state")
+os="$(cat "${__global:?}/explorer/os")"
+
+if [ -z "${certbot_fullpath}" ]; then
+ os_version="$(cat "${__global}/explorer/os_version")"
+ # Use this, very common value, as a default. It is OS-dependent
+ certbot_fullpath="/usr/bin/certbot"
+ case "$os" in
+ archlinux)
+ __package certbot
+ ;;
+ alpine)
+ __package certbot
+ ;;
+ debian)
+ case "$os_version" in
+ 8*)
+ __apt_source jessie-backports \
+ --uri http://http.debian.net/debian \
+ --distribution jessie-backports \
+ --component main
+
+ require="__apt_source/jessie-backports" __package_apt python-certbot \
+ --target-release jessie-backports
+ require="__apt_source/jessie-backports" __package_apt certbot \
+ --target-release jessie-backports
+ # Seems to be a missing dependency on debian 8
+ __package python-ndg-httpsclient
+ ;;
+ 9*)
+ __apt_source stretch-backports \
+ --uri http://http.debian.net/debian \
+ --distribution stretch-backports \
+ --component main
+
+ require="__apt_source/stretch-backports" __package_apt python-certbot \
+ --target-release stretch-backports
+ require="__apt_source/stretch-backports" __package_apt certbot \
+ --target-release stretch-backports
+ ;;
+ 10*)
+ __package_apt certbot
+ ;;
+
+ *)
+ echo "Unsupported OS version: $os_version" >&2
+ exit 1
+ ;;
+ esac
+ ;;
+ devuan)
+ case "$os_version" in
+ jessie)
+ __apt_source jessie-backports \
+ --uri http://auto.mirror.devuan.org/merged \
+ --distribution jessie-backports \
+ --component main
+
+ require="__apt_source/jessie-backports" __package_apt python-certbot \
+ --target-release jessie-backports
+ require="__apt_source/jessie-backports" __package_apt certbot \
+ --target-release jessie-backports
+ # Seems to be a missing dependency on debian 8
+ __package python-ndg-httpsclient
+ ;;
+ ascii*)
+ __apt_source ascii-backports \
+ --uri http://auto.mirror.devuan.org/merged \
+ --distribution ascii-backports \
+ --component main
+
+ require="__apt_source/ascii-backports" __package_apt certbot \
+ --target-release ascii-backports
+ ;;
+ beowulf*)
+ __package_apt certbot
+ ;;
+ *)
+ echo "Unsupported OS version: $os_version" >&2
+ exit 1
+ ;;
+ esac
+ ;;
+ freebsd)
+ __package py37-certbot
+ certbot_fullpath="/usr/local/bin/certbot"
+ ;;
+ ubuntu)
+ __package certbot
+ ;;
+ *)
+ echo "Unsupported os: $os" >&2
+ exit 1
+ ;;
+ esac
+fi
+
+# Other OS-dependent values that we want to set every time
+LE_DIR="/etc/letsencrypt"
+certbot_cronjob_state="absent"
case "$os" in
- debian)
- case "$os_version" in
- 8*)
- __apt_source jessie-backports \
- --uri http://http.debian.net/debian \
- --distribution jessie-backports \
- --component main
-
- require="__apt_source/jessie-backports" __package_apt python-certbot --target-release jessie-backports
- require="__apt_source/jessie-backports" __package_apt certbot --target-release jessie-backports
- # Seems to be a missing dependency on debian 8
- __package python-ndg-httpsclient
- ;;
- 9*)
- __apt_source stretch-backports \
- --uri http://http.debian.net/debian \
- --distribution stretch-backports \
- --component main
-
- require="__apt_source/stretch-backports" __package_apt python-certbot --target-release stretch-backports
- require="__apt_source/stretch-backports" __package_apt certbot --target-release stretch-backports
- ;;
- *)
- echo "Unsupported OS version: $os_version" >&2
- exit 1
- ;;
- esac
-
- certbot_fullpath=/usr/bin/certbot
- ;;
- devuan)
- case "$os_version" in
- jessie)
- __apt_source jessie-backports \
- --uri http://auto.mirror.devuan.org/merged \
- --distribution jessie-backports \
- --component main
-
- require="__apt_source/jessie-backports" __package_apt python-certbot --target-release jessie-backports
- require="__apt_source/jessie-backports" __package_apt certbot --target-release jessie-backports
- # Seems to be a missing dependency on debian 8
- __package python-ndg-httpsclient
- ;;
- *)
- echo "Unsupported OS version: $os_version" >&2
- exit 1
- ;;
- esac
-
- certbot_fullpath=/usr/bin/certbot
- ;;
- freebsd)
- __package py27-certbot
-
- certbot_fullpath=/usr/local/bin/certbot
- ;;
- *)
- echo "Unsupported os: $os" >&2
- exit 1
- ;;
+ archlinux|alpine)
+ certbot_cronjob_state="present"
+ ;;
+ freebsd)
+ LE_DIR="/usr/local/etc/letsencrypt"
+ # FreeBSD uses periodic(8) instead of crontabs for this
+ __line "periodic.conf_weekly_certbot" \
+ --file "/etc/periodic.conf" \
+ --regex "^(#[[:space:]]*)?weekly_certbot_enable=.*" \
+ --state "replace" \
+ --line 'weekly_certbot_enable="YES"'
+ ;;
+ *)
+ ;;
esac
+# This is only necessary in certain OS
+__cron letsencrypt-certbot \
+ --user root \
+ --command "${certbot_fullpath} renew -q" \
+ --hour 0 \
+ --minute 47 \
+ --state "${certbot_cronjob_state}"
-__cron letsencrypt-certbot \
- --user root \
- --command "$certbot_fullpath renew -q" \
- --hour 0 \
- --minute 47
+# Ensure hook directories
+HOOKS_DIR="${LE_DIR}/renewal-hooks"
+__directory "${LE_DIR}" --mode 0755
+require="__directory/${LE_DIR}" __directory "${HOOKS_DIR}" --mode 0755
+
+if [ -f "${__object}/parameter/domain" ]; then
+ domains="$(sort "${__object}/parameter/domain")"
+else
+ domains="${__object_id}"
+fi
+
+# Install hooks as needed
+for hook in deploy pre post; do
+ # Using something unique and specific to this object
+ hook_file="${HOOKS_DIR}/${hook}/${__object_id}.cdist.sh"
+
+ # This defines hook_contents
+ # shellcheck source=cdist/conf/type/__letsencrypt_cert/files/gen_hook.sh
+ . "${__type}/files/gen_hook.sh"
+
+ # Ensure hook directory exists
+ require="__directory/${HOOKS_DIR}" __directory "${HOOKS_DIR}/${hook}" \
+ --mode 0755
+ require="__directory/${HOOKS_DIR}/${hook}" __file "${hook_file}" \
+ --mode 0555 \
+ --source '-' \
+ --state "${hook_state}" <.
#
-#
-file="/$__object_id"
-[ -f "$__object/parameter/file" ] && file=$(cat "$__object/parameter/file")
+if [ -f "$__object/parameter/file" ]; then
+ file=$(cat "$__object/parameter/file")
+else
+ file="/$__object_id"
+fi
+
+[ -f "$file" ] || exit 0
+
+if [ -f "$__object/parameter/before" ]; then
+ position="before"
+elif [ -f "$__object/parameter/after" ]; then
+ position="after"
+else
+ # By default we append to the end of the file.
+ position="end"
+fi
if [ -f "$__object/parameter/regex" ]; then
- regex=$(cat "$__object/parameter/regex")
- greparg=""
+ needle="regex"
else
- if [ ! -f "$__object/parameter/line" ]; then
- echo "Parameter line and regex missing - cannot explore" >&2
- exit 1
- fi
- regex="$(cat "$__object/parameter/line")"
- greparg="-F -x"
+ needle="line"
fi
-# Allow missing file - thus 2>/dev/null
-if grep -q $greparg "$regex" "$file" 2>/dev/null; then
- echo present
-else
- echo absent
-fi
+awk -v position="$position" -v needle="$needle" '
+function _find(_text, _pattern) {
+ if (needle == "regex") {
+ return match(_text, _pattern)
+ } else {
+ return index(_text, _pattern) == 1
+ }
+}
+BEGIN {
+ getline anchor < (ENVIRON["__object"] "/parameter/" position)
+ getline pattern < (ENVIRON["__object"] "/parameter/" needle)
+ getline line < (ENVIRON["__object"] "/parameter/line")
+
+ found_line = 0
+ correct_line = 0
+ correct_pos = (position != "after" && position != "before")
+}
+{
+ if (position == "after") {
+ if (match($0, anchor)) {
+ getline
+ if (_find($0, pattern)) {
+ found_line++
+ if (index($0, line) == 1) { correct_line++ }
+ correct_pos = 1
+ exit 0
+ }
+ } else if (_find($0, pattern)) {
+ found_line++
+ if (index($0, line) == 1) { correct_line++ }
+ }
+ } else if (position == "before") {
+ if (_find($0, pattern)) {
+ found_line++
+ if (index($0, line) == 1) { correct_line++ }
+ getline
+ if (match($0, anchor)) {
+ correct_pos = 1
+ exit 0
+ }
+ }
+ } else {
+ if (_find($0, pattern)) {
+ found_line++
+ if (index($0, line) == 1) { correct_line++ }
+ exit 0
+ }
+ }
+}
+END {
+ if (found_line && correct_pos) {
+ if (correct_line) {
+ print "present"
+ } else {
+ print "matching"
+ }
+ } else if (found_line) {
+ print "wrongposition"
+ } else {
+ print "absent"
+ }
+}
+' "$file"
diff --git a/cdist/conf/type/__line/gencode-remote b/cdist/conf/type/__line/gencode-remote
index 4a75b4c5..a89886da 100755
--- a/cdist/conf/type/__line/gencode-remote
+++ b/cdist/conf/type/__line/gencode-remote
@@ -1,7 +1,7 @@
#!/bin/sh -e
#
-# 2012 Nico Schottelius (nico-cdist at schottelius.org)
-# 2014 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2018 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -18,76 +18,123 @@
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see .
#
-#
-file="/$__object_id"
-regex=""
-state_should="present"
-[ -f "$__object/parameter/file" ] && file=$(cat "$__object/parameter/file")
-[ -f "$__object/parameter/regex" ] && regex=$(cat "$__object/parameter/regex")
-[ -f "$__object/parameter/state" ] && state_should=$(cat "$__object/parameter/state")
-[ -f "$__object/parameter/line" ] && line=$(cat "$__object/parameter/line")
+if [ -f "$__object/parameter/before" ] && [ -f "$__object/parameter/after" ]; then
+ echo "Use either --before OR --after but not both." >&2
+ exit 1
+fi
+if [ -f "$__object/parameter/file" ]; then
+ file="$(cat "$__object/parameter/file")"
+else
+ file="/$__object_id"
+fi
+
+state_should="$(cat "$__object/parameter/state")"
state_is="$(cat "$__object/explorer/state")"
-[ "$state_should" = "$state_is" ] && exit 0
+if [ -z "$state_is" ]; then
+ printf 'The file "%s" is missing. Please create it before using %s on it.\n' "$file" "${__type##*/}" >&2
+ exit 1
+fi
+if [ "$state_should" = "$state_is" ] || \
+ { [ "$state_should" = "present" ] && [ "$state_is" = "matching" ] ;} || \
+ { [ "$state_should" = "replace" ] && [ "$state_is" = "present" ] ;} ; then
+ # If state matches already, or 'present' is used and regex matches
+ # or 'replace' is used and the exact line is present, then there is
+ # nothing to do
+ exit 0
+fi
+
+if [ -f "$__object/parameter/before" ]; then
+ position="before"
+elif [ -f "$__object/parameter/after" ]; then
+ position="after"
+else
+ # By default we append to the end of the file.
+ position="end"
+fi
+
+if [ -f "$__object/parameter/regex" ]; then
+ needle="regex"
+else
+ needle="line"
+fi
+
+add=0
+remove=0
case "$state_should" in
- present)
- if [ ! "$line" ]; then
- echo "Required parameter \"line\" is missing" >&2
- exit 1
- fi
+ present|replace)
+ if [ "$state_is" = "wrongposition" ] || [ "$state_is" = "matching" ]; then
+ echo updated >> "$__messages_out"
+ remove=1
+ else
+ echo added >> "$__messages_out"
+ fi
+ add=1
+ ;;
+ absent)
+ echo removed >> "$__messages_out"
+ remove=1
+ ;;
+esac
- #echo "echo \"$line\" >> $file"
- #line_sanitised=$(cat "$__object/parameter/line" | sed 's/"/\"/g')
- # Idea: replace ' in the string:
- # '"'"'
- # |------> ': end the string
- # |-|---> "'": create ' in the output string
- # |--> ': continue the string
- #
- # Replace all \ so \t and other combinations are not interpreted
- #
-
-
- # line_sanitised=$(cat "$__object/parameter/line" | sed -e "s/'/'\"'\"'/g" -e 's/\\/\\\\/g')
- # The one above does not work:
- # --line "PS1='[\t] \[\033[1m\]\h\[\033[0m\]:\w\\$ '"
- # becomes
- # PS1='[\\t] \\[\\033[1m\\]\\h\\[\\033[0m\\]:\\w\\$ '
-
- # Only replace ' with '"'"' and keep \ as they are
- line_sanitised=$(cat "$__object/parameter/line" | sed -e "s/'/'\"'\"'/g")
- printf '%s' "printf '%s\n' '$line_sanitised' >> $file"
- echo "added" >> "$__messages_out"
-
- ;;
- absent)
- if [ "$regex" -a "$line" ]; then
- echo "Mutally exclusive parameters regex and line given for state absent" >&2
- exit 1
- fi
-
- greparg=""
- if [ "$line" ]; then
- regex="$line"
- greparg="-F -x"
- fi
-
- cat << eof
+cat << DONE
tmpfile=\$(mktemp ${file}.cdist.XXXXXXXXXX)
# preserve ownership and permissions of existing file
if [ -f "$file" ]; then
cp -p "$file" "\$tmpfile"
fi
-grep -v $greparg "$regex" '$file' > \$tmpfile || true
+
+awk -v position="$position" -v needle="$needle" -v remove=$remove -v add=$add '
+function _find(_text, _pattern) {
+ if (needle == "regex") {
+ return match(_text, _pattern)
+ } else {
+ return index(_text, _pattern)
+ }
+}
+BEGIN {
+ line_file = ENVIRON["__object"] "/parameter/line"
+ getline line < line_file
+ # Need to close line file as it may be re-read as pattern below.
+ close(line_file)
+ getline pattern < (ENVIRON["__object"] "/parameter/" needle)
+ getline anchor < (ENVIRON["__object"] "/parameter/" position)
+}
+{
+ if (remove) {
+ if (_find(\$0, pattern)) {
+ # skip over this line -> remove it
+ next
+ }
+ }
+ if (add) {
+ if (anchor && match(\$0, anchor)) {
+ if (position == "before") {
+ print line
+ add = 0
+ print
+ } else if (position == "after") {
+ print
+ print line
+ add = 0
+ }
+ next
+ }
+ }
+ print
+}
+END {
+ if (add) {
+ print line
+ }
+}
+' "$file" > "\$tmpfile"
mv -f "\$tmpfile" "$file"
-eof
- echo "removed" >> "$__messages_out"
- ;;
- *)
- echo "Unknown state: $state_should" >&2
- exit 1
- ;;
-esac
+DONE
+
+if [ -f "$__object/parameter/onchange" ]; then
+ cat "$__object/parameter/onchange"
+fi
diff --git a/cdist/conf/type/__line/man.rst b/cdist/conf/type/__line/man.rst
index b63ea2b3..70490f68 100644
--- a/cdist/conf/type/__line/man.rst
+++ b/cdist/conf/type/__line/man.rst
@@ -13,72 +13,113 @@ This cdist type allows you to add lines and remove lines from files.
REQUIRED PARAMETERS
-------------------
+None.
+
OPTIONAL PARAMETERS
-------------------
-state
- 'present' or 'absent', defaults to 'present'
+after
+ Insert the given line after this pattern.
-line
- Specifies the line which should be absent or present
-
- Must be present, if state is present.
- Must not be combined with regex, if state is absent.
-
-regex
- If state is present, search for this pattern and add
- given line, if the given regular expression does not match.
-
- In case of absent, ensure all lines matching the
- regular expression are absent.
-
- The regular expression is interpreted by grep.
-
- Must not be combined with line, if state is absent.
+before
+ Insert the given line before this pattern.
file
If supplied, use this as the destination file.
Otherwise the object_id is used.
+line
+ Specifies the line which should be absent or present.
+
+ Must be present, if state is 'present' or 'replace'.
+ Ignored if regex is given and state is 'absent'.
+
+regex
+ If state is 'present', search for this pattern and if it matches add
+ the given line.
+
+ If state is 'absent', ensure all lines matching the regular expression
+ are absent.
+
+ If state is 'replace', ensure all lines matching the regular expression
+ are exactly 'line'.
+
+ The regular expression is interpreted by awk's match function.
+
+state
+ 'present', 'absent' or 'replace', defaults to 'present'.
+
+onchange
+ The code to run if line is added, removed or updated.
+
+
+BOOLEAN PARAMETERS
+------------------
+None.
+
+
MESSAGES
--------
added
- The line was added.
+ The line was added.
+
+updated
+ The line or its position was changed.
removed
- The line was removed.
+ The line was removed.
+
EXAMPLES
--------
.. code-block:: sh
- # Manage the DAEMONS line in rc.conf
- __line daemons --file /etc/rc.conf --line 'DAEMONS=(hwclock !network sshd crond postfix)'
+ # Manage a hosts entry for www.example.com.
+ __line /etc/hosts \
+ --line '127.0.0.2 www.example.com'
- # Ensure the home mount is present in /etc/fstab - explicitly make it present
- __line home-fstab \
- --file /etc/fstab \
- --line 'filer.fs:/vol/home /home nfs defaults 0 0' \
- --state present
+ # Manage another hosts entry for test.example.com.
+ __line hosts:test.example.com \
+ --file /etc/hosts \
+ --line '127.0.0.3 test.example.com'
- # Removes the line specifiend in "include_www" from the file "lighttpd.conf"
- __line legacy_timezone --file /etc/rc.conf --regex 'TIMEZONE=.*' --state absent
+ # Remove the line starting with TIMEZONE from the /etc/rc.conf file.
+ __line legacy_timezone \
+ --file /etc/rc.conf \
+ --regex 'TIMEZONE=.*' \
+ --state absent
+
+ # Insert a line before another one.
+ __line password-auth-local:classify \
+ --file /etc/pam.d/password-auth-local \
+ --line '-session required pam_exec.so debug log=/tmp/classify.log /usr/local/libexec/classify' \
+ --before '^session[[:space:]]+include[[:space:]]+password-auth-ac$'
+
+ # Insert a line after another one.
+ __line password-auth-local:classify \
+ --file /etc/pam.d/password-auth-local \
+ --line '-session required pam_exec.so debug log=/tmp/classify.log /usr/local/libexec/classify' \
+ --after '^session[[:space:]]+include[[:space:]]+password-auth-ac$'
+
+ # Uncomment as needed and set a value in a configuration file.
+ __line /etc/example.conf \
+ --line 'SomeSetting SomeValue' \
+ --regex '^(#[[:space:]]*)?SomeSetting[[:space:]]' \
+ --state replace
SEE ALSO
--------
-:strong:`grep`\ (1)
+:strong:`cdist-type`\ (7)
AUTHORS
-------
-Nico Schottelius
+Steven Armstrong
COPYING
-------
-Copyright \(C) 2012-2013 Nico Schottelius. You can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
+Copyright \(C) 2018 Steven Armstrong. Free use of this software is
+granted under the terms of the GNU General Public License version 3 (GPLv3).
diff --git a/cdist/conf/type/__line/parameter/default/state b/cdist/conf/type/__line/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__line/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__line/parameter/optional b/cdist/conf/type/__line/parameter/optional
index 604a203e..1c34c699 100644
--- a/cdist/conf/type/__line/parameter/optional
+++ b/cdist/conf/type/__line/parameter/optional
@@ -1,4 +1,7 @@
-state
-regex
+after
+before
file
line
+regex
+state
+onchange
diff --git a/cdist/conf/type/__link/explorer/state b/cdist/conf/type/__link/explorer/state
index b8d8fc2b..7150df25 100755
--- a/cdist/conf/type/__link/explorer/state
+++ b/cdist/conf/type/__link/explorer/state
@@ -32,9 +32,9 @@ destination_dir="${destination%/*}"
case "$type" in
symbolic)
- cd "$destination_dir"
- source_is=$(ls -l "$destination" | sed 's/.*-> //g')
+ cd "$destination_dir" || exit 1
if [ -h "$destination" ]; then
+ source_is=$(readlink "$destination")
# ignore trailing slashes for comparison
if [ "${source_is%/}" = "${source%/}" ]; then
echo present
@@ -46,13 +46,19 @@ case "$type" in
fi
;;
hard)
- cd "$destination_dir"
+ cd "$destination_dir" || exit 1
# check source relative to destination_dir
if [ ! -e "$source" ]; then
echo sourcemissing
exit 0
fi
+ # Currently not worth the effor to change it, stat is not defined by POSIX
+ # and different OSes has different implementations for it.
+ # shellcheck disable=SC2012
destination_inode=$(ls -i "$destination" | awk '{print $1}')
+ # Currently not worth the effor to change it, stat is not defined by POSIX
+ # and different OSes has different implementations for it.
+ # shellcheck disable=SC2012
source_inode=$(ls -i "$source" | awk '{print $1}')
if [ "$destination_inode" -eq "$source_inode" ]; then
echo present
diff --git a/cdist/conf/type/__link/explorer/type b/cdist/conf/type/__link/explorer/type
index 579fd081..b322bf42 100755
--- a/cdist/conf/type/__link/explorer/type
+++ b/cdist/conf/type/__link/explorer/type
@@ -24,23 +24,26 @@
destination="/$__object_id"
if [ ! -e "$destination" ]; then
- echo none
+ echo none
elif [ -h "$destination" ]; then
- echo symlink
+ echo symlink
elif [ -f "$destination" ]; then
- type="$(cat "$__object/parameter/type")"
- case "$type" in
- hard)
- link_count=$(ls -l "$destination" | awk '{ print $2 }')
- if [ $link_count -gt 1 ]; then
- echo hardlink
- exit 0
- fi
- ;;
- esac
- echo file
+ type="$(cat "$__object/parameter/type")"
+ case "$type" in
+ hard)
+ # Currently not worth the effor to change it, stat is not defined by POSIX
+ # and different OSes has different implementations for it.
+ # shellcheck disable=SC2012
+ link_count=$(ls -l "$destination" | awk '{ print $2 }')
+ if [ "$link_count" -gt 1 ]; then
+ echo hardlink
+ exit 0
+ fi
+ ;;
+ esac
+ echo file
elif [ -d "$destination" ]; then
- echo directory
+ echo directory
else
- echo unknown
+ echo unknown
fi
diff --git a/cdist/conf/type/__link/gencode-remote b/cdist/conf/type/__link/gencode-remote
index 4467fb8e..45c22fcc 100755
--- a/cdist/conf/type/__link/gencode-remote
+++ b/cdist/conf/type/__link/gencode-remote
@@ -48,21 +48,25 @@ case "$state_should" in
if [ "$file_type" = "directory" ]; then
# our destination is currently a directory, delete it
printf 'rm -rf "%s" &&\n' "$destination"
+ echo "removed '$destination' (directory)" >> "$__messages_out"
else
if [ "$state_is" = "wrongsource" ]; then
# our destination is a symlink but points to the wrong source,
# delete it
printf 'rm -f "%s" &&\n' "$destination"
+ echo "removed '$destination' (wrongsource)" >> "$__messages_out"
fi
fi
# create our link
printf 'ln %s -f "%s" "%s"\n' "$lnopt" "$source" "$destination"
+ echo "created '$destination'" >> "$__messages_out"
;;
absent)
# only delete if it is a sym/hard link
- if [ "$file_type" = "symlink" -o "$file_type" = "hardlink" ]; then
+ if [ "$file_type" = "symlink" ] || [ "$file_type" = "hardlink" ]; then
printf 'rm -f "%s"\n' "$destination"
+ echo "removed '$destination'" >> "$__messages_out"
fi
;;
*)
diff --git a/cdist/conf/type/__link/man.rst b/cdist/conf/type/__link/man.rst
index 9dc4665f..2e81aea9 100644
--- a/cdist/conf/type/__link/man.rst
+++ b/cdist/conf/type/__link/man.rst
@@ -18,7 +18,7 @@ source
Specifies the link source.
type
- Specifies the link type: Either hard or symoblic.
+ Specifies the link type: Either hard or symbolic.
OPTIONAL PARAMETERS
@@ -27,6 +27,22 @@ state
'present' or 'absent', defaults to 'present'
+MESSAGES
+--------
+
+created
+ Link to destination was created.
+
+removed
+ Link to destination was removed.
+
+removed (directory)
+ Destination was removed because state is ``present`` and destination was directory.
+
+removed (wrongsource)
+ Destination was removed because state is ``present`` and destination link source was wrong.
+
+
EXAMPLES
--------
diff --git a/cdist/conf/type/__locale/deprecated b/cdist/conf/type/__locale/deprecated
new file mode 100644
index 00000000..5a06b28e
--- /dev/null
+++ b/cdist/conf/type/__locale/deprecated
@@ -0,0 +1 @@
+This type is deprecated. Please use __localedef instead.
diff --git a/cdist/conf/type/__locale/explorer/state b/cdist/conf/type/__locale/explorer/state
new file mode 100755
index 00000000..4494fcbc
--- /dev/null
+++ b/cdist/conf/type/__locale/explorer/state
@@ -0,0 +1,36 @@
+#!/bin/sh -e
+# __locale/explorer/state
+#
+# 2020 Matthias Stecher (matthiasstecher at gmx.de)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+#
+# Check if the locale is already installed on the system.
+# Outputs 'present' or 'absent' depending if the locale exists.
+#
+
+
+# Get user-defined locale
+# locale name is echoed differently than the user propably set it (for UTF-8)
+locale="$(echo "$__object_id" | sed 's/UTF-8/utf8/')"
+
+# Check if the given locale exists on the system
+if localedef --list-archive | grep -qFx "$locale"; then
+ echo present
+else
+ echo absent
+fi
diff --git a/cdist/conf/type/__locale/gencode-remote b/cdist/conf/type/__locale/gencode-remote
index 04e48712..4639cef8 100755
--- a/cdist/conf/type/__locale/gencode-remote
+++ b/cdist/conf/type/__locale/gencode-remote
@@ -1,6 +1,6 @@
#!/bin/sh -e
#
-# 2013 Nico Schottelius (nico-cdist at schottelius.org)
+# 2013-2019 Nico Schottelius (nico-cdist at schottelius.org)
#
# This file is part of cdist.
#
@@ -23,6 +23,15 @@
locale="$__object_id"
+state_is=$(cat "$__object/explorer/state")
+state_should=$(cat "$__object/parameter/state")
+
+# short circuit if there is nothing to do
+if [ "$state_is" = "$state_should" ]; then
+ exit 0
+fi
+
+
# Hardcoded, create a pull request with
# branching on $os in case it is at another location
alias=/usr/share/locale/locale.alias
@@ -35,9 +44,16 @@ charmap=$(echo "$locale" | cut -d . -f 2)
# W-T-F!
locale_remove=$(echo "$locale" | sed 's/UTF-8/utf8/')
-state=$(cat "$__object/parameter/state")
+os=$(cat "$__global/explorer/os")
-case "$state" in
+# Nothing to be done on alpine
+case "$os" in
+ alpine)
+ exit 0
+ ;;
+esac
+
+case "$state_should" in
present)
echo localedef -A "$alias" -f "$charmap" -i "$input" "$locale"
;;
@@ -45,7 +61,7 @@ case "$state" in
echo localedef --delete-from-archive "$locale_remove"
;;
*)
- echo "Unsupported state: $state" >&2
+ echo "Unsupported state: $state_should" >&2
exit 1
;;
esac
diff --git a/cdist/conf/type/__locale/man.rst b/cdist/conf/type/__locale/man.rst
index 60a4eacc..e36ab061 100644
--- a/cdist/conf/type/__locale/man.rst
+++ b/cdist/conf/type/__locale/man.rst
@@ -8,7 +8,8 @@ cdist-type__locale - Configure locales
DESCRIPTION
-----------
-This cdist type allows you to setup locales.
+This cdist type allows you to setup locales. On systems that don't
+support locale setting like alpine/musl libc, it is a no-op.
OPTIONAL PARAMETERS
@@ -44,6 +45,6 @@ Nico Schottelius
COPYING
-------
-Copyright \(C) 2013-2016 Nico Schottelius. Free use of this software is
+Copyright \(C) 2013-2019 Nico Schottelius. Free use of this software is
granted under the terms of the GNU General Public License version 3 or
later (GPLv3+).
diff --git a/cdist/conf/type/__locale/manifest b/cdist/conf/type/__locale/manifest
index cacd0b42..9f1e17ac 100755
--- a/cdist/conf/type/__locale/manifest
+++ b/cdist/conf/type/__locale/manifest
@@ -1,6 +1,6 @@
#!/bin/sh -e
#
-# 2013-2015 Nico Schottelius (nico-cdist at schottelius.org)
+# 2013-2019 Nico Schottelius (nico-cdist at schottelius.org)
# 2015 David Hürlimann (david at ungleich.ch)
#
# This file is part of cdist.
@@ -19,7 +19,7 @@
# along with cdist. If not, see .
#
#
-# Install required packages
+# Install required packages
#
os=$(cat "$__global/explorer/os")
@@ -30,7 +30,7 @@ case "$os" in
# Debian needs a seperate package
__package locales --state present
;;
- archlinux|suse|ubuntu|scientific|centos)
+ archlinux|suse|ubuntu|scientific|centos|alpine)
:
;;
*)
diff --git a/cdist/conf/type/__locale_system/manifest b/cdist/conf/type/__locale_system/manifest
index 80f7401b..4b996ebc 100755
--- a/cdist/conf/type/__locale_system/manifest
+++ b/cdist/conf/type/__locale_system/manifest
@@ -3,6 +3,7 @@
# 2012-2016 Steven Armstrong (steven-cdist at armstrong.cc)
# 2016 Carlos Ortigoza (carlos.ortigoza at ungleich.ch)
# 2016 Nico Schottelius (nico.schottelius at ungleich.ch)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -23,17 +24,171 @@
# Configure system-wide locale by modifying i18n file.
#
+version_ge() {
+ awk -F '[^0-9.]' -v target="${1:?}" '
+ function max(x, y) { return x > y ? x : y }
+ BEGIN {
+ getline
+ nx = split($1, x, ".")
+ ny = split(target, y, ".")
+ for (i = 1; i <= max(nx, ny); ++i) {
+ diff = int(x[i]) - int(y[i])
+ if (diff == 0) continue
+ exit (diff < 0)
+ }
+ }'
+}
+
+
+key=$__object_id
+onchange_cmd= # none, by default
+quote_value=false
+
+catval() {
+ # shellcheck disable=SC2059
+ printf "$($quote_value && echo '"%s"' || echo '%s')" "$(cat "$1")"
+}
+
+state_should=$(cat "${__object}/parameter/state")
+
os=$(cat "$__global/explorer/os")
-case "$os" in
- debian|ubuntu)
+case $os
+in
+ debian)
+ if version_ge 4 <"${__global}/explorer/os_version"
+ then
+ # Debian 4 (etch) and later
+ locale_conf="/etc/default/locale"
+ else
+ locale_conf="/etc/environment"
+ fi
+ ;;
+ devuan)
locale_conf="/etc/default/locale"
;;
+ ubuntu)
+ if version_ge 6.10 <"${__global}/explorer/os_version"
+ then
+ # Ubuntu 6.10 (edgy) and later
+ locale_conf="/etc/default/locale"
+ else
+ locale_conf="/etc/environment"
+ fi
+ ;;
archlinux)
locale_conf="/etc/locale.conf"
;;
- redhat|centos)
- locale_conf="/etc/sysconfig/i18n"
+ centos|redhat|scientific)
+ # shellcheck source=/dev/null
+ version_id=$(. "${__global}/explorer/os_release" && echo "${VERSION_ID:-0}")
+ if echo "${version_id}" | version_ge 7
+ then
+ locale_conf="/etc/locale.conf"
+ else
+ locale_conf="/etc/sysconfig/i18n"
+ fi
+ ;;
+ fedora)
+ # shellcheck source=/dev/null
+ version_id=$(. "${__global}/explorer/os_release" && echo "${VERSION_ID:-0}")
+ if echo "${version_id}" | version_ge 18
+ then
+ locale_conf="/etc/locale.conf"
+ quote_value=false
+ else
+ locale_conf="/etc/sysconfig/i18n"
+ fi
+ ;;
+ gentoo)
+ case $(cat "${__global}/explorer/init")
+ in
+ (*openrc*)
+ locale_conf="/etc/env.d/02locale"
+ onchange_cmd="env-update --no-ldconfig"
+ quote_value=true
+ ;;
+ (systemd)
+ locale_conf="/etc/locale.conf"
+ ;;
+ esac
+ ;;
+ freebsd|netbsd)
+ # NetBSD doesn't have a separate configuration file to set locales.
+ # In FreeBSD locales could be configured via /etc/login.conf but parsing
+ # that would be annoying, so the shell login file will have to do.
+ # "Non-POSIX" shells like csh will not be updated here.
+
+ locale_conf="/etc/profile"
+ quote_value=true
+ value="$(catval "${__object}/parameter/value"); export ${key}"
+ ;;
+ solaris)
+ locale_conf="/etc/default/init"
+ locale_conf_group="sys"
+
+ if version_ge 5.11 <"${__global}/explorer/os_version"
+ then
+ # mode on Oracle Solaris 11 is actually 0444,
+ # but the write bit makes sense, IMO
+ locale_conf_mode=0644
+
+ # Oracle Solaris 11.2 and later uses SMF to store environment info.
+ # This is a hack, but I didn't feel like modifying the whole type
+ # just for some Oracle nonsense.
+ # 11.3 apparently added nlsadm(1m), but it is missing from 11.2.
+ # Illumos continues to use /etc/default/init
+ # NOTE: Remember not to use "cool" POSIX features like -q or -e with
+ # Solaris grep.
+ release_regex='Oracle Solaris 11.[2-9][0-9]*'
+ case $state_should
+ in
+ (present)
+ svccfg_cmd="svccfg -s svc:/system/environment:init setprop environment/${key} = astring: '$(cat "${__object}/parameter/value")'"
+ ;;
+ (absent)
+ svccfg_cmd="svccfg -s svc:/system/environment:init delprop environment/${key}"
+ ;;
+ esac
+ refresh_cmd='svcadm refresh svc:/system/environment'
+ onchange_cmd="grep '${release_regex}' /etc/release >&- || exit 0; ${svccfg_cmd:-:} && ${refresh_cmd}"
+ else
+ locale_conf_mode=0555
+ fi
+ ;;
+ slackware)
+ # NOTE: lang.csh (csh config) is ignored here.
+ locale_conf="/etc/profile.d/lang.sh"
+ locale_conf_mode=0755
+ key="export ${__object_id}"
+ ;;
+ suse)
+ if test -s "${__global}/explorer/os_release"
+ then
+ # shellcheck source=/dev/null
+ os_version=$(. "${__global}/explorer/os_release" && echo "${VERSION}")
+ else
+ os_version=$(sed -n 's/^VERSION\ *=\ *//p' "${__global}/explorer/os_version")
+ fi
+ os_major=$(expr "${os_version}" : '\([0-9]\{1,\}\)')
+
+ # https://documentation.suse.com/sles/15-SP2/html/SLES-all/cha-suse.html#sec-suse-l10n
+ if expr "${os_major}" '>=' 15 \& "${os_major}" != 42
+ then
+ # It seems that starting with SuSE 15 the systemd /etc/locale.conf
+ # is the preferred way to set locales, although
+ # /etc/sysconfig/language is still available.
+ # Older documentation doesn't mention /etc/locale.conf, even though
+ # is it created when localectl is used.
+ locale_conf="/etc/locale.conf"
+ else
+ locale_conf="/etc/sysconfig/language"
+ quote_value=true
+ key="RC_${__object_id}"
+ fi
+ ;;
+ voidlinux)
+ locale_conf="/etc/locale.conf"
;;
*)
echo "Your operating system ($os) is currently not supported by this type (${__type##*/})." >&2
@@ -42,14 +197,16 @@ case "$os" in
;;
esac
-__file "$locale_conf" \
- --owner root --group root --mode 644 \
- --state exists
+__file "${locale_conf}" --state exists \
+ --owner "${locale_conf_owner:-0}" \
+ --group "${locale_conf_group:-0}" \
+ --mode "${locale_conf_mode:-0644}"
-require="__file/$locale_conf" \
- __key_value "$locale_conf:$__object_id" \
- --file "$locale_conf" \
- --key "$__object_id" \
- --delimiter = \
- --state "$(cat "$__object/parameter/state")" \
- --value "$(cat "$__object/parameter/value")"
+require="__file/${locale_conf}" \
+__key_value "${locale_conf}:${key#export }" \
+ --file "${locale_conf}" \
+ --key "${key}" \
+ --delimiter '=' --exact_delimiter \
+ --state "${state_should}" \
+ --value "${value:-$(catval "${__object}/parameter/value")}" \
+ --onchange "${onchange_cmd}"
diff --git a/cdist/conf/type/__localedef/explorer/state b/cdist/conf/type/__localedef/explorer/state
new file mode 100755
index 00000000..3ba57661
--- /dev/null
+++ b/cdist/conf/type/__localedef/explorer/state
@@ -0,0 +1,100 @@
+#!/bin/sh -e
+#
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+# This explorer determines if the locale is defined on the target system.
+# Will print nothing on error.
+#
+# Possible output:
+# present:
+# the main locale (and possibly aliases) is present
+# absent:
+# neither the main locale nor any aliases are present
+# alias-present:
+# the main locale is absent, but at least one of its aliases is present
+#
+
+# Hardcoded, create a pull request in case it is at another location for
+# some other distro. (cf. gencode-remote)
+aliasfile='/usr/share/locale/locale.alias'
+
+command -v locale >/dev/null 2>&1 || exit 0
+
+locales=$(locale -a)
+
+parse_locale() {
+ # This function will split locales into their parts. Locale strings are
+ # usually of the form: [language[_territory][.codeset][@modifier]]
+ # For simplicity, language and territory are not separated by this function.
+ # Old Linux systems were also using "english" or "german" as locale strings.
+ # Usage: parse_locale locale_str lang_var codeset_var modifier_var
+ eval "${2:?}"="$(expr "$1" : '\([^.@]*\)')"
+ eval "${3:?}"="$(expr "$1" : '[^.]*\.\([^@]*\)')"
+ eval "${4:?}"="$(expr "$1" : '.*@\(.*\)$')"
+}
+
+format_locale() {
+ # Usage: format_locale language codeset modifier
+ printf '%s' "$1"
+ test -z "$2" || printf '.%s' "$2"
+ test -z "$3" || printf '@%s' "$3"
+ printf '\n'
+}
+
+gnu_normalize_codeset() {
+ # reimplementation of glibc/locale/programs/localedef.c normalize_codeset()
+ echo "$*" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]'
+}
+
+locale_available() (
+ echo "${locales}" | grep -qxF "$1" || {
+ # glibc uses "normalized" locale names in archives.
+ # If a locale is stored in an archive, the normalized name will be
+ # printed by locale, so that needs to be checked, too.
+ localename=$(
+ parse_locale "$1" _lang _codeset _modifier \
+ && format_locale "${_lang:?}" "$(gnu_normalize_codeset "${_codeset?}")" \
+ "${_modifier?}")
+ echo "${locales}" | grep -qxF "${localename}"
+ }
+)
+
+if locale_available "${__object_id:?}"
+then
+ echo present
+else
+ # NOTE: locale.alias can be symlinked.
+ if test -e "${aliasfile}"
+ then
+ # Check if one of the aliases of the locale is defined
+ baselocale=$(
+ parse_locale "${__object_id:?}" _lang _codeset _modifiers \
+ && format_locale "${_lang}" "${_codeset}")
+ while read -r _alias _localename
+ do
+ if test "${_localename}" = "${baselocale}" \
+ && echo "${locales}" | grep -qxF "${_alias}"
+ then
+ echo alias-present
+ exit 0
+ fi
+ done <"${aliasfile}"
+ fi
+
+ echo absent
+fi
diff --git a/cdist/conf/type/__localedef/files/lib/glibc.sh b/cdist/conf/type/__localedef/files/lib/glibc.sh
new file mode 100644
index 00000000..6ace80d4
--- /dev/null
+++ b/cdist/conf/type/__localedef/files/lib/glibc.sh
@@ -0,0 +1,5 @@
+# -*- mode: sh; indent-tabs-mode: t -*-
+
+gnu_normalize_codeset() {
+ echo "$*" | tr -cd '[:alnum:]' | tr '[:upper:]' '[:lower:]'
+}
diff --git a/cdist/conf/type/__localedef/files/lib/locale.sh b/cdist/conf/type/__localedef/files/lib/locale.sh
new file mode 100644
index 00000000..b5e61374
--- /dev/null
+++ b/cdist/conf/type/__localedef/files/lib/locale.sh
@@ -0,0 +1,20 @@
+# -*- mode: sh; indent-tabs-mode:t -*-
+
+parse_locale() {
+ # This function will split locales into their parts. Locale strings are
+ # usually of the form: [language[_territory][.codeset][@modifier]]
+ # For simplicity, language and territory are not separated by this function.
+ # Old Linux systems were also using "english" or "german" as locale strings.
+ # Usage: parse_locale locale_str lang_var codeset_var modifier_var
+ eval "${2:?}"="$(expr "$1" : '\([^.@]*\)')"
+ eval "${3:?}"="$(expr "$1" : '[^.]*\.\([^@]*\)')"
+ eval "${4:?}"="$(expr "$1" : '.*@\(.*\)$')"
+}
+
+format_locale() {
+ # Usage: format_locale language codeset modifier
+ printf '%s' "$1"
+ test -z "$2" || printf '.%s' "$2"
+ test -z "$3" || printf '@%s' "$3"
+ printf '\n'
+}
diff --git a/cdist/conf/type/__localedef/gencode-remote b/cdist/conf/type/__localedef/gencode-remote
new file mode 100755
index 00000000..4538151f
--- /dev/null
+++ b/cdist/conf/type/__localedef/gencode-remote
@@ -0,0 +1,136 @@
+#!/bin/sh -e
+#
+# 2013-2019 Nico Schottelius (nico-cdist at schottelius.org)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+# Manage system locales using localedef(1).
+#
+
+# shellcheck source=cdist/conf/type/__localedef/files/lib/locale.sh
+. "${__type:?}/files/lib/locale.sh"
+# shellcheck source=cdist/conf/type/__localedef/files/lib/glibc.sh
+. "${__type:?}/files/lib/glibc.sh"
+
+state_is=$(cat "${__object:?}/explorer/state")
+state_should=$(cat "${__object:?}/parameter/state")
+
+test "${state_should}" = 'present' -o "${state_should}" = 'absent' || {
+ printf 'Invalid state: %s\n' "${state_should}" >&2
+ exit 1
+}
+
+# NOTE: If state explorer fails (e.g. locale(1) missing), the following check
+# will always fail and let definition/removal run.
+if test "${state_is}" = "${state_should}"
+then
+ exit 0
+fi
+
+locale=${__object_id:?}
+os=$(cat "${__global:?}/explorer/os")
+
+if expr "${locale}" : '.*/' >/dev/null
+then
+ printf 'Paths as locales are not supported.\n' >&2
+ printf '__object_id is: %s\n' "${locale}" >&2
+ exit 1
+fi
+
+: "${lang=}" "${codeset=}" "${modifier=}" # declare variables for shellcheck
+parse_locale "${locale}" lang codeset modifier
+
+
+case ${os}
+in
+ (alpine|openwrt)
+ printf '%s does not support locales.\n' "${os}" >&2
+ exit 1
+ ;;
+ (archlinux|debian|devuan|ubuntu|suse|centos|fedora|redhat|scientific)
+ # FIXME: The code below only works for glibc-based installations.
+
+ # NOTE: Hardcoded, create a pull request in case it is at another
+ # location for some opther distro.
+ # NOTE: locale.alias can be symlinked (e.g. Debian)
+ aliasfile='/usr/share/locale/locale.alias'
+
+ case ${state_should}
+ in
+ (present)
+ input=$(format_locale "${lang}" '' "${modifier}")
+ cat <<-EOF
+ set --
+ if test -e '${aliasfile}'
+ then
+ set -- -A '${aliasfile}'
+ fi
+
+ localedef -i '${input}' -f '${codeset}' "\$@" '${locale}'
+ EOF
+ ;;
+ (absent)
+ main_localename=$(format_locale "${lang}" "$(gnu_normalize_codeset "${codeset}")" "${modifier}")
+
+ cat <<-EOF
+ while read -r _alias _localename
+ do
+ if test "\${_localename}" = '$(format_locale "${lang}" "${codeset}")'
+ then
+ localedef --delete-from-archive "\${_alias}"
+ fi
+ done <'${aliasfile}'
+ EOF
+
+ if test "${state_is}" = present
+ then
+ printf "localedef --delete-from-archive '%s'\n" "${main_localename}"
+ fi
+ ;;
+ esac
+ ;;
+ (freebsd)
+ case ${state_should}
+ in
+ (present)
+ if expr "$(grep -oe '^[0-9]*' "${__global:?}/explorer/os_version")" '>=' 11 >/dev/null
+ then
+ # localedef(1) is available with FreeBSD >= 11
+ printf "localedef -i '%s' -f '%s' '%s'\n" "${input}" "${codeset}" "${locale}"
+ else
+ printf 'localedef(1) was added to FreeBSD starting with version 11.\n' >&2
+ printf 'Please upgrade your FreeBSD installation to use %s.\n' "${__type##*/}" >&2
+ exit 1
+ fi
+ ;;
+ (absent)
+ printf "rm -R '/usr/share/locale/%s'\n" "${locale}"
+ ;;
+ esac
+ ;;
+ (netbsd|openbsd)
+ # NetBSD/OpenBSD are missing localedef(1).
+ # We also do not delete defined locales because they can't be recreated.
+ echo "${os} is lacking localedef(1). Locale management unavailable." >&2
+ exit 1
+ ;;
+ (*)
+ echo "Your operating system (${os}) is currently not supported by this type (${__type##*/})." >&2
+ echo "Please contribute an implementation for it if you can." >&2
+ exit 1
+ ;;
+esac
diff --git a/cdist/conf/type/__localedef/man.rst b/cdist/conf/type/__localedef/man.rst
new file mode 100644
index 00000000..454ce9d1
--- /dev/null
+++ b/cdist/conf/type/__localedef/man.rst
@@ -0,0 +1,60 @@
+cdist-type__localedef(7)
+========================
+
+NAME
+----
+cdist-type__localedef - Define and remove system locales
+
+
+DESCRIPTION
+-----------
+This cdist type allows you to define locales on the system using
+:strong:`localedef`\ (1) or remove them.
+On systems that don't support definition of new locales, the type will raise an
+error.
+
+**NB:** This type respects the glibc ``locale.alias`` file,
+i.e. it defines alias locales or deletes aliases of a locale when it is removed.
+It is not possible, however, to use alias names to define locales or only remove
+certain aliases of a locale.
+
+
+OPTIONAL PARAMETERS
+-------------------
+state
+ ``present`` or ``absent``. Defaults to ``present``.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # Add locale de_CH.UTF-8
+ __localedef de_CH.UTF-8
+
+ # Same as above, but more explicit
+ __localedef de_CH.UTF-8 --state present
+
+ # Remove colourful British English
+ __localedef en_GB.UTF-8 --state absent
+
+
+SEE ALSO
+--------
+:strong:`locale`\ (1),
+:strong:`localedef`\ (1),
+:strong:`cdist-type__locale_system`\ (7)
+
+
+AUTHORS
+-------
+| Dennis Camera
+| Nico Schottelius
+
+
+COPYING
+-------
+Copyright \(C) 2013-2019 Nico Schottelius, 2020 Dennis Camera. Free use of this
+software is granted under the terms of the GNU General Public License version 3
+or later (GPLv3+).
diff --git a/cdist/conf/type/__localedef/manifest b/cdist/conf/type/__localedef/manifest
new file mode 100755
index 00000000..3ab3ad8c
--- /dev/null
+++ b/cdist/conf/type/__localedef/manifest
@@ -0,0 +1,30 @@
+#!/bin/sh -e
+#
+# 2013-2019 Nico Schottelius (nico-cdist at schottelius.org)
+# 2015 David Hürlimann (david at ungleich.ch)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+# Install required packages.
+#
+
+case $(cat "${__global:?}/explorer/os")
+in
+ (debian|devuan)
+ __package_apt locales --state present
+ ;;
+esac
diff --git a/cdist/conf/type/__localedef/parameter/default/state b/cdist/conf/type/__localedef/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__localedef/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__localedef/parameter/optional b/cdist/conf/type/__localedef/parameter/optional
new file mode 100644
index 00000000..ff72b5c7
--- /dev/null
+++ b/cdist/conf/type/__localedef/parameter/optional
@@ -0,0 +1 @@
+state
diff --git a/cdist/conf/type/__motd/gencode-remote b/cdist/conf/type/__motd/gencode-remote
index bc842cc8..cb7bfc84 100755
--- a/cdist/conf/type/__motd/gencode-remote
+++ b/cdist/conf/type/__motd/gencode-remote
@@ -22,14 +22,18 @@
os=$(cat "$__global/explorer/os")
case "$os" in
- debian|ubuntu|devuan)
-
- # Debian and Ubuntu need to be updated,
- # as seen in /etc/init.d/bootlogs
- echo "uname -snrvm > /var/run/motd"
- echo "cat /etc/motd.tail >> /var/run/motd"
+ freebsd)
+ # FreeBSD only updates /etc/motd on boot,
+ # as seen in /etc/rc.d/motd
+ echo "uname -sri > /etc/motd"
+ echo "cat /etc/motd.template >> /etc/motd"
+ # FreeBSD 13 starts treating motd slightly different from previous
+ # versions this ensures hosts have the expected config.
+ echo "rm /etc/motd.template || true"
+ echo "service motd start"
;;
*)
+ # Other OS tend to treat /etc/motd statically
exit 0
;;
esac
diff --git a/cdist/conf/type/__motd/man.rst b/cdist/conf/type/__motd/man.rst
index 17369684..a567dc80 100644
--- a/cdist/conf/type/__motd/man.rst
+++ b/cdist/conf/type/__motd/man.rst
@@ -10,6 +10,13 @@ DESCRIPTION
-----------
This cdist type allows you to easily setup /etc/motd.
+.. note::
+ In some OS, motd is a bit special, check `motd(5)`.
+ Currently Debian, Devuan, Ubuntu and FreeBSD are taken into account.
+ If your OS of choice does something besides /etc/motd, check the source
+ and contribute support for it.
+ Otherwise it will likely just work.
+
REQUIRED PARAMETERS
-------------------
@@ -20,6 +27,7 @@ OPTIONAL PARAMETERS
-------------------
source
If supplied, copy this file from the host running cdist to the target.
+ If source is '-' (dash), take what was written to stdin as the file content.
If not supplied, a default message will be placed onto the target.
@@ -34,6 +42,15 @@ EXAMPLES
# Supply source file from a different type
__motd --source "$__type/files/my-motd"
+ # Supply source from stdin
+ __motd --source "-" <
COPYING
-------
-Copyright \(C) 2011 Nico Schottelius. You can redistribute it
+Copyright \(C) 2020 Nico Schottelius. You can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
diff --git a/cdist/conf/type/__motd/manifest b/cdist/conf/type/__motd/manifest
index 0e2e8097..b8f74ebf 100755
--- a/cdist/conf/type/__motd/manifest
+++ b/cdist/conf/type/__motd/manifest
@@ -22,6 +22,9 @@
# Select motd source
if [ -f "$__object/parameter/source" ]; then
source="$(cat "$__object/parameter/source")"
+ if [ "$source" = "-" ]; then
+ source="${__object}/stdin"
+ fi
else
source="$__type/files/motd"
fi
@@ -30,10 +33,14 @@ os=$(cat "$__global/explorer/os")
case "$os" in
- debian|ubuntu|devuan)
- destination=/etc/motd.tail
+ freebsd)
+ # FreeBSD uses motd.template to prepend system information on boot
+ # (this actually only applies starting with version 13,
+ # but we fix that for whatever version in gencode-remote)
+ destination=/etc/motd.template
;;
*)
+ # Most UNIX systems, including other Linux and OpenBSD just use /etc/motd
destination=/etc/motd
;;
esac
diff --git a/cdist/conf/type/__mount/gencode-remote b/cdist/conf/type/__mount/gencode-remote
index 66d85f88..b2096764 100755
--- a/cdist/conf/type/__mount/gencode-remote
+++ b/cdist/conf/type/__mount/gencode-remote
@@ -39,7 +39,7 @@ case "$state_should" in
printf ' -o %s' "$(cat "$__object/parameter/options")"
fi
printf ' %s' "$(cat "$__object/parameter/device")"
- printf " %s\n" "$path"
+ printf ' %s\n' "$path"
else
# mount using existing fstab entry
printf 'mount "%s"\n' "$path"
diff --git a/cdist/conf/type/__mount/manifest b/cdist/conf/type/__mount/manifest
index 73937899..999d806c 100755
--- a/cdist/conf/type/__mount/manifest
+++ b/cdist/conf/type/__mount/manifest
@@ -31,7 +31,7 @@ printf " %s" "$type"
options="$(cat "$__object/parameter/options")"
printf " %s" "$options"
printf " %s" "$(cat "$__object/parameter/dump")"
-printf " %s\n" "$(cat "$__object/parameter/pass")"
+printf ' %s\n' "$(cat "$__object/parameter/pass")"
) | \
__block "$__object_name" \
--file "/etc/fstab" \
diff --git a/cdist/conf/type/__mysql_database/explorer/state b/cdist/conf/type/__mysql_database/explorer/state
new file mode 100755
index 00000000..79858695
--- /dev/null
+++ b/cdist/conf/type/__mysql_database/explorer/state
@@ -0,0 +1,33 @@
+#!/bin/sh -e
+#
+# 2020 Ander Punnar (ander-at-kvlt-dot-ee)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+if [ -f "$__object/parameter/name" ]
+then
+ name="$( cat "$__object/parameter/name" )"
+else
+ name="$__object_id"
+fi
+
+if [ -n "$( mysql -B -N -e "show databases like '$name'" )" ]
+then
+ echo 'present'
+else
+ echo 'absent'
+fi
diff --git a/cdist/conf/type/__mysql_database/gencode-remote b/cdist/conf/type/__mysql_database/gencode-remote
index 23e51b05..1bdb2b11 100755
--- a/cdist/conf/type/__mysql_database/gencode-remote
+++ b/cdist/conf/type/__mysql_database/gencode-remote
@@ -1,6 +1,6 @@
#!/bin/sh -e
#
-# 2012 Benedikt Koeppel (code@benediktkoeppel.ch)
+# 2020 Ander Punnar (ander-at-kvlt-dot-ee)
#
# This file is part of cdist.
#
@@ -17,38 +17,30 @@
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see .
#
-#
-# if --database was specified
-if [ -f "$__object/parameter/name" ]; then
- database="$(cat "$__object/parameter/name")"
-else # otherwise use the object id as database name
- database="$__object_id"
+state_is="$( cat "$__object/explorer/state" )"
+
+state_should="$( cat "$__object/parameter/state" )"
+
+if [ "$state_is" = "$state_should" ]
+then
+ exit 0
fi
-cat <<-EOFF
-mysql -u root <<-EOF
- CREATE DATABASE IF NOT EXISTS $database
-EOF
-EOFF
-
-# if --user was specified
-if [ -f "$__object/parameter/user" ]; then
- user="$(cat "$__object/parameter/user")"
-
- # if --password was specified
- if [ -f "$__object/parameter/password" ]; then
- password="$(cat "$__object/parameter/password")"
- cat <<-EOFF
- mysql -u root <<-EOF
- GRANT ALL PRIVILEGES ON $database.* to '$user'@'localhost' IDENTIFIED BY '$password';
-EOF
-EOFF
- else
- cat <<-EOFF
- mysql -u root <<-EOF
- GRANT ALL PRIVILEGES ON $database.* to '$user'@'localhost';
-EOF
-EOFF
- fi
+if [ -f "$__object/parameter/name" ]
+then
+ name="$( cat "$__object/parameter/name" )"
+else
+ name="$__object_id"
fi
+
+case "$state_should" in
+ present)
+ echo "mysql -e 'create database \`$name\`'"
+ echo "create database $name" >> "$__messages_out"
+ ;;
+ absent)
+ echo "mysql -e 'drop database \`$name\`'"
+ echo "drop database $name" >> "$__messages_out"
+ ;;
+esac
diff --git a/cdist/conf/type/__mysql_database/man.rst b/cdist/conf/type/__mysql_database/man.rst
index 1e245a08..b3b56b5f 100644
--- a/cdist/conf/type/__mysql_database/man.rst
+++ b/cdist/conf/type/__mysql_database/man.rst
@@ -8,24 +8,24 @@ cdist-type__mysql_database - Manage a MySQL database
DESCRIPTION
-----------
-This cdist type allows you to install a MySQL database.
+Create MySQL database and optionally user with all privileges.
-REQUIRED PARAMETERS
--------------------
-None.
OPTIONAL PARAMETERS
-------------------
name
- The name of the database to install
- defaults to the object id
+ Name of database. Defaults to object id.
user
- A user that should have access to the database
+ Create user and give all privileges to database.
password
- The password for the user who manages the database
+ Password for user.
+
+state
+ Defaults to present.
+ If absent and user is also set, both will be removed (with privileges).
EXAMPLES
@@ -33,17 +33,23 @@ EXAMPLES
.. code-block:: sh
- __mysql_database "cdist" --name "cdist" --user "myuser" --password "mypwd"
+ # just create database
+ __mysql_database foo
+
+ # create database with respective user with all privileges to database
+ __mysql_database bar \
+ --user name \
+ --password secret
AUTHORS
-------
-Benedikt Koeppel
+Ander Punnar
COPYING
-------
-Copyright \(C) 2012 Benedikt Koeppel. You can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
+Copyright \(C) 2020 Ander Punnar. You can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or (at your option) any
+later version.
diff --git a/cdist/conf/type/__mysql_database/manifest b/cdist/conf/type/__mysql_database/manifest
new file mode 100755
index 00000000..a3c9ed5d
--- /dev/null
+++ b/cdist/conf/type/__mysql_database/manifest
@@ -0,0 +1,52 @@
+#!/bin/sh -e
+#
+# 2020 Ander Punnar (ander-at-kvlt-dot-ee)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+if [ -f "$__object/parameter/user" ]
+then
+ user="$( cat "$__object/parameter/user" )"
+fi
+
+if [ -f "$__object/parameter/password" ]
+then
+ password="$( cat "$__object/parameter/password" )"
+fi
+
+if [ -n "$user" ] && [ -n "$password" ]
+then
+ if [ -f "$__object/parameter/name" ]
+ then
+ database="$( cat "$__object/parameter/name" )"
+ else
+ database="$__object_id"
+ fi
+
+ state_should="$( cat "$__object/parameter/state" )"
+
+ __mysql_user "$user" \
+ --password "$password" \
+ --state "$state_should"
+
+ # removing user should remove all user's privileges
+ require="__mysql_user/$user" \
+ __mysql_privileges "$database/$user" \
+ --database "$database" \
+ --user "$user" \
+ --state "$state_should"
+fi
diff --git a/cdist/conf/type/__mysql_database/parameter/default/state b/cdist/conf/type/__mysql_database/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__mysql_database/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__mysql_database/parameter/optional b/cdist/conf/type/__mysql_database/parameter/optional
index 756afee7..6c0b1e85 100644
--- a/cdist/conf/type/__mysql_database/parameter/optional
+++ b/cdist/conf/type/__mysql_database/parameter/optional
@@ -1,3 +1,4 @@
name
user
password
+state
diff --git a/cdist/conf/type/__mysql_privileges/explorer/state b/cdist/conf/type/__mysql_privileges/explorer/state
new file mode 100755
index 00000000..4f13a70c
--- /dev/null
+++ b/cdist/conf/type/__mysql_privileges/explorer/state
@@ -0,0 +1,40 @@
+#!/bin/sh -e
+#
+# 2020 Ander Punnar (ander-at-kvlt-dot-ee)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+privileges="$( cat "$__object/parameter/privileges" )"
+
+database="$( cat "$__object/parameter/database" )"
+
+table="$( cat "$__object/parameter/table" )"
+
+user="$( cat "$__object/parameter/user" )"
+
+host="$( cat "$__object/parameter/host" )"
+
+check_privileges="$(
+ mysql -B -N -e "show grants for '$user'@'$host'" \
+ | grep -Ei "^grant $privileges on .$database.\..?$table.? to " || true )"
+
+if [ -n "$check_privileges" ]
+then
+ echo 'present'
+else
+ echo 'absent'
+fi
diff --git a/cdist/conf/type/__mysql_privileges/gencode-remote b/cdist/conf/type/__mysql_privileges/gencode-remote
new file mode 100755
index 00000000..0656699f
--- /dev/null
+++ b/cdist/conf/type/__mysql_privileges/gencode-remote
@@ -0,0 +1,55 @@
+#!/bin/sh -e
+#
+# 2020 Ander Punnar (ander-at-kvlt-dot-ee)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+state_is="$( cat "$__object/explorer/state" )"
+
+state_should="$( cat "$__object/parameter/state" )"
+
+if [ "$state_is" = "$state_should" ]
+then
+ exit 0
+fi
+
+privileges="$( cat "$__object/parameter/privileges" )"
+
+database="$( cat "$__object/parameter/database" )"
+
+table="$( cat "$__object/parameter/table" )"
+
+user="$( cat "$__object/parameter/user" )"
+
+host="$( cat "$__object/parameter/host" )"
+
+if [ "$table" != '*' ]
+then
+ # shellcheck disable=SC2016
+ table="$( printf '`%s`' "$table" )"
+fi
+
+case "$state_should" in
+ present)
+ echo "mysql -e 'grant $privileges on \`$database\`.$table to \`$user\`@\`$host\`'"
+ echo "grant $privileges on $database.$table to $user@$host" >> "$__messages_out"
+ ;;
+ absent)
+ echo "mysql -e 'revoke $privileges on \`$database\`.$table from \`$user\`@\`$host\`'"
+ echo "revoke $privileges on $database.$table from $user@$host" >> "$__messages_out"
+ ;;
+esac
diff --git a/cdist/conf/type/__mysql_privileges/man.rst b/cdist/conf/type/__mysql_privileges/man.rst
new file mode 100644
index 00000000..b72c9eba
--- /dev/null
+++ b/cdist/conf/type/__mysql_privileges/man.rst
@@ -0,0 +1,57 @@
+cdist-type__mysql_privileges(7)
+===============================
+
+NAME
+----
+cdist-type__mysql_privileges - Manage MySQL privileges
+
+
+DESCRIPTION
+-----------
+
+Grant and revoke privileges of MySQL user.
+
+
+REQUIRED PARAMETERS
+-------------------
+database
+ Name of database.
+
+user
+ Name of user.
+
+
+OPTIONAL PARAMETERS
+-------------------
+privileges
+ Defaults to "all".
+
+table
+ Defaults to "*".
+
+host
+ Defaults to localhost.
+
+state
+ "present" grants and "absent" revokes. Defaults to present.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ __mysql_privileges user-to-db --database db --user user
+
+
+AUTHORS
+-------
+Ander Punnar
+
+
+COPYING
+-------
+Copyright \(C) 2020 Ander Punnar. You can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or (at your option) any
+later version.
diff --git a/cdist/conf/type/__mysql_privileges/parameter/default/host b/cdist/conf/type/__mysql_privileges/parameter/default/host
new file mode 100644
index 00000000..2fbb50c4
--- /dev/null
+++ b/cdist/conf/type/__mysql_privileges/parameter/default/host
@@ -0,0 +1 @@
+localhost
diff --git a/cdist/conf/type/__mysql_privileges/parameter/default/privileges b/cdist/conf/type/__mysql_privileges/parameter/default/privileges
new file mode 100644
index 00000000..5472efad
--- /dev/null
+++ b/cdist/conf/type/__mysql_privileges/parameter/default/privileges
@@ -0,0 +1 @@
+all privileges
diff --git a/cdist/conf/type/__mysql_privileges/parameter/default/state b/cdist/conf/type/__mysql_privileges/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__mysql_privileges/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__mysql_privileges/parameter/default/table b/cdist/conf/type/__mysql_privileges/parameter/default/table
new file mode 100644
index 00000000..72e8ffc0
--- /dev/null
+++ b/cdist/conf/type/__mysql_privileges/parameter/default/table
@@ -0,0 +1 @@
+*
diff --git a/cdist/conf/type/__mysql_privileges/parameter/optional b/cdist/conf/type/__mysql_privileges/parameter/optional
new file mode 100644
index 00000000..d4ed5bc5
--- /dev/null
+++ b/cdist/conf/type/__mysql_privileges/parameter/optional
@@ -0,0 +1,4 @@
+privileges
+table
+host
+state
diff --git a/cdist/conf/type/__mysql_privileges/parameter/required b/cdist/conf/type/__mysql_privileges/parameter/required
new file mode 100644
index 00000000..152b4a1e
--- /dev/null
+++ b/cdist/conf/type/__mysql_privileges/parameter/required
@@ -0,0 +1,2 @@
+database
+user
diff --git a/cdist/conf/type/__mysql_user/explorer/state b/cdist/conf/type/__mysql_user/explorer/state
new file mode 100755
index 00000000..6817ee9d
--- /dev/null
+++ b/cdist/conf/type/__mysql_user/explorer/state
@@ -0,0 +1,54 @@
+#!/bin/sh -e
+#
+# 2020 Ander Punnar (ander-at-kvlt-dot-ee)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+if [ -f "$__object/parameter/name" ]
+then
+ name="$( cat "$__object/parameter/name" )"
+else
+ name="$__object_id"
+fi
+
+if [ -f "$__object/parameter/password" ]
+then
+ password="$( cat "$__object/parameter/password" )"
+else
+ password=''
+fi
+
+host="$( cat "$__object/parameter/host" )"
+
+check_user="$( mysql -B -N -e "select user from mysql.user where user = '$name' and host = '$host'" )"
+
+if [ -n "$check_user" ]
+then
+ if [ -n "$password" ]
+ then
+ check_password="$( mysql -B -N -e "select user from mysql.user where user = '$name' and host = '$host' and password = password( '$password' )" )"
+ fi
+
+ if [ -n "$password" ] && [ -z "$check_password" ]
+ then
+ echo 'change-password'
+ else
+ echo 'present'
+ fi
+else
+ echo 'absent'
+fi
diff --git a/cdist/conf/type/__mysql_user/gencode-remote b/cdist/conf/type/__mysql_user/gencode-remote
new file mode 100755
index 00000000..5f13bc87
--- /dev/null
+++ b/cdist/conf/type/__mysql_user/gencode-remote
@@ -0,0 +1,68 @@
+#!/bin/sh -e
+#
+# 2020 Ander Punnar (ander-at-kvlt-dot-ee)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+state_is="$( cat "$__object/explorer/state" )"
+
+state_should="$( cat "$__object/parameter/state" )"
+
+if [ "$state_is" = "$state_should" ]
+then
+ exit 0
+fi
+
+if [ -f "$__object/parameter/name" ]
+then
+ name="$( cat "$__object/parameter/name" )"
+else
+ name="$__object_id"
+fi
+
+host="$( cat "$__object/parameter/host" )"
+
+if [ -f "$__object/parameter/password" ]
+then
+ password="$( cat "$__object/parameter/password" )"
+else
+ if [ "$state_should" = 'present' ]
+ then
+ echo '--password needed' >&2
+ exit 1
+ else
+ password=''
+ fi
+fi
+
+if [ "$state_is" = 'absent' ] && [ "$state_should" = 'present' ]
+then
+ echo "mysql -e 'create user \`$name\`@\`$host\` identified by \"$password\"'"
+ echo "create user $name@$host" >> "$__messages_out"
+
+elif [ "$state_is" != 'absent' ] && [ "$state_should" = 'absent' ]
+then
+ echo "mysql -e 'drop user \`$name\`@\`$host\`'"
+ echo "drop user $name@$host" >> "$__messages_out"
+
+elif [ "$state_is" = 'change-password' ]
+then
+ # this only works with MySQL 5.7.6 and later or MariaDB 10.1.20 and later
+ echo "mysql -e 'alter user \`$name\`@\`$host\` identified by \"$password\"'"
+ echo "mysql -e 'flush privileges'"
+ echo "change password $name@$host" >> "$__messages_out"
+fi
diff --git a/cdist/conf/type/__mysql_user/man.rst b/cdist/conf/type/__mysql_user/man.rst
new file mode 100644
index 00000000..c2b222d5
--- /dev/null
+++ b/cdist/conf/type/__mysql_user/man.rst
@@ -0,0 +1,48 @@
+cdist-type__mysql_user(7)
+=========================
+
+NAME
+----
+cdist-type__mysql_user - Manage a MySQL user
+
+
+DESCRIPTION
+-----------
+
+Create MySQL user or change password for the user.
+
+
+OPTIONAL PARAMETERS
+-------------------
+name
+ Name of user. Defaults to object id.
+
+host
+ Host of user. Defaults to localhost.
+
+password
+ Password of user.
+
+state
+ Defaults to present.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ __mysql_user user --password secret
+
+
+AUTHORS
+-------
+Ander Punnar
+
+
+COPYING
+-------
+Copyright \(C) 2020 Ander Punnar. You can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation, either version 3 of the License, or (at your option) any
+later version.
diff --git a/cdist/conf/type/__mysql_user/parameter/default/host b/cdist/conf/type/__mysql_user/parameter/default/host
new file mode 100644
index 00000000..2fbb50c4
--- /dev/null
+++ b/cdist/conf/type/__mysql_user/parameter/default/host
@@ -0,0 +1 @@
+localhost
diff --git a/cdist/conf/type/__mysql_user/parameter/default/state b/cdist/conf/type/__mysql_user/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__mysql_user/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__mysql_user/parameter/optional b/cdist/conf/type/__mysql_user/parameter/optional
new file mode 100644
index 00000000..a286266c
--- /dev/null
+++ b/cdist/conf/type/__mysql_user/parameter/optional
@@ -0,0 +1,4 @@
+name
+host
+password
+state
diff --git a/cdist/conf/type/__openldap_server/gencode-remote b/cdist/conf/type/__openldap_server/gencode-remote
new file mode 100644
index 00000000..b1e98f8c
--- /dev/null
+++ b/cdist/conf/type/__openldap_server/gencode-remote
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+manager_dn=$(cat "${__object}/parameter/manager-dn")
+manager_password=$(cat "${__object}/parameter/manager-password")
+description=$(cat "${__object}/parameter/description")
+suffix=$(cat "${__object}/parameter/suffix")
+suffix_dc=$(printf "%s" "${suffix}" | awk -F',' '{print $1}' | awk -F'=' '{print $2}')
+
+SLAPD_IPC=$(tr '\n' ' ' < "${__object}/parameter/slapd-url" | awk '{ print $1}')
+
+cat <&1 > /dev/null; then
+ # Already exists, use ldapmodify
+ ldapmodify -xZ -D "${manager_dn}" -w "${manager_password}" -H '${SLAPD_IPC}' <
+Evilham
+
+
+COPYING
+-------
+Copyright \(C) 2020 ungleich glarus ag. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__openldap_server/manifest b/cdist/conf/type/__openldap_server/manifest
new file mode 100644
index 00000000..2aeece26
--- /dev/null
+++ b/cdist/conf/type/__openldap_server/manifest
@@ -0,0 +1,297 @@
+#!/bin/sh
+
+name="${__target_host}"
+manager_dn=$(cat "${__object}/parameter/manager-dn")
+manager_password_hash=$(cat "${__object}/parameter/manager-password-hash")
+serverid=$(cat "${__object}/parameter/serverid")
+suffix=$(cat "${__object}/parameter/suffix")
+slapd_modules=$(cat "${__object}/parameter/module" 2>/dev/null || true)
+schemas=$(cat "${__object}/parameter/schema")
+slapd_urls=$(tr '\n' ' ' < "${__object}/parameter/slapd-url")
+tls_cipher_suite=$(cat "${__object}/parameter/tls-cipher-suite" 2>/dev/null || true)
+extra_config=$(cat "${__object}/parameter/extra-config" || true)
+
+
+os="$(cat "${__global}/explorer/os")"
+
+# Setup OS-dependent vars
+CONF_OWNER="root"
+CONF_GROUP="root"
+case "${os}" in
+ freebsd)
+ PKGS="openldap-server"
+ ETC="/usr/local/etc"
+ SLAPD_DIR="/usr/local/etc/openldap"
+ SLAPD_DATA_DIR="/var/db/openldap-data"
+ SLAPD_RUN_DIR="/var/run/openldap"
+ SLAPD_MODULE_PATH="/usr/local/libexec/openldap"
+ SLAPD_MODULE_TYPE="la"
+ if [ -z "${slapd_modules}" ]; then
+ # It looks like ppolicy and syncprov must be compiled
+ slapd_modules="back_mdb back_monitor"
+ fi
+ CONF_OWNER="ldap"
+ CONF_GROUP="ldap"
+ if [ -z "${tls_cipher_suite}" ]; then
+ # TODO: research default for FreeBSD. 'NORMAL' appears to not work
+ tls_cipher_suite="HIGH:MEDIUM:+SSLv2"
+ fi
+ ;;
+ debian|ubuntu|devuan)
+ PKGS="slapd ldap-utils"
+ ETC="/etc"
+ SLAPD_DIR="/etc/ldap"
+ SLAPD_DATA_DIR="/var/lib/ldap"
+ SLAPD_RUN_DIR="/var/run/slapd"
+ SLAPD_MODULE_PATH="/usr/lib/ldap"
+ SLAPD_MODULE_TYPE="la"
+ if [ -z "${slapd_modules}" ]; then
+ slapd_modules="back_mdb ppolicy syncprov back_monitor"
+ fi
+ CONF_OWNER="openldap"
+ CONF_GROUP="openldap"
+ if [ -z "${tls_cipher_suite}" ]; then
+ tls_cipher_suite="NORMAL"
+ fi
+ ;;
+ alpine)
+ PKGS="openldap openldap-clients"
+ ETC="/etc"
+ SLAPD_DIR="/etc/openldap"
+ SLAPD_DATA_DIR="/var/lib/openldap"
+ SLAPD_RUN_DIR="/var/run/openldap"
+ SLAPD_MODULE_PATH="/usr/lib/openldap"
+ SLAPD_MODULE_TYPE="so"
+ if [ -z "${slapd_modules}" ]; then
+ slapd_modules="back_mdb ppolicy syncprov back_monitor"
+ PKGS="$PKGS openldap-back-mdb openldap-back-monitor openldap-overlay-all"
+ fi
+ CONF_OWNER="ldap"
+ CONF_GROUP="$SLAPD_USER"
+ if [ -z "${tls_cipher_suite}" ]; then
+ tls_cipher_suite="DEFAULT"
+ fi
+ ;;
+ *)
+ echo "Don't know the openldap defaults for: $os" >&2
+ exit 1
+ ;;
+esac
+
+PKG_MAIN=$(echo "${PKGS}" | awk '{print $1;}')
+
+
+# Determine if __letsencrypt_cert is to be used and setup vars accordingly
+if [ -f "${__object}/parameter/tls-cert" ]; then
+ tls_cert=$(cat "${__object}/parameter/tls-cert")
+
+ if [ ! -f "${__object}/parameter/tls-privkey" ]; then
+ echo "When tls-cert is defined, tls-privkey is also required." >&2
+ exit 1
+ fi
+ tls_privkey=$(cat "${__object}/parameter/tls-privkey")
+
+ if [ ! -f "${__object}/parameter/tls-ca" ]; then
+ echo "When tls-cert is defined, tls-ca is also required." >&2
+ exit 1
+ fi
+ tls_ca=$(cat "${__object}/parameter/tls-ca")
+
+ _skip_letsencrypt_cert="YES"
+else
+ if [ ! -f "${__object}/parameter/admin-email" ]; then
+ echo "When using __letsencrypt_cert, admin-email is also required." >&2
+ exit 1
+ fi
+ admin_email=$(cat "${__object}/parameter/admin-email")
+
+ tls_cert="${SLAPD_DIR}/sasl2/cert.pem"
+ tls_privkey="${SLAPD_DIR}/sasl2/privkey.pem"
+ tls_ca="${SLAPD_DIR}/sasl2/chain.pem"
+fi
+
+mkdir "${__object}/files"
+ldapconf="${__object}/files/ldapconf"
+
+replication=""
+if [ -f "${__object}/parameter/replicate" ]; then
+ replication=yes
+
+ if [ ! -f "${__object}/parameter/syncrepl-searchbase" ]; then
+ echo "Requiring the searchbase for replication" >&2
+ exit 1
+ fi
+ syncrepl_searchbase=$(cat "${__object}/parameter/syncrepl-searchbase")
+
+ if [ ! -f "${__object}/parameter/syncrepl-credentials" ]; then
+ echo "Requiring credentials for replication" >&2
+ exit 1
+ fi
+
+ syncrepl_credentials=$(cat "${__object}/parameter/syncrepl-credentials")
+
+ if [ ! -f "${__object}/parameter/syncrepl-host" ]; then
+ echo "Requiring host(s) for replication" >&2
+ exit 1
+ fi
+ syncrepl_hosts=$(cat "${__object}/parameter/syncrepl-host")
+
+fi
+
+# Install required packages
+for pkg in ${PKGS}; do
+ __package "${pkg}"
+done
+
+
+require="__package/${PKG_MAIN}" __start_on_boot slapd
+
+# Setup -h flag for the listeners. See man slapd (-h flag).
+case "${os}" in
+ freebsd)
+ require="__start_on_boot/slapd" __key_value \
+ --file "/etc/rc.conf" \
+ --key "slapd_flags" \
+ --value "\"-h '${slapd_urls}'\"" \
+ --delimiter "=" \
+ --comment "# LDAP Listener URLs" \
+ "${__target_host}__slapd_flags"
+ ;;
+ debian|ubuntu|devuan)
+ require="__package/${PKG_MAIN}" __line rm_slapd_conf \
+ --file ${ETC}/default/slapd \
+ --regex 'SLAPD_CONF=.*' \
+ --state absent
+
+ require="__package/${PKG_MAIN}" __line rm_slapd_services \
+ --file ${ETC}/default/slapd \
+ --regex 'SLAPD_SERVICES=.*' \
+ --state absent
+
+ require="__line/rm_slapd_conf" __line add_slapd_conf \
+ --file ${ETC}/default/slapd \
+ --line "SLAPD_CONF=${SLAPD_DIR}/slapd.conf" \
+ --state present
+
+ require="__line/rm_slapd_services" __line add_slapd_services \
+ --file ${ETC}/default/slapd \
+ --line "SLAPD_SERVICES=\"${slapd_urls}\"" \
+ --state present
+ ;;
+ alpine)
+ require="__package/${PKG_MAIN}" __line add_slapd_services \
+ --file ${ETC}/conf.d/slapd \
+ --line "command_args=\"-h '${slapd_urls}'\"" \
+ --state present
+ ;;
+ *)
+ # Nothing to do here, move on.
+ ;;
+esac
+
+
+if [ -z "${_skip_letsencrypt_cert}" ]; then
+ if [ -f "${__object}/parameter/staging" ]; then
+ staging="--staging"
+ else
+ staging=""
+ fi
+
+ # shellcheck disable=SC2086
+ __directory ${SLAPD_DIR}/sasl2
+ require="__directory/${SLAPD_DIR}/sasl2" __letsencrypt_cert "${name}" \
+ --admin-email "${admin_email}" \
+ --renew-hook "cp ${ETC}/letsencrypt/live/${name}/*.pem ${SLAPD_DIR}/sasl2 && chown -R ${CONF_OWNER}:${CONF_GROUP} ${SLAPD_DIR}/sasl2 && service slapd restart" \
+ --automatic-renewal "${staging}"
+fi
+
+require="__package/${PKG_MAIN}" __directory ${SLAPD_DIR}/slapd.d --state absent
+
+if [ -z "${_skip_letsencrypt_cert}" ]; then
+ require="__package/${PKG_MAIN} __letsencrypt_cert/${name}" \
+ __file "${SLAPD_DIR}/slapd.conf" --owner "${CONF_OWNER}" --group "${CONF_GROUP}" --mode 644 \
+ --source "${ldapconf}"
+else
+ require="__package/${PKG_MAIN}" \
+ __file "${SLAPD_DIR}/slapd.conf" --owner "${CONF_OWNER}" --group "${CONF_GROUP}" --mode 644 \
+ --source "${ldapconf}"
+fi
+
+# Start slapd.conf
+cat << EOF > "${ldapconf}"
+pidfile ${SLAPD_RUN_DIR}/slapd.pid
+argsfile ${SLAPD_RUN_DIR}/slapd.args
+
+TLSCipherSuite ${tls_cipher_suite}
+TLSCertificateFile ${tls_cert}
+TLSCertificateKeyFile ${tls_privkey}
+TLSCACertificateFile ${tls_ca}
+
+disallow bind_anon
+require bind
+security tls=1
+EOF
+
+# Add specified schemas
+for schema in ${schemas}; do
+ echo "include ${SLAPD_DIR}/schema/${schema}.schema" >> "${ldapconf}"
+done
+
+# Add specified modules
+echo "modulepath ${SLAPD_MODULE_PATH}" >> "${ldapconf}"
+for module in ${slapd_modules}; do
+ echo "moduleload ${module}.${SLAPD_MODULE_TYPE}" >> "${ldapconf}"
+done
+
+# Rest of the config
+cat << EOF >> "${ldapconf}"
+loglevel 1024
+
+database mdb
+maxsize 1073741824
+
+suffix "${suffix}"
+directory ${SLAPD_DATA_DIR}
+rootdn "${manager_dn}"
+rootpw "${manager_password_hash}"
+
+index objectClass eq,pres
+index ou,cn,mail,surname,givenname eq,pres,sub
+index uidNumber,gidNumber,loginShell eq,pres
+index uid,memberUid eq,pres,sub
+index nisMapName,nisMapEntry eq,pres,sub
+index entryCSN,entryUUID eq
+
+${extra_config}
+
+serverid ${serverid}
+EOF
+
+# Setup replication
+if [ "${replication}" ]; then
+ rid=1;
+ for syncrepl in ${syncrepl_hosts}; do
+ cat <> "${ldapconf}"
+syncrepl rid=${rid}
+ provider=ldap://${syncrepl}
+ bindmethod=simple
+ starttls=yes
+ binddn="${manager_dn}"
+ credentials=${syncrepl_credentials}
+ searchbase="${syncrepl_searchbase}"
+ type=refreshAndPersist
+ retry="5 + 5 +"
+ interval=00:00:00:05
+EOF
+ rid=$((rid + 1))
+ done
+ cat <> "${ldapconf}"
+mirrormode true
+overlay syncprov
+syncprov-checkpoint 100 5
+syncprov-sessionlog 100
+
+database monitor
+limits dn.exact="${manager_dn}" time=unlimited size=unlimited
+EOF
+fi
diff --git a/cdist/conf/type/__openldap_server/parameter/boolean b/cdist/conf/type/__openldap_server/parameter/boolean
new file mode 100644
index 00000000..45056fe9
--- /dev/null
+++ b/cdist/conf/type/__openldap_server/parameter/boolean
@@ -0,0 +1,2 @@
+staging
+replicate
diff --git a/cdist/conf/type/__openldap_server/parameter/default/description b/cdist/conf/type/__openldap_server/parameter/default/description
new file mode 100644
index 00000000..6d8e37e1
--- /dev/null
+++ b/cdist/conf/type/__openldap_server/parameter/default/description
@@ -0,0 +1 @@
+Managed by cdist, do not edit manually.
diff --git a/cdist/conf/type/__openldap_server/parameter/default/schema b/cdist/conf/type/__openldap_server/parameter/default/schema
new file mode 100644
index 00000000..825bdb15
--- /dev/null
+++ b/cdist/conf/type/__openldap_server/parameter/default/schema
@@ -0,0 +1,12 @@
+corba
+core
+cosine
+duaconf
+dyngroup
+inetorgperson
+java
+misc
+nis
+openldap
+ppolicy
+collective
diff --git a/cdist/conf/type/__openldap_server/parameter/optional b/cdist/conf/type/__openldap_server/parameter/optional
new file mode 100644
index 00000000..71c64659
--- /dev/null
+++ b/cdist/conf/type/__openldap_server/parameter/optional
@@ -0,0 +1,9 @@
+description
+syncrepl-credentials
+syncrepl-searchbase
+admin-email
+tls-cipher-suite
+tls-cert
+tls-privkey
+tls-ca
+extra-config
diff --git a/cdist/conf/type/__openldap_server/parameter/optional_multiple b/cdist/conf/type/__openldap_server/parameter/optional_multiple
new file mode 100644
index 00000000..52a83d5c
--- /dev/null
+++ b/cdist/conf/type/__openldap_server/parameter/optional_multiple
@@ -0,0 +1,3 @@
+syncrepl-host
+module
+schema
diff --git a/cdist/conf/type/__openldap_server/parameter/required b/cdist/conf/type/__openldap_server/parameter/required
new file mode 100644
index 00000000..ff58158d
--- /dev/null
+++ b/cdist/conf/type/__openldap_server/parameter/required
@@ -0,0 +1,5 @@
+manager-dn
+manager-password
+manager-password-hash
+serverid
+suffix
diff --git a/cdist/conf/type/__openldap_server/parameter/required_multiple b/cdist/conf/type/__openldap_server/parameter/required_multiple
new file mode 100644
index 00000000..848b8dc2
--- /dev/null
+++ b/cdist/conf/type/__openldap_server/parameter/required_multiple
@@ -0,0 +1 @@
+slapd-url
\ No newline at end of file
diff --git a/cdist/conf/type/__openldap_server/singleton b/cdist/conf/type/__openldap_server/singleton
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__package/explorer/pkgng_exists b/cdist/conf/type/__package/explorer/pkgng_exists
index 355c5d65..6d69ba14 100755
--- a/cdist/conf/type/__package/explorer/pkgng_exists
+++ b/cdist/conf/type/__package/explorer/pkgng_exists
@@ -21,7 +21,7 @@
# Retrieve the status of a package - parsed dpkg output
#
-if [ "$($__explorer/os)" = "freebsd" ]; then
+if [ "$("$__explorer/os")" = "freebsd" ]; then
command -v pkg
fi
diff --git a/cdist/conf/type/__package/manifest b/cdist/conf/type/__package/manifest
index fe7abedc..a453c32b 100755
--- a/cdist/conf/type/__package/manifest
+++ b/cdist/conf/type/__package/manifest
@@ -1,6 +1,7 @@
#!/bin/sh -e
#
# 2011-2013 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2019 Nico Schottelius (nico-cdist at schottelius.org)
#
# This file is part of cdist.
#
@@ -44,6 +45,7 @@ else
suse) type="zypper" ;;
openwrt) type="opkg" ;;
openbsd) type="pkg_openbsd" ;;
+ alpine) type="apk" ;;
*)
echo "Don't know how to manage packages on: $os" >&2
exit 1
@@ -55,8 +57,8 @@ state="$(cat "$__object/parameter/state")"
set -- "$@" "$__object_id" "--state" "$state"
cd "$__object/parameter"
-for property in $(ls .); do
- if [ "$property" != "type" -a "$property" != "state" ]; then
+for property in *; do
+ if [ "$property" != "type" ] && [ "$property" != "state" ]; then
set -- "$@" "--$property" "$(cat "$property")"
fi
done
diff --git a/cdist/conf/type/__package_pkg_openbsd/explorer/pkg_version b/cdist/conf/type/__package_apk/explorer/state
similarity index 70%
rename from cdist/conf/type/__package_pkg_openbsd/explorer/pkg_version
rename to cdist/conf/type/__package_apk/explorer/state
index 212f0d96..b477ca7c 100755
--- a/cdist/conf/type/__package_pkg_openbsd/explorer/pkg_version
+++ b/cdist/conf/type/__package_apk/explorer/state
@@ -1,7 +1,6 @@
#!/bin/sh
#
-# 2011 Andi Brönnimann (andi-cdist at v-net.ch)
-# Copyright 2017, Philippe Gregoire
+# 2019 Nico Schottelius (nico-cdist at schottelius.org)
#
# This file is part of cdist.
#
@@ -19,7 +18,7 @@
# along with cdist. If not, see .
#
#
-# Retrieve the status of a package - parsed dpkg output
+# Retrieve the status of a package - parsed apk output
#
if [ -f "$__object/parameter/name" ]; then
@@ -28,5 +27,12 @@ else
name="$__object_id"
fi
-#TODO: Is there a better way?
-pkg_info | grep "^$name-[0-9]" | sed 's|.*\(-[0-9][0-9.]*\).*|\1|' | sed 's/-//'
+# Remove the @.. repo tag for finding out whether it is installed
+# f.i. pass@testing => pass
+name="$(echo "$name" | sed 's/@.*//')"
+
+if [ "$(apk list -I "$name")" ]; then
+ echo present
+else
+ echo absent
+fi
diff --git a/cdist/conf/type/__package_apk/gencode-remote b/cdist/conf/type/__package_apk/gencode-remote
new file mode 100755
index 00000000..79e3d2b6
--- /dev/null
+++ b/cdist/conf/type/__package_apk/gencode-remote
@@ -0,0 +1,49 @@
+#!/bin/sh -e
+#
+# 2019 Nico Schottelius (nico-cdist at schottelius.org)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+#
+# Manage packages on Debian and co.
+#
+
+if [ -f "$__object/parameter/name" ]; then
+ name="$(cat "$__object/parameter/name")"
+else
+ name="$__object_id"
+fi
+
+state_should="$(cat "$__object/parameter/state")"
+state_is="$(cat "$__object/explorer/state")"
+
+# Nothing to be done
+[ "$state_is" = "$state_should" ] && exit 0
+
+case "$state_should" in
+ present)
+ echo "apk add -q '$name'"
+ echo "installed" >> "$__messages_out"
+ ;;
+ absent)
+ echo "apk del -q '$name'"
+ echo "removed" >> "$__messages_out"
+ ;;
+ *)
+ echo "Unknown state: $state_should" >&2
+ exit 1
+ ;;
+esac
diff --git a/cdist/conf/type/__package_apk/man.rst b/cdist/conf/type/__package_apk/man.rst
new file mode 100644
index 00000000..bc2408b4
--- /dev/null
+++ b/cdist/conf/type/__package_apk/man.rst
@@ -0,0 +1,55 @@
+cdist-type__package_akp(7)
+==========================
+
+NAME
+----
+cdist-type__package_akp - Manage packages with akp
+
+
+DESCRIPTION
+-----------
+apk is usually used on Alpine to manage packages.
+
+
+REQUIRED PARAMETERS
+-------------------
+None
+
+
+OPTIONAL PARAMETERS
+-------------------
+name
+ If supplied, use the name and not the object id as the package name.
+
+state
+ Either "present" or "absent", defaults to "present"
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # Ensure zsh in installed
+ __package_apk zsh --state present
+
+ # Remove package
+ __package_apk apache2 --state absent
+
+
+SEE ALSO
+--------
+:strong:`cdist-type__package`\ (7)
+
+
+AUTHORS
+-------
+Nico Schottelius
+
+
+COPYING
+-------
+Copyright \(C) 2019 Nico Schottelius. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__package_apk/nonparallel b/cdist/conf/type/__package_apk/nonparallel
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__package_apk/parameter/default/state b/cdist/conf/type/__package_apk/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__package_apk/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__package_apk/parameter/optional b/cdist/conf/type/__package_apk/parameter/optional
new file mode 100644
index 00000000..1b423dc4
--- /dev/null
+++ b/cdist/conf/type/__package_apk/parameter/optional
@@ -0,0 +1,2 @@
+name
+state
diff --git a/cdist/conf/type/__package_apt/explorer/state b/cdist/conf/type/__package_apt/explorer/state
index 04926b60..7ccd6fce 100755
--- a/cdist/conf/type/__package_apt/explorer/state
+++ b/cdist/conf/type/__package_apt/explorer/state
@@ -30,8 +30,9 @@ fi
# Except dpkg failing, if package is not known / installed
packages="$(apt-cache showpkg "$name" | sed -e "1,/Reverse Provides:/d" | cut -d ' ' -f 1) $name"
for p in $packages; do
- if [ -n "$(dpkg -s "$p" 2>/dev/null | grep "^Status: install ok installed$")" ]; then
- echo "present $p"
+ if dpkg -s "$p" 2>/dev/null | grep --quiet "^Status: install ok installed$" ; then
+ version=$(dpkg -s "$p" 2>/dev/null | grep "^Version:" | cut -d ' ' -f 2)
+ echo "present $p $version"
exit 0
fi
done
diff --git a/cdist/conf/type/__package_apt/gencode-remote b/cdist/conf/type/__package_apt/gencode-remote
index e3e31c2b..fbfca330 100755
--- a/cdist/conf/type/__package_apt/gencode-remote
+++ b/cdist/conf/type/__package_apt/gencode-remote
@@ -29,12 +29,31 @@ fi
state_should="$(cat "$__object/parameter/state")"
+version_param="$__object/parameter/version"
+
+version=""
+if [ -f "$version_param" ]; then
+ version="$(cat "$version_param")"
+fi
+
if [ -f "$__object/parameter/target-release" ]; then
target_release="--target-release $(cat "$__object/parameter/target-release")"
else
target_release=""
fi
+if [ -f "$__object/parameter/install-recommends" ]; then
+ # required if __apt_norecommends is used
+ recommendsparam="-o APT::Install-Recommends=1"
+else
+ recommendsparam="-o APT::Install-Recommends=0"
+fi
+
+if [ -f "$__object/parameter/purge-if-absent" ]; then
+ purgeparam="--purge"
+else
+ purgeparam=""
+fi
# FIXME: use grep directly, state is a list, not a line!
@@ -42,22 +61,43 @@ state_is="$(cat "$__object/explorer/state")"
case "$state_is" in
present*)
name="$(echo "$state_is" | cut -d ' ' -f 2)"
+ version_is="$(echo "$state_is" | cut -d ' ' -f 3)"
state_is="present"
;;
+ *)
+ version_is=""
+ ;;
esac
+if [ "$state_is" = "$state_should" ]; then
+ if [ -z "$version" ] || [ "$version" = "$version_is" ]; then
+ exit 0;
+ fi
+fi
+
# Hint if we need to avoid questions at some point:
# DEBIAN_PRIORITY=critical can reduce the number of questions
-aptget="DEBIAN_FRONTEND=noninteractive apt-get --quiet --yes --no-install-recommends -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\""
-
-[ "$state_is" = "$state_should" ] && exit 0
+aptget="DEBIAN_FRONTEND=noninteractive apt-get --quiet --yes -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\""
case "$state_should" in
present)
- echo $aptget install $target_release \"$name\"
+ # following is bit ugly, but important hack.
+ # due to how cdist config run works, there isn't
+ # currently better way to do it :(
+ cat << EOF
+if [ ! -f /var/cache/apt/pkgcache.bin ] || [ "\$( stat --format %Y /var/cache/apt/pkgcache.bin )" -lt "\$( date +%s -d '-1 day' )" ]
+then echo apt-get update > /dev/null 2>&1 || true
+fi
+EOF
+ if [ -n "$version" ]; then
+ name="${name}=${version}"
+ fi
+ echo "$aptget $recommendsparam install $target_release '$name'"
+ echo "installed" >> "$__messages_out"
;;
absent)
- echo $aptget remove \"$name\"
+ echo "$aptget remove $purgeparam '$name'"
+ echo "removed" >> "$__messages_out"
;;
*)
echo "Unknown state: $state_should" >&2
diff --git a/cdist/conf/type/__package_apt/man.rst b/cdist/conf/type/__package_apt/man.rst
index 0a7958d4..4e6101a5 100644
--- a/cdist/conf/type/__package_apt/man.rst
+++ b/cdist/conf/type/__package_apt/man.rst
@@ -9,7 +9,12 @@ cdist-type__package_apt - Manage packages with apt-get
DESCRIPTION
-----------
apt-get is usually used on Debian and variants (like Ubuntu) to
-manage packages.
+manage packages. The package will be installed without recommended
+or suggested packages. If such packages are required, install them
+separatly or use the parameter ``--install-recommends``.
+
+This type will also update package index, if it is older
+than one day, to avoid missing package error messages.
REQUIRED PARAMETERS
@@ -20,7 +25,7 @@ None
OPTIONAL PARAMETERS
-------------------
name
- If supplied, use the name and not the object id as the package name.
+ If supplied, use the name and not the object id as the package name.
state
Either "present" or "absent", defaults to "present"
@@ -29,6 +34,27 @@ target-release
Passed on to apt-get install, see apt-get(8).
Essentially allows you to retrieve packages from a different release
+version
+ The version of the package to install. Default is to install the version
+ chosen by the local package manager.
+
+
+BOOLEAN PARAMETERS
+------------------
+install-recommends
+ If the package will be installed, it also installs recommended packages
+ with it. It will not install recommended packages if the original package
+ is already installed.
+
+ In most cases, it is recommended to install recommended packages separatly
+ to control which additional packages will be installed to avoid useless
+ installed packages.
+
+purge-if-absent
+ If this parameter is given when state is `absent`, the package is
+ purged from the system (using `--purge`).
+
+
EXAMPLES
--------
diff --git a/cdist/conf/type/__package_apt/parameter/boolean b/cdist/conf/type/__package_apt/parameter/boolean
new file mode 100644
index 00000000..a2e433f3
--- /dev/null
+++ b/cdist/conf/type/__package_apt/parameter/boolean
@@ -0,0 +1,2 @@
+install-recommends
+purge-if-absent
diff --git a/cdist/conf/type/__package_dpkg/explorer/pkg_state b/cdist/conf/type/__package_dpkg/explorer/pkg_state
new file mode 100644
index 00000000..d7487ed8
--- /dev/null
+++ b/cdist/conf/type/__package_dpkg/explorer/pkg_state
@@ -0,0 +1,11 @@
+#!/bin/sh -e
+
+package=$( basename "$__object_id" )
+
+dpkg_status="$(dpkg-query --show --showformat='${db:Status-Abbrev} ${binary:Package}_${Version}_${Architecture}.deb\n' "${package%%_*}" 2>/dev/null || true)"
+
+if echo "$dpkg_status" | grep -q '^ii'; then
+ echo "${dpkg_status##* }"
+fi
+
+
diff --git a/cdist/conf/type/__package_dpkg/gencode-remote b/cdist/conf/type/__package_dpkg/gencode-remote
index 90921ae3..1c271748 100755
--- a/cdist/conf/type/__package_dpkg/gencode-remote
+++ b/cdist/conf/type/__package_dpkg/gencode-remote
@@ -1,6 +1,7 @@
#!/bin/sh -e
#
# 2013 Tomas Pospisek (tpo_deb sourcepole.ch)
+# 2018 Thomas Eckert (tom at it-eckert.de)
#
# This file is based on cdist's __file/gencode-local and part of cdist.
#
@@ -26,5 +27,25 @@
# to conflict with dpkg's --force options). But currently we don't
# do any checks or --force'ing.
#
+state=$( cat "$__object/parameter/state" )
+package=$( basename "$__object_id" )
+state_is="$(cat "$__object/explorer/pkg_state")"
+state_should=""
-echo "dpkg -i /var/cache/apt/archives/$__object_id"
+[ "$state" = "absent" ] || state_should="$package"
+[ "$state_is" = "$state_should" ] && exit 0
+
+case "$state" in
+ present)
+ echo "dpkg --install /var/cache/apt/archives/$__object_id"
+ echo "installed" >> "$__messages_out"
+ ;;
+ absent)
+ [ -f "$__object/parameter/purge-if-absent" ] \
+ && action="--purge" \
+ || action="--remove"
+ echo "dpkg $action ${__object_id%%_*}"
+ echo "removed ($action)" >> "$__messages_out"
+ ;;
+ *) echo "ERROR: unknown state '$state'" >&2 ;;
+esac
diff --git a/cdist/conf/type/__package_dpkg/man.rst b/cdist/conf/type/__package_dpkg/man.rst
index df2d86a7..828d8cdd 100644
--- a/cdist/conf/type/__package_dpkg/man.rst
+++ b/cdist/conf/type/__package_dpkg/man.rst
@@ -12,30 +12,77 @@ This type is used on Debian and variants (like Ubuntu) to
install packages that are provided locally as \*.deb files.
The object given to this type must be the name of the deb package.
+The filename of the deb package has to follow Debian naming conventions, i.e.
+`${binary:Package}_${Version}_${Architecture}.deb` (see `dpkg-query(1)` for
+details).
+OPTIONAL PARAMETERS
+-------------------
+state
+ `present` or `absent`, defaults to `present`.
+
REQUIRED PARAMETERS
-------------------
source
path to the \*.deb package
+
+BOOLEAN PARAMETERS
+------------------
+purge-if-absent
+ If this parameter is given when state is `absent`, the package is
+ purged from the system (using `--purge`).
+
+
+EXPLORER
+--------
+pkg_state
+ Returns the full package name if package is installed, empty otherwise.
+
+
+MESSAGES
+--------
+installed
+ The deb-file was installed.
+
+removed (--remove)
+ The package was removed, keeping config.
+
+removed (--purge)
+ The package was removed including config (purged).
+
+
EXAMPLES
--------
.. code-block:: sh
# Install foo and bar packages
- __package_dpkg --source /tmp/foo_0.1_all.deb foo_0.1_all.deb
- __package_dpkg --source $__type/files/bar_1.4.deb bar_1.4.deb
+ __package_dpkg foo_0.1_all.deb --source /tmp/foo_0.1_all.deb
+ __package_dpkg bar_1.4.deb --source $__type/files/bar_1.4.deb
+
+ # uninstall baz:
+ __package_dpkg baz_1.4_amd64.deb \
+ --source $__type/files/baz_1.4_amd64.deb \
+ --state "absent"
+ # uninstall baz and also purge config-files:
+ __package_dpkg baz_1.4_amd64.deb \
+ --source $__type/files/baz_1.4_amd64.deb \
+ --purge-if-absent \
+ --state "absent"
SEE ALSO
--------
-:strong:`cdist-type__package`\ (7)
+:strong:`cdist-type__package`\ (7), :strong:`dpkg-query`\ (1)
+
AUTHORS
-------
-Tomas Pospisek
+| Tomas Pospisek
+| Thomas Eckert
+
COPYING
-------
diff --git a/cdist/conf/type/__package_dpkg/manifest b/cdist/conf/type/__package_dpkg/manifest
index 9f0c1a97..6d228d8e 100755
--- a/cdist/conf/type/__package_dpkg/manifest
+++ b/cdist/conf/type/__package_dpkg/manifest
@@ -25,10 +25,16 @@
# do any checks or --force'ing.
+state=$( cat "$__object/parameter/state" )
package_path=$( cat "$__object/parameter/source" )
package=$( basename "$__object_id" )
+state_is="$(cat "$__object/explorer/pkg_state")"
+state_should=""
+
+[ "$state" = "absent" ] || state_should="$package"
+[ "$state_is" = "$state_should" ] && exit 0
__file "/var/cache/apt/archives/$package" \
- --source "$package_path" \
- --state present
+ --source "$package_path" \
+ --state "$state"
diff --git a/cdist/conf/type/__package_dpkg/parameter/boolean b/cdist/conf/type/__package_dpkg/parameter/boolean
new file mode 100644
index 00000000..f9a0f6b0
--- /dev/null
+++ b/cdist/conf/type/__package_dpkg/parameter/boolean
@@ -0,0 +1 @@
+purge-if-absent
diff --git a/cdist/conf/type/__package_dpkg/parameter/default/state b/cdist/conf/type/__package_dpkg/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__package_dpkg/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__package_dpkg/parameter/optional b/cdist/conf/type/__package_dpkg/parameter/optional
new file mode 100644
index 00000000..ff72b5c7
--- /dev/null
+++ b/cdist/conf/type/__package_dpkg/parameter/optional
@@ -0,0 +1 @@
+state
diff --git a/cdist/conf/type/__package_emerge/explorer/pkg_version b/cdist/conf/type/__package_emerge/explorer/pkg_version
index 7053eaff..d02b9d6b 100644
--- a/cdist/conf/type/__package_emerge/explorer/pkg_version
+++ b/cdist/conf/type/__package_emerge/explorer/pkg_version
@@ -32,4 +32,5 @@ else
name="$__object_id"
fi
+# shellcheck disable=SC2016
equery -q l -F '$cp $fullversion' "$name" || true
diff --git a/cdist/conf/type/__package_emerge/gencode-remote b/cdist/conf/type/__package_emerge/gencode-remote
index 6abe2d61..e1b85ebb 100755
--- a/cdist/conf/type/__package_emerge/gencode-remote
+++ b/cdist/conf/type/__package_emerge/gencode-remote
@@ -38,13 +38,13 @@ fi
pkg_version="$(cat "$__object/explorer/pkg_version")"
if [ -z "$pkg_version" ]; then
state_is="absent"
-elif [ -z "$version" -a $(echo "$pkg_version" | wc -l) -gt 1 ]; then
- echo "Package name is not unique! The following packages are installed:"
- echo "$pkg_version"
+elif [ -z "$version" ] && [ "$(echo "$pkg_version" | wc -l)" -gt 1 ]; then
+ echo "Package name is not unique! The following packages are installed:" >&2
+ echo "$pkg_version" >&2
exit 1
-elif [ -n "$version" -a $(echo "$pkg_version" | cut -d " " -f 1 | sort | uniq | wc -l) -gt 1 ]; then
- echo "Package name is not unique! The following packages are installed:"
- echo "$pkg_version"
+elif [ -n "$version" ] && [ "$(echo "$pkg_version" | cut -d " " -f 1 | sort | uniq | wc -l)" -gt 1 ]; then
+ echo "Package name is not unique! The following packages are installed:" >&2
+ echo "$pkg_version" >&2
exit 1
else
state_is="present"
@@ -57,16 +57,18 @@ fi
# Exit if nothing is needed to be done
-[ "$state_is" = "$state_should" ] && ( [ -z "$version" ] || [ "$installed_version" = "$version" ] ) && exit 0
-[ "$state_should" = "absent" ] && [ ! -z "$version" ] && [ "$installed_version" != "$version" ] && exit 0
+[ "$state_is" = "$state_should" ] && { [ -z "$version" ] || [ "$installed_version" = "$version" ]; } && exit 0
+[ "$state_should" = "absent" ] && [ -n "$version" ] && [ "$installed_version" != "$version" ] && exit 0
case "$state_should" in
present)
- echo "emerge \"$name\" &>/dev/null || exit 1"
+ echo "emerge '$name' &>/dev/null || exit 1"
+ echo "installed" >> "$__messages_out"
;;
absent)
- echo "emerge -C \"$name\" &>/dev/null || exit 1"
+ echo "emerge -C '$name' &>/dev/null || exit 1"
+ echo "removed" >> "$__messages_out"
;;
*)
echo "Unknown state: $state_should" >&2
diff --git a/cdist/conf/type/__package_emerge_dependencies/gencode-remote b/cdist/conf/type/__package_emerge_dependencies/gencode-remote
index face898a..f3e6f76e 100755
--- a/cdist/conf/type/__package_emerge_dependencies/gencode-remote
+++ b/cdist/conf/type/__package_emerge_dependencies/gencode-remote
@@ -6,10 +6,11 @@ flaggie_installed="$(cat "$__object/explorer/flaggie_installed")"
if [ "${gentoolkit_installed}" != "true" ]; then
# emerge app-portage/gentoolkit
echo "emerge app-portage/gentoolkit &> /dev/null || exit 1"
+ echo "installed app-portage/gentoolkit" >> "$__messages_out"
fi
if [ "${flaggie_installed}" != "true" ]; then
# emerge app-portage/flaggie
echo "emerge app-portage/flaggie &> /dev/null || exit 1"
+ echo "installed app-portage/flaggie" >> "$__messages_out"
fi
-
diff --git a/cdist/conf/type/__package_luarocks/explorer/pkg_status b/cdist/conf/type/__package_luarocks/explorer/pkg_status
index 3eb73298..e83e8ce6 100755
--- a/cdist/conf/type/__package_luarocks/explorer/pkg_status
+++ b/cdist/conf/type/__package_luarocks/explorer/pkg_status
@@ -28,4 +28,4 @@ else
fi
# Accept luarocks failing if package is not known/installed
-luarocks list "$name" | egrep -A1 "^$name$" || exit 0
+luarocks list "$name" | grep -E -A1 "^$name$" || exit 0
diff --git a/cdist/conf/type/__package_luarocks/gencode-remote b/cdist/conf/type/__package_luarocks/gencode-remote
index cae06b22..d83b3c3a 100755
--- a/cdist/conf/type/__package_luarocks/gencode-remote
+++ b/cdist/conf/type/__package_luarocks/gencode-remote
@@ -42,10 +42,12 @@ fi
case "$state_should" in
present)
- echo luarocks install \"$name\"
+ echo "luarocks install '$name'"
+ echo "installed" >> "$__messages_out"
;;
absent)
- echo luarocks remove \"$name\"
+ echo "luarocks remove '$name'"
+ echo "removed" >> "$__messages_out"
;;
*)
echo "Unknown state: $state_should" >&2
diff --git a/cdist/conf/type/__package_opkg/explorer/pkg_status b/cdist/conf/type/__package_opkg/explorer/pkg_status
index 5da4f742..de7b896b 100755
--- a/cdist/conf/type/__package_opkg/explorer/pkg_status
+++ b/cdist/conf/type/__package_opkg/explorer/pkg_status
@@ -1,7 +1,8 @@
-#!/bin/sh
+#!/bin/sh -e
#
# 2011 Nico Schottelius (nico-cdist at schottelius.org)
# 2012 Giel van Schijndel (giel plus cdist at mortis dot eu)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -19,21 +20,78 @@
# along with cdist. If not, see .
#
#
-# Retrieve the status of a package - parsed opkg output
+# Retrieve the status of a package - parses opkg output
#
-if [ -f "$__object/parameter/name" ]; then
- name="$(cat "$__object/parameter/name")"
+readonly __type_path=${__object%%${__object_id}*}
+test -d "${__type_path}" || { echo 'Cannot determine __type_path' >&2; exit 1; }
+readonly LOCKFILE="${__type_path:?}/.cdist_opkg.lock"
+
+if command -v flock >/dev/null 2>&1
+then
+ # use flock (if available) on FD 9
+ _lock() {
+ exec 9<>"${LOCKFILE:?}"
+ flock -x 9
+ echo $$>&9
+ }
+ _unlock() {
+ :>"${LOCKFILE:?}"
+ flock -u 9
+ exec 9<&-
+ }
else
- name="$__object_id"
+ # fallback to mkdir if flock is missing
+ _lock() {
+ until mkdir "${LOCKFILE:?}.dir" 2>/dev/null
+ do
+ while test -d "${LOCKFILE}.dir"
+ do
+ # DEBUG:
+ # printf 'Locked by PID: %u\n' "$(cat "${LOCKFILE}.dir/pid")"
+ sleep 1
+ done
+ done
+ echo $$ >"${LOCKFILE:?}.dir/pid"
+ }
+ _unlock() {
+ test -d "${LOCKFILE}.dir" || return 0
+ if test -s "${LOCKFILE}.dir/pid"
+ then
+ test "$(cat "${LOCKFILE}.dir/pid")" = $$ || return 1
+ rm "${LOCKFILE:?}.dir/pid"
+ fi
+ rmdir "${LOCKFILE:?}.dir"
+ }
fi
-# Except dpkg failing, if package is not known / installed
-if opkg status "$name" 2>/dev/null | grep -q "^Status: install user installed$"; then
- echo "present"
- exit 0
-elif [ "$(opkg info "$name" 2> /dev/null | wc -l)" -eq 0 ]; then
- echo "absent notpresent"
- exit 0
+
+if test -f "${__object}/parameter/name"
+then
+ pkg_name=$(cat "${__object}/parameter/name")
+else
+ pkg_name=$__object_id
+fi
+
+
+# NOTE: We need to lock parallel execution of type explorers and code-remote
+# because opkg will try to acquire the OPKG lock (usually /var/lock/opkg.lock)
+# using lockf(2) for every operation.
+# It will not wait for the lock but terminate with an error.
+# This leads to incorrect 'absent notpresent' statuses when parallel execution
+# is enabled.
+trap _unlock EXIT
+_lock
+
+
+# Except opkg failing, if package is not known / installed
+if opkg status "${pkg_name}" 2>/dev/null \
+ | grep -q -e '^Status: [^ ][^ ]* [^ ][^ ]* installed$'
+then
+ echo 'present'
+elif opkg info "${pkg_name}" 2>/dev/null | grep -q .
+then
+ echo 'absent notpresent'
+else
+ echo 'absent'
fi
-echo "absent"
diff --git a/cdist/conf/type/__package_opkg/gencode-remote b/cdist/conf/type/__package_opkg/gencode-remote
index 09fe69a4..28caff71 100755
--- a/cdist/conf/type/__package_opkg/gencode-remote
+++ b/cdist/conf/type/__package_opkg/gencode-remote
@@ -2,6 +2,7 @@
#
# 2011,2013 Nico Schottelius (nico-cdist at schottelius.org)
# 2012 Giel van Schijndel (giel plus cdist at mortis dot eu)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -19,39 +20,50 @@
# along with cdist. If not, see .
#
#
-# Manage packages on OpenWRT and co.
+# Manage packages on OpenWrt, optware, and co.
#
-if [ -f "$__object/parameter/name" ]; then
- name="$(cat "$__object/parameter/name")"
+if test -f "${__object}/parameter/name"
+then
+ name=$(cat "${__object}/parameter/name")
else
- name="$__object_id"
+ name=$__object_id
fi
-state_should="$(cat "$__object/parameter/state")"
+state_should=$(cat "${__object}/parameter/state")
+state_is=$(cat "${__object}/explorer/pkg_status")
-state_is="$(cat "$__object/explorer/pkg_status")"
-case "$state_is" in
- absent*)
- present="$(echo "$state_is" | cut -d ' ' -f 2)"
- state_is="absent"
- ;;
+case $state_is
+in
+ (absent*)
+ presence=$(echo "${state_is}" | cut -d ' ' -f 2)
+ state_is='absent'
+ ;;
esac
-[ "$state_is" = "$state_should" ] && exit 0
+if test "${state_is}" = "${state_should}"
+then
+ exit 0
+fi
-case "$state_should" in
- present)
- if [ "$present" = "notpresent" ]; then
- echo opkg --verbosity=0 update
- fi
- echo opkg --verbosity=0 install \"$name\"
- ;;
- absent)
- echo opkg --verbosity=0 remove \"$name\"
- ;;
- *)
- echo "Unknown state: $state" >&2
- exit 1
- ;;
+
+case $state_should
+in
+ (present)
+ if test "${presence}" = 'notpresent'
+ then
+ echo 'opkg --verbosity=0 update'
+ fi
+
+ printf "opkg --verbosity=0 install '%s'\n" "${name}"
+ echo 'installed' >>"${__messages_out}"
+ ;;
+ (absent)
+ printf "opkg --verbosity=0 remove '%s'" "${name}"
+ echo 'removed' >>"${__messages_out}"
+ ;;
+ (*)
+ printf 'Unknown state: %s\n' "${state_should}" >&2
+ exit 1
+ ;;
esac
diff --git a/cdist/conf/type/__package_pacman/gencode-remote b/cdist/conf/type/__package_pacman/gencode-remote
index 69a5d62a..2e076ec3 100755
--- a/cdist/conf/type/__package_pacman/gencode-remote
+++ b/cdist/conf/type/__package_pacman/gencode-remote
@@ -45,10 +45,12 @@ fi
case "$state_should" in
present)
- echo pacman --needed --noconfirm --noprogressbar -S \"$name\"
+ echo "pacman --needed --noconfirm --noprogressbar -S '$name'"
+ echo "installed" >> "$__messages_out"
;;
absent)
- echo pacman --noconfirm --noprogressbar -R \"$name\"
+ echo "pacman --noconfirm --noprogressbar -R '$name'"
+ echo "removed" >> "$__messages_out"
;;
*)
echo "Unknown state: $state_should" >&2
diff --git a/cdist/conf/type/__package_pip/explorer/distinfo-dir b/cdist/conf/type/__package_pip/explorer/distinfo-dir
new file mode 100755
index 00000000..18e169ae
--- /dev/null
+++ b/cdist/conf/type/__package_pip/explorer/distinfo-dir
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+# 2021 Matthias Stecher (matthiasstecher at gmx.de)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+
+nameparam="$__object/parameter/name"
+if [ -f "$nameparam" ]; then
+ name=$(cat "$nameparam")
+else
+ name="$__object_id"
+fi
+
+pipparam="$__object/parameter/pip"
+if [ -f "$pipparam" ]; then
+ pip=$(cat "$pipparam")
+else
+ pip="$( "$__type_explorer/pip" )"
+fi
+
+
+if command -v "$pip" >/dev/null 2>&1; then
+ # assemble the path where pip stores all pip package info
+ "$pip" show "$name" \
+ | awk -F': ' '
+ $1 == "Name" {name=$2; gsub(/-/,"_",name); next}
+ $1 == "Version" {version=$2; next}
+ $1 == "Location" {location=$2; next}
+ END {if (version != "") printf "%s/%s-%s.dist-info", location, name, version}'
+fi
diff --git a/cdist/conf/type/__package_pip/explorer/extras b/cdist/conf/type/__package_pip/explorer/extras
new file mode 100755
index 00000000..bbdc17ab
--- /dev/null
+++ b/cdist/conf/type/__package_pip/explorer/extras
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# 2021 Matthias Stecher (matthiasstecher at gmx.de)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+#
+# Checks if the given extras are really installed or not. It will be
+# done by querring all dependencies for that extra and return it as
+# "to be installed" if no dependency was found.
+#
+
+
+distinfo_dir="$("$__type_explorer/distinfo-dir")"
+
+# check if we have something to check
+if [ "$distinfo_dir" ] && [ -s "$__object/parameter/extra" ]
+then
+ # save cause freezing is slow
+ mkdir "$__object/files"
+ pip_freeze="$__object/files/pip-freeze.tmp"
+ pip3 freeze > "$pip_freeze"
+
+ # If all is set, it searches all available extras to separatly check them.
+ # It would work with just 'all' (cause dependencies are specified for
+ # 'all'), but will not update if one extra is already present. Side effect
+ # is that it will not use [all] but instead name all extras seperatly.
+ for extra in $(if grep -qFx all "$__object/parameter/extra";
+ then awk -F': ' '$1 == "Provides-Extra" && $2 != "all"{print $2}' "$distinfo_dir/METADATA";
+ else tr ',' '\n' < "$__object/parameter/extra";
+ fi)
+ do
+ # create a grep BRE pattern to search all packages
+ # maybe a file full of patterns for -F could be written
+ grep_pattern="$(
+ awk -F'(: | ; )' -v check="$extra" '
+ $1 == "Requires-Dist" {
+ split($2, r, " ");
+ sub("extra == ", "", $3); gsub("'"'"'", "", $3);
+ if($3 == check) print r[1]
+ }' "$distinfo_dir/METADATA" \
+ | sed ':a; $!N; s/\n/\\|/; ta'
+ )"
+
+ # echo the extra if no packages where found for it
+ # if there is no pattern, we don't need to search ;-)
+ # pip matches packages case-insensetive, we need to do that, too
+ if [ "$grep_pattern" ] && ! grep -qi "$grep_pattern" "$pip_freeze"
+ then
+ echo "$extra"
+ fi
+ done
+fi
diff --git a/cdist/conf/type/__package_pip/explorer/pip b/cdist/conf/type/__package_pip/explorer/pip
new file mode 100755
index 00000000..cf9fae89
--- /dev/null
+++ b/cdist/conf/type/__package_pip/explorer/pip
@@ -0,0 +1,10 @@
+#!/bin/sh -e
+
+for bin in pip3 pip
+do
+ if check="$( command -v "$bin" )"
+ then
+ echo "$check"
+ break
+ fi
+done
diff --git a/cdist/conf/type/__package_pip/explorer/state b/cdist/conf/type/__package_pip/explorer/state
old mode 100644
new mode 100755
index 5be07280..3cc98ab9
--- a/cdist/conf/type/__package_pip/explorer/state
+++ b/cdist/conf/type/__package_pip/explorer/state
@@ -32,7 +32,7 @@ pipparam="$__object/parameter/pip"
if [ -f "$pipparam" ]; then
pip=$(cat "$pipparam")
else
- pip="pip"
+ pip="$( "$__type_explorer/pip" )"
fi
# If there is no pip, it may get created from somebody else.
diff --git a/cdist/conf/type/__package_pip/gencode-remote b/cdist/conf/type/__package_pip/gencode-remote
index 933406f2..9abe28bf 100755
--- a/cdist/conf/type/__package_pip/gencode-remote
+++ b/cdist/conf/type/__package_pip/gencode-remote
@@ -2,6 +2,7 @@
#
# 2012 Nico Schottelius (nico-cdist at schottelius.org)
# 2016 Darko Poljak (darko.poljak at gmail.com)
+# 2021 Matthias Stecher (matthiasstecher at gmx.de)
#
# This file is part of cdist.
#
@@ -25,7 +26,10 @@
state_is=$(cat "$__object/explorer/state")
state_should="$(cat "$__object/parameter/state")"
-[ "$state_is" = "$state_should" ] && exit 0
+# short circuit if state is the same and no extras to install
+[ "$state_is" = "$state_should" ] && ! [ -s "$__object/explorer/extras" ] \
+ && exit 0
+
nameparam="$__object/parameter/name"
if [ -f "$nameparam" ]; then
@@ -38,7 +42,12 @@ pipparam="$__object/parameter/pip"
if [ -f "$pipparam" ]; then
pip=$(cat "$pipparam")
else
- pip="pip"
+ pip="$( cat "$__object/explorer/pip" )"
+ if [ -z "$pip" ]
+ then
+ echo 'pip not found in path' >&2
+ exit 1
+ fi
fi
runasparam="$__object/parameter/runas"
@@ -51,20 +60,30 @@ fi
case "$state_should" in
present)
+ if [ -s "$__object/explorer/extras" ]
+ then
+ # all extras are passed to pip in a comma-separated list in the name
+ # sed loops through all input lines and add commas between them
+ extras="$(sed ':a; $!N; s/\n/,/; ta' "$__object/explorer/extras")"
+ name="${name}[${extras}]"
+ fi
+
if [ "$runas" ]
then
- echo "su -c \"$pip install -q $name\" $runas"
+ echo "su -c '$pip install -q $name' $runas"
else
- echo $pip install -q "$name"
+ echo "$pip" install -q "$name"
fi
+ echo "installed" >> "$__messages_out"
;;
absent)
if [ "$runas" ]
then
- echo "su -c \"$pip uninstall -q -y $name\" $runas"
+ echo "su -c '$pip uninstall -q -y $name' $runas"
else
- echo $pip uninstall -q -y "$name"
+ echo "$pip" uninstall -q -y "$name"
fi
+ echo "removed" >> "$__messages_out"
;;
*)
echo "Unknown state: $state_should" >&2
diff --git a/cdist/conf/type/__package_pip/man.rst b/cdist/conf/type/__package_pip/man.rst
index 234ceee2..5a2bc673 100644
--- a/cdist/conf/type/__package_pip/man.rst
+++ b/cdist/conf/type/__package_pip/man.rst
@@ -22,6 +22,16 @@ OPTIONAL PARAMETERS
name
If supplied, use the name and not the object id as the package name.
+extra
+ Extra optional dependencies which should be installed along the selected
+ package. Can be specified multiple times. Multiple extras can be passed
+ in one `--extra` as a comma-separated list.
+
+ Extra optional dependencies will be installed even when the base package
+ is already installed. Notice that the type will not remove installed extras
+ that are not explicitly named for the type because pip does not offer a
+ management for orphaned packages and they may be used by other packages.
+
pip
Instead of using pip from PATH, use the specific pip path.
@@ -46,6 +56,14 @@ EXAMPLES
# Use pip in a virtualenv located at /foo/shinken_virtualenv as user foo
__package_pip pyro --state present --pip /foo/shinken_virtualenv/bin/pip --runas foo
+ # Install package with optional dependencies
+ __package_pip mautrix-telegram --extra speedups --extra webp_convert --extra hq_thumbnails
+ # the extras can also be specified comma-separated
+ __package_pip mautrix-telegram --extra speedups,webp_convert,hq_thumbnails --extra postgres
+
+ # or take all extras
+ __package_pip mautrix-telegram --extra all
+
SEE ALSO
--------
@@ -54,12 +72,13 @@ SEE ALSO
AUTHORS
-------
-Nico Schottelius
+| Nico Schottelius
+| Matthias Stecher
COPYING
-------
-Copyright \(C) 2012 Nico Schottelius. You can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
+Copyright \(C) 2012 Nico Schottelius, 2021 Matthias Stecher. You can
+redistribute it and/or modify it under the terms of the GNU General
+Public License as published by the Free Software Foundation, either
+version 3 of the License, or (at your option) any later version.
diff --git a/cdist/conf/type/__package_pip/parameter/optional_multiple b/cdist/conf/type/__package_pip/parameter/optional_multiple
new file mode 100644
index 00000000..0f228715
--- /dev/null
+++ b/cdist/conf/type/__package_pip/parameter/optional_multiple
@@ -0,0 +1 @@
+extra
diff --git a/cdist/conf/type/__package_pkg_freebsd/explorer/pkg_version b/cdist/conf/type/__package_pkg_freebsd/explorer/pkg_version
index 1335ba79..0a1ab75c 100755
--- a/cdist/conf/type/__package_pkg_freebsd/explorer/pkg_version
+++ b/cdist/conf/type/__package_pkg_freebsd/explorer/pkg_version
@@ -30,7 +30,7 @@ fi
# Don't produce "no pkgs installed" output -- breaks things
PKG_OUTPUT=$(pkg_info 2>&1)
if [ ! "$PKG_OUTPUT" = "pkg_info: no packages installed" ]; then
- echo -n "$(echo "$PKG_OUTPUT" \
+ printf "%s" "$(echo "$PKG_OUTPUT" \
| awk '{print $1}' \
| sed 's/^\(.*\)-\([^-]*\)$/name:\1 ver:\2/g' \
| grep "name:$name ver:" \
diff --git a/cdist/conf/type/__package_pkg_freebsd/gencode-remote b/cdist/conf/type/__package_pkg_freebsd/gencode-remote
index b51c3153..3f88f6bc 100755
--- a/cdist/conf/type/__package_pkg_freebsd/gencode-remote
+++ b/cdist/conf/type/__package_pkg_freebsd/gencode-remote
@@ -33,12 +33,13 @@ assert () # If condition false,
lineno=$2
- if [ ! $1 ]
+ if [ ! "$1" ]
then
echo "Assertion failed: \"$1\""
+ # shellcheck disable=SC2039
echo "File \"$0\", line $lineno, called by $(caller 0)"
exit $E_ASSERT_FAILED
- fi
+ fi
}
# Debug
@@ -66,7 +67,7 @@ cmd=""
# FIXME: This is ugly.
execcmd(){
# Set the PACKAGESITE if we're ADDing a new package
- if [ "$1" = "add" -a -n "$pkgsite" ]; then
+ if [ "$1" = "add" ] && [ -n "$pkgsite" ]; then
# Use http.../All/ if we know the exact version we want, use .../Latest/ otherwise
pkgsite="export PACKAGESITE=${pkgsite}"
[ -n "$version" ] && pkgsite="${pkgsite}/All/" || pkgsite="${pkgsite}/Latest/"
@@ -88,6 +89,7 @@ if [ -n "$curr_version" ]; then # PKG *is* installed
cmd="${rm_cmd} ${name}-${curr_version}"
fi
execcmd "remove" "${cmd}"
+ echo "removed" >> "$__messages_out"
exit 0
else # Should be installed
if [ -n "$version" ]; then # Want a specific version
@@ -95,11 +97,13 @@ if [ -n "$curr_version" ]; then # PKG *is* installed
exit 0
else # Current version is wrong, fix
#updatepkg "$name" "$version"
+ # shellcheck disable=SC2039
assert "! ${version} = ${curr_version}" $LINENO
cmd="${rm_cmd} ${name}-${curr_version}"
execcmd "remove" "${cmd}"
cmd="${add_cmd} -r ${name}-${version}"
execcmd "add" "${cmd}"
+ echo "installed" >> "$__messages_out"
fi
else # Don't care what version to use
exit 0
@@ -118,6 +122,7 @@ else # PKG *isn't* installed
cmd="${cmd}-${version}"
fi
execcmd "add" "${cmd}"
+ echo "installed" >> "$__messages_out"
exit 0
fi
fi
diff --git a/cdist/conf/type/__package_pkg_openbsd/explorer/pkg_state b/cdist/conf/type/__package_pkg_openbsd/explorer/pkg_state
new file mode 100755
index 00000000..9cd17787
--- /dev/null
+++ b/cdist/conf/type/__package_pkg_openbsd/explorer/pkg_state
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# Copyright 2018, Takashi Yoshi
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+#
+# Retrieve the status of a package - parsed pkg_info output
+#
+
+if [ -f "${__object}/parameter/name" ]
+then
+ pkgid="$(cat "${__object}/parameter/name")"
+else
+ pkgid="${__object_id}"
+fi
+
+if [ -f "${__object}/parameter/version" ]
+then
+ pkgid="${pkgid}-$(cat "${__object}/parameter/version")"
+fi
+
+if [ -f "${__object}/parameter/flavor" ]
+then
+ # If a flavor but no version is given we need to add another -,
+ # otherwise pkg_info confuses the flavor with the version.
+ [ -f "${__object}/parameter/version" ] || pkgid="${pkgid}-"
+
+ pkgid="${pkgid}-$(cat "${__object}/parameter/flavor")"
+fi
+
+
+pkg_info -q -I "inst:${pkgid}" >/dev/null 2>&1 \
+ && echo 'present' || echo 'absent'
+
+exit 0
diff --git a/cdist/conf/type/__package_pkg_openbsd/gencode-remote b/cdist/conf/type/__package_pkg_openbsd/gencode-remote
index 4a6763cd..5a21ce12 100755
--- a/cdist/conf/type/__package_pkg_openbsd/gencode-remote
+++ b/cdist/conf/type/__package_pkg_openbsd/gencode-remote
@@ -2,6 +2,7 @@
#
# 2011 Andi Brönnimann (andi-cdist at v-net.ch)
# 2012 Nico Schottelius (nico-cdist at schottelius.org)
+# 2018 Takashi Yoshi
#
# This file is part of cdist.
#
@@ -22,105 +23,96 @@
# Manage packages with pkg on OpenBSD
#
-# Debug
-# exec >&2
-# set -x
+os_version=$(cat "${__global}/explorer/os_version")
+machine=$(cat "${__global}/explorer/machine")
-os_version="$(cat "$__global/explorer/os_version")"
-machine="$(cat "$__global/explorer/machine")"
-
-if [ -f "$__object/parameter/version" ]; then
- version="$(cat "$__object/parameter/version")"
+if [ -f "${__object}/parameter/version" ]; then
+ version=$(cat "${__object}/parameter/version")
fi
-if [ -f "$__object/parameter/flavor" ]; then
- flavor="$(cat "$__object/parameter/flavor")"
+if [ -f "${__object}/parameter/flavor" ]; then
+ flavor=$(cat "${__object}/parameter/flavor")
fi
-# do not show progress bar
-pkgopts="-x"
+# Do not show progress bar
+pkgopts='-x'
-if [ -f "$__object/parameter/name" ]; then
- name=$(cat "$__object/parameter/name")
+name="${__object_id}"
+if [ -f "${__object}/parameter/name" ]; then
+ name=$(cat "${__object}/parameter/name")
+fi
+
+if [ -n "${version}" ] && [ -n "${flavor}" ]; then
+ pkgid="${name}-${version}-${flavor}"
+elif [ -n "${version}" ]; then
+ pkgid="${name}-${version}"
+elif [ -f "${__object}/parameter/flavor" ]; then
+ pkgid="${name}--${flavor}"
else
- name="$__object_id"
+ pkgid="${name}"
fi
-if [ -n "$version" -a -n "$flavor" ]; then
- pkgid="$name-$version-$flavor"
-elif [ -n "$version" ]; then
- pkgid="$name-$version"
-elif [ -n "$flavor" ]; then
- pkgid="$name--$flavor"
-elif [ -f "$__object/parameter/flavor" ]; then
- pkgid="$name--"
+state_should=$(cat "${__object}/parameter/state")
+
+if [ -f "${__object}/parameter/pkg_path" ]; then
+ pkg_path=$(cat "${__object}/parameter/pkg_path")
else
- pkgid="$name"
+ has_installurl=$(cat "${__object}/explorer/has_installurl")
+ if [ 'yes' != "${has_installurl}" ]; then
+ # There is no default PKG_PATH, try to provide one
+ pkg_path="ftp://ftp.openbsd.org/pub/OpenBSD/${os_version}/packages/${machine}/"
+ fi
fi
-state_should="$(cat "$__object/parameter/state")"
+state_is=$(cat "${__object}/explorer/pkg_state")
+[ "${state_is}" = "${state_should}" ] && exit 0
-pkg_version="$(cat "$__object/explorer/pkg_version")"
+case "${state_should}" in
+ present)
+ if [ -n "${pkg_path}" ]; then
+ echo "export PKG_PATH='${pkg_path}'"
+ fi
-if [ -f "$__object/parameter/pkg_path" ]; then
- pkg_path="$(cat "$__object/parameter/pkg_path")"
-else
- has_installurl=$(cat "${__object}/explorer/has_installurl")
- if [ Xyes != X"${has_installurl}" ]; then
- # there is no default PKG_PATH, try to provide one
- pkg_path="ftp://ftp.openbsd.org/pub/OpenBSD/$os_version/packages/$machine/"
- fi
-fi
+ # Use this because pkg_add doesn't properly handle errors
+ cat <&1 || true)
-if [ "$pkg_version" ]; then
- state_is="present"
-else
- state_is="absent"
+if ! pkg_info -q -I 'inst:${pkgid}' | grep -q '^${name}-${version}.*${flavor}$' 2>/dev/null
+then
+ # We didn't find the package in the list of 'installed packages', so it failed.
+ # This is necessary because pkg_add doesn't return properly
+
+ if [ -z "\${status}" ]; then
+ status='Failed to add package, uncaught exception.'
+ fi
+ echo "Error: \${status}" >&2
+ exit 1
fi
+EOF
+ echo 'installed' >> "${__messages_out}"
+ ;;
-[ "$state_is" = "$state_should" ] && exit 0
+ absent)
+ # Use this because pkg_delete doesn't properly handle errors
+ cat <&1 || true)
-case "$state_should" in
- present)
- # use this because pkg_add doesn't properly handle errors
- cat << eof
-if [ X != X"${pkg_path}" ]; then
- PKG_PATH="${pkg_path}"; export PKG_PATH
+if pkg_info -q -I 'inst:${pkgid}' | grep -q '^${name}-${version}.*${flavor}' 2>/dev/null
+then
+ # We found the package in the list of 'installed packages'.
+ # This would indicate that pkg_delete failed, send the output of pkg_delete
+
+ if [ -z "\${status}" ]; then
+ status='Failed to remove package, uncaught exception.'
+ fi
+ echo "Error: \${status}" >&2
+ exit 1
fi
-status=\$(pkg_add "$pkgopts" "$pkgid" 2>&1)
-pkg_info | grep "^${name}.*${version}.*${flavor}" > /dev/null 2>&1
-
-# We didn't find the package in the list of 'installed packages', so it failed
-# This is necessary because pkg_add doesn't return properly
-if [ \$? -ne 0 ]; then
- if [ -z "\${status}" ]; then
- status="Failed to add package, uncaught exception."
- fi
- echo "Error: \$status"
- exit 1
-fi
-eof
- ;;
-
- absent)
- # use this because pkg_add doesn't properly handle errors
- cat << eof
-status=\$(pkg_delete "$pkgopts" "$pkgid")
-pkg_info | grep "^${name}.*${version}.*${flavor}" > /dev/null 2>&1
-
-# We found the package in the list of 'installed packages'
-# This would indicate that pkg_delete failed, send the output of pkg_delete
-if [ \$? -eq 0 ]; then
- if [ -z "\${status}" ]; then
- status="Failed to remove package, uncaught exception."
- fi
- echo "Error: \$status"
- exit 1
-fi
-eof
- ;;
- *)
- echo "Unknown state: $state_should" >&2
+EOF
+ echo 'removed' >> "${__messages_out}"
+ ;;
+ *)
+ echo "Unknown state: ${state_should}" >&2
exit 1
- ;;
+ ;;
esac
diff --git a/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_bootstrapped b/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_bootstrapped
new file mode 100755
index 00000000..429f15d3
--- /dev/null
+++ b/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_bootstrapped
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+if pkg -N >/dev/null 2>&1; then
+ echo "YES"
+fi
diff --git a/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version b/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version
index 947857b9..1c6ba5e5 100755
--- a/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version
+++ b/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version
@@ -18,9 +18,14 @@
# along with cdist. If not, see .
#
#
-# Retrieve the status of a package - parsed dpkg output
+# Retrieve the status of a package - parsed pkgng output
#
+if ! pkg -N >/dev/null 2>&1; then
+ # Nothing to do if pkg is not bootstrapped
+ exit
+fi
+
if [ -f "$__object/parameter/name" ]; then
name="$(cat "$__object/parameter/name")"
else
@@ -29,7 +34,7 @@ fi
# Don't produce "no pkgs installed" output -- breaks things
PKG_OUTPUT=$(pkg info 2>&1)
-echo -n "$(echo "$PKG_OUTPUT" \
+printf "%s" "$(echo "$PKG_OUTPUT" \
| awk '{print $1}' \
| sed 's/^\(.*\)-\([^-]*\)$/name:\1 ver:\2/g' \
| grep "name:$name ver:" \
diff --git a/cdist/conf/type/__package_pkgng_freebsd/gencode-remote b/cdist/conf/type/__package_pkgng_freebsd/gencode-remote
index aa00de6a..05ba4cb2 100755
--- a/cdist/conf/type/__package_pkgng_freebsd/gencode-remote
+++ b/cdist/conf/type/__package_pkgng_freebsd/gencode-remote
@@ -43,6 +43,7 @@ fi
repo="$(cat "$__object/parameter/repo")"
state="$(cat "$__object/parameter/state")"
curr_version="$(cat "$__object/explorer/pkg_version")"
+pkg_bootstrapped="$(cat "$__object/explorer/pkg_bootstrapped")"
add_cmd="pkg install -y"
rm_cmd="pkg delete -y"
upg_cmd="pkg upgrade -y"
@@ -52,17 +53,20 @@ cmd=""
# Parms: $1 -- mode, "rm", "add", or "upg"
# $2 -- the command to be echoed
execcmd(){
- local _cmd=""
+ _cmd=""
case "$1" in
add)
_cmd="${add_cmd} $2"
+ echo "installed" >> "$__messages_out"
;;
rm)
_cmd="${rm_cmd} $2"
+ echo "removed" >> "$__messages_out"
;;
upg)
_cmd="${upg_cmd} $2"
+ echo "installed" >> "$__messages_out"
;;
*)
printf "Error. Don't understand command: %s" "$1" >&2
@@ -70,7 +74,11 @@ execcmd(){
;;
esac
- echo "$_cmd 2>&- >&-" # Silence the output of the command
+ if [ -z "${pkg_bootstrapped}" ]; then
+ echo "ASSUME_ALWAYS_YES=yes pkg bootstrap >/dev/null 2>&1"
+ fi
+
+ echo "$_cmd >/dev/null 2>&1" # Silence the output of the command
echo "status=\$?"
echo "if [ \"\$status\" -ne \"0\" ]; then"
echo " echo \"Error: ${_cmd} exited nonzero with \$status\"'!' >&2"
@@ -95,7 +103,7 @@ if [ -n "$curr_version" ]; then # PKG *is* installed
if [ "$upgrade" = "true" ]; then
execcmd "upg" "${cmd}"
else
- printf "Version %s is already installed and pkg-ng can't upgrade directly to version %s.\nTo upgrade to the latest version, use the --upgrade flag.\n" "$curr_version" "$version" >&2
+ printf 'Version %s is already installed and pkg-ng cannot upgrade directly to version %s.\nTo upgrade to the latest version, use the --upgrade flag.\n' "$curr_version" "$version" >&2
exit 1
fi
# PKG is supposed to be installed to the latest version
diff --git a/cdist/conf/type/__package_rubygem/gencode-remote b/cdist/conf/type/__package_rubygem/gencode-remote
index 6d793ac0..abb40653 100755
--- a/cdist/conf/type/__package_rubygem/gencode-remote
+++ b/cdist/conf/type/__package_rubygem/gencode-remote
@@ -39,10 +39,12 @@ fi
case "$state_should" in
present)
- echo gem install \"$name\" --no-ri --no-rdoc
+ echo "gem install '$name' --no-ri --no-rdoc"
+ echo "installed" >> "$__messages_out"
;;
absent)
- echo gem uninstall \"$name\"
+ echo "gem uninstall '$name'"
+ echo "removed" >> "$__messages_out"
;;
*)
echo "Unknown state: $state_should" >&2
diff --git a/cdist/conf/type/__package_update_index/explorer/currage b/cdist/conf/type/__package_update_index/explorer/currage
new file mode 100644
index 00000000..8eadaf53
--- /dev/null
+++ b/cdist/conf/type/__package_update_index/explorer/currage
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+# 2018 Thomas Eckert (tom at it-eckert.de)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+
+type="$("$__type_explorer/type")"
+
+case "$type" in
+ apt)
+ if [ -f "/var/cache/apt/pkgcache.bin" ]; then
+ echo $(($(date +"%s")-$(stat --format '%Y' /var/cache/apt/pkgcache.bin)))
+ else
+ echo -- -1
+ fi
+ ;;
+ pacman)
+ if [ -d "/var/lib/pacman/sync" ]; then
+ echo $(($(date +"%s")-$(stat --format '%Y' /var/lib/pacman/sync)))
+ else
+ echo -- -1
+ fi
+ ;;
+ alpine)
+ echo -- -1
+ ;;
+ *) echo "Your specified type ($type) is currently not supported." >&2
+ echo "Please contribute an implementation for it if you can." >&2
+ ;;
+esac
diff --git a/cdist/conf/type/__pf_ruleset/explorer/cksum b/cdist/conf/type/__package_update_index/explorer/type
old mode 100755
new mode 100644
similarity index 55%
rename from cdist/conf/type/__pf_ruleset/explorer/cksum
rename to cdist/conf/type/__package_update_index/explorer/type
index f8679836..c98e1e67
--- a/cdist/conf/type/__pf_ruleset/explorer/cksum
+++ b/cdist/conf/type/__package_update_index/explorer/type
@@ -1,6 +1,6 @@
#!/bin/sh
#
-# 2012 Jake Guffey (jake.guffey at eprotex.com)
+# 2018 Stu Zhao (z12y12l12 at gmail.com)
#
# This file is part of cdist.
#
@@ -16,26 +16,20 @@
#
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see .
-#
-#
-# Get the 256 bit SHA2 checksum of the pf ruleset on the target host.
-#
-# Debug
-#exec >&2
-#set -x
-
-# Check /etc/rc.conf for pf's configuration file name. Default to /etc/pf.conf
-# See if file exists and if so, get checksum
-
-RC="/etc/rc.conf"
-TMP="$(grep '^pf_rules=' ${RC} | cut -d= -f2 | sed 's/"//g')"
-PFCONF="${TMP:-"/etc/pf.conf"}"
-
-if [ -f "${PFCONF}" ]; then # The pf config file exists, find its cksum.
- cksum -o 1 ${PFCONF} | cut -d= -f2 | awk '{print $1}'
+if [ -f "$__object/parameter/type" ]; then
+ cat "$__object/parameter/type"
+else
+ # By default determine package manager based on operating system
+ os="$("$__explorer/os")"
+ case "$os" in
+ amazon|scientific|centos|fedora|redhat) echo "yum" ;;
+ debian|ubuntu|devuan) echo "apt" ;;
+ archlinux) echo "pacman" ;;
+ alpine) echo "apk" ;;
+ *)
+ echo "Don't know how to manage packages on: $os" >&2
+ exit 1
+ ;;
+ esac
fi
-
-# Debug
-#set +x
-
diff --git a/cdist/conf/type/__package_update_index/gencode-remote b/cdist/conf/type/__package_update_index/gencode-remote
index 20beed5b..803468b5 100755
--- a/cdist/conf/type/__package_update_index/gencode-remote
+++ b/cdist/conf/type/__package_update_index/gencode-remote
@@ -21,30 +21,39 @@
# Update the package index with the appropriate package manager
#
-type="$__object/parameter/type"
-
-if [ -f "$type" ]; then
- type="$(cat "$type")"
-else
- # By default determine package manager based on operating system
- os="$(cat "$__global/explorer/os")"
- case "$os" in
- amazon|scientific|centos|fedora|redhat) type="yum" ;;
- debian|ubuntu|devuan) type="apt" ;;
- archlinux) type="pacman" ;;
- *)
- echo "Don't know how to manage packages on: $os" >&2
- exit 1
- ;;
- esac
+type=$(cat "$__object/explorer/type")
+currage="$(cat "$__object/explorer/currage")"
+if [ -f "$__object/parameter/maxage" ]; then
+ maxage="$(cat "$__object/parameter/maxage")"
fi
+if [ -n "$maxage" ]; then
+ if [ "$type" != "apt" ] && [ "$type" != "pacman" ]; then
+ echo "ERROR: \"--maxage\" only supported for \"apt\" or \"pacman\" pkg-manager." >&2
+ exit 1
+ # do not exit if no value found (represented as -1)
+ elif [ "$currage" -ne -1 ] && [ "$currage" -lt "$maxage" ]; then
+ exit 0 # no need to update
+ fi
+fi
+
+
case "$type" in
yum) ;;
- apt) echo "apt-get --quiet update" ;;
- pacman) echo "pacman --noprogressbar --sync --refresh" ;;
+ apt)
+ echo "apt-get --quiet update"
+ echo "apt-cache updated (age was: $currage)" >> "$__messages_out"
+ ;;
+ pacman)
+ echo "pacman --noprogressbar --sync --refresh"
+ echo "pacman package database synced (age was: $currage)" >> "$__messages_out"
+ ;;
+ apk)
+ echo "apk update"
+ echo "apk package database updated." >>"$__messages_out"
+ ;;
*)
- echo "Don't know how to manage packages on: $os" >&2
+ echo "Don't know how to manage packages for type: $type" >&2
exit 1
;;
esac
diff --git a/cdist/conf/type/__package_update_index/man.rst b/cdist/conf/type/__package_update_index/man.rst
index 454aa05b..3cd787b9 100644
--- a/cdist/conf/type/__package_update_index/man.rst
+++ b/cdist/conf/type/__package_update_index/man.rst
@@ -27,6 +27,16 @@ type
* yum for Red Hat
* pacman for Arch Linux
+maxage
+ Available for package manager apt and pacman, max time in seconds since
+ last update. Repo update is skipped if maxage is not reached yet.
+
+MESSAGES
+--------
+apt-cache updated (age was: currage)
+ apt-cache was updated (run of `apt-get update`). `currage` is the time
+ in seconds since the previous run.
+
EXAMPLES
--------
@@ -39,10 +49,17 @@ EXAMPLES
# Force use of a specific package manager
__package_update_index --type apt
+ # Only update every hour:
+ __package_update_index --maxage 3600 --type apt
+
+ # same as above (on apt-type systems):
+ __package_update_index --maxage 3600
AUTHORS
-------
-Ricardo Catalinas Jiménez
+| Ricardo Catalinas Jiménez
+| Thomas Eckert
+| Stu Zhao
COPYING
diff --git a/cdist/conf/type/__package_update_index/parameter/optional b/cdist/conf/type/__package_update_index/parameter/optional
index aa80e646..7a0be716 100644
--- a/cdist/conf/type/__package_update_index/parameter/optional
+++ b/cdist/conf/type/__package_update_index/parameter/optional
@@ -1 +1,2 @@
type
+maxage
diff --git a/cdist/conf/type/__package_upgrade_all/gencode-remote b/cdist/conf/type/__package_upgrade_all/gencode-remote
index bcad8a43..38aa001e 100755
--- a/cdist/conf/type/__package_upgrade_all/gencode-remote
+++ b/cdist/conf/type/__package_upgrade_all/gencode-remote
@@ -53,8 +53,8 @@ case "$type" in
;;
apt)
if [ -f "$apt_dist_upgrade" ]
- then echo $aptget dist-upgrade
- else echo $aptget upgrade
+ then echo "$aptget dist-upgrade"
+ else echo "$aptget upgrade"
fi
if [ -f "$apt_clean" ]
diff --git a/cdist/conf/type/__package_yum/explorer/pkg_version b/cdist/conf/type/__package_yum/explorer/pkg_version
index fb3b7753..b81b0fe9 100755
--- a/cdist/conf/type/__package_yum/explorer/pkg_version
+++ b/cdist/conf/type/__package_yum/explorer/pkg_version
@@ -27,4 +27,4 @@ else
name="$__object_id"
fi
-rpm -q --whatprovides "$name" 2>/dev/null || true
+rpm -q "$name" 2>/dev/null || rpm -q --whatprovides "$name" 2>/dev/null || true
diff --git a/cdist/conf/type/__package_yum/gencode-remote b/cdist/conf/type/__package_yum/gencode-remote
index e9b48ee8..b52953f6 100755
--- a/cdist/conf/type/__package_yum/gencode-remote
+++ b/cdist/conf/type/__package_yum/gencode-remote
@@ -43,10 +43,15 @@ else
opts="--assumeyes --quiet"
fi
-not_installed="^no package provides"
+not_provided="^no package provides"
+not_installed='is not installed$'
-if grep -q "$not_installed" "$__object/explorer/pkg_version"; then
- state_is="absent"
+if grep -q "$not_provided" "$__object/explorer/pkg_version"; then
+ if grep -q "$not_installed" "$__object/explorer/pkg_version"; then
+ state_is="absent"
+ else
+ state_is="present"
+ fi
else
state_is="present"
fi
@@ -55,10 +60,12 @@ fi
case "$state_should" in
present)
- echo yum $opts install \"$install_name\"
+ echo "yum $opts install '$install_name'"
+ echo "installed" >> "$__messages_out"
;;
absent)
- echo yum $opts remove \"$name\"
+ echo "yum $opts remove '$name'"
+ echo "removed" >> "$__messages_out"
;;
*)
echo "Unknown state: $state_should" >&2
diff --git a/cdist/conf/type/__package_zypper/gencode-remote b/cdist/conf/type/__package_zypper/gencode-remote
index d9372b6d..e45dd9ff 100755
--- a/cdist/conf/type/__package_zypper/gencode-remote
+++ b/cdist/conf/type/__package_zypper/gencode-remote
@@ -61,15 +61,18 @@ case "$state_should" in
present)
if [ -z "$version_should" ]; then
[ "$state_is" = "present" ] && exit 0 # if state is present, we dont need to do anything
- echo zypper $globalopts install --type \"$ptype\" --auto-agree-with-licenses \"$name\" ">/dev/null"
+ echo "zypper $globalopts install --type '$ptype' --auto-agree-with-licenses '$name' >/dev/null"
+ echo "removed" >> "$__messages_out"
else
[ "$state_is" = "present" ] && [ "$version_should" = "$version_is" ] && exit 0 # if state is present and version is correct, we dont need to do anything
- echo zypper $globalopts install --oldpackage --type \"$ptype\" --auto-agree-with-licenses \"$name\" = \"$version_should\" ">/dev/null"
+ echo "zypper $globalopts install --oldpackage --type '$ptype' --auto-agree-with-licenses '$name' = '$version_should' >/dev/null"
+ echo "installed" >> "$__messages_out"
fi
;;
absent)
[ "$state_is" = "absent" ] && exit 0 # if state is absent, we dont need to do anything
- echo zypper $globalopts remove --type \"$ptype\" \"$name\" ">/dev/null"
+ echo "zypper $globalopts remove --type '$ptype' '$name' >/dev/null"
+ echo "removed" >> "$__messages_out"
;;
*)
echo "Unknown state: $state_should" >&2
diff --git a/cdist/conf/type/__pacman_conf/manifest b/cdist/conf/type/__pacman_conf/manifest
index 1561d613..a43f18a1 100755
--- a/cdist/conf/type/__pacman_conf/manifest
+++ b/cdist/conf/type/__pacman_conf/manifest
@@ -59,13 +59,13 @@ if [ "${file}" ]; then
if [ "${state}" = "present" ]; then
- require="__file/${sec_path}/plain_file_${file}" __key_value ${file}_${key}\
- --file ${sec_path}/plain_file_${file} --key ${key} --value ${value} --delimiter ' = '
+ require="__file/${sec_path}/plain_file_${file}" __key_value "${file}_${key}" \
+ --file "${sec_path}/plain_file_${file}" --key "${key}" --value "${value}" --delimiter ' = '
exit 0
elif [ "${state}" = "absent" ]; then
- require="__file/${sec_path}/plain_file_${file}" __key_value ${file}_${key}\
+ require="__file/${sec_path}/plain_file_${file}" __key_value "${file}_${key}" \
--state absent
exit 0
@@ -87,19 +87,19 @@ eof
if [ "${MATCH}" -eq 1 ]; then
if [ "${value}" = "on" ]; then
- require="__file/${sec_path}/${section}" __line ${key}_${value}\
- --file ${sec_path}/${section} --line ${key}
+ require="__file/${sec_path}/${section}" __line "${key}_${value}" \
+ --file "${sec_path}/${section}" --line "${key}"
elif [ "${value}" = "off" ]; then
- require="__file/${sec_path}/${section}" __line ${key}_${value}\
- --file ${sec_path}/${section} --line ${key} --state absent
+ require="__file/${sec_path}/${section}" __line "${key}_${value}" \
+ --file "${sec_path}/${section}" --line "${key}" --state absent
fi
else
contains_element "${key}" "${allowed_option_keys}"
if [ "${MATCH}" -eq 1 ]; then
- require="__file/${sec_path}/${section}" __key_value ${section}_${key}\
- --file ${sec_path}/${section} --key ${key} --value ${value} --delimiter ' = '
+ require="__file/${sec_path}/${section}" __key_value "${section}_${key}" \
+ --file "${sec_path}/${section}" --key "${key}" --value "${value}" --delimiter ' = '
else
echo "Key: ${key} is not valid. Have a look at man pacman.conf" >&2
fi
@@ -118,12 +118,12 @@ eof
exit
fi
- require="__file/${sec_path}/repo_${section}" __key_value ${section}_${key}\
- --file ${sec_path}/repo_${section} --key ${key} --value ${value} --delimiter ' = '
+ require="__file/${sec_path}/repo_${section}" __key_value "${section}_${key}" \
+ --file "${sec_path}/repo_${section}" --key "${key}" --value "${value}" --delimiter ' = '
elif [ "${state}" = "absent" ]; then
- require="__file/${sec_path}/repo_${section}" __key_value ${section}_${key}\
+ require="__file/${sec_path}/repo_${section}" __key_value "${section}_${key}" \
--state absent
else
diff --git a/cdist/conf/type/__pacman_conf_integrate/manifest b/cdist/conf/type/__pacman_conf_integrate/manifest
index b26bca50..0ce0bee5 100755
--- a/cdist/conf/type/__pacman_conf_integrate/manifest
+++ b/cdist/conf/type/__pacman_conf_integrate/manifest
@@ -18,16 +18,14 @@
# along with cdist. If not, see .
#
-state=$(cat $__object/parameter/state 2>/dev/null)
-
-path="/etc/"
+state=$(cat "$__object/parameter/state" 2>/dev/null)
if [ "${state}" = "present" ]; then
__file /etc/pacman.conf\
- --owner root --group root --mode 644 --source $__type/files/pacman.conf.cdist
+ --owner root --group root --mode 644 --source "$__type/files/pacman.conf.cdist"
__file /etc/pacman.d/options\
- --owner root --group root --mode 644 --source $__type/files/options
+ --owner root --group root --mode 644 --source "$__type/files/options"
__file /etc/pacman.d/repo_empty_placeholder\
--owner root --group root --mode 644
@@ -38,10 +36,10 @@ if [ "${state}" = "present" ]; then
elif [ "${state}" = "absent" ]; then
__file /etc/pacman.conf\
- --owner root --group root --mode 644 --source $__type/files/pacman.conf.pacman
+ --owner root --group root --mode 644 --source "$__type/files/pacman.conf.pacman"
__file /etc/pacman.d/mirrorlist\
- --owner root --group root --mode 644 --source $__type/files/mirrorlist
+ --owner root --group root --mode 644 --source "$__type/files/mirrorlist"
__file /etc/pacman.d/options\
--state absent
diff --git a/cdist/conf/type/__pf_apply/gencode-remote b/cdist/conf/type/__pf_apply/gencode-remote
deleted file mode 100755
index c8f7a25a..00000000
--- a/cdist/conf/type/__pf_apply/gencode-remote
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/bin/sh -e
-#
-# 2012 Jake Guffey (jake.guffey at eprotex.com)
-#
-# This file is part of cdist.
-#
-# cdist is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# cdist is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with cdist. If not, see .
-#
-#
-# Apply pf(4) ruleset on *BSD
-#
-
-# Debug
-#exec >&2
-#set -x
-
-rcvar=$(cat "$__object/explorer/rcvar")
-
-cat <&2
- fi
-fi
-EOF
-
-# Debug
-#set +x
-
diff --git a/cdist/conf/type/__pf_apply/man.rst b/cdist/conf/type/__pf_apply/man.rst
deleted file mode 100644
index eee345e7..00000000
--- a/cdist/conf/type/__pf_apply/man.rst
+++ /dev/null
@@ -1,55 +0,0 @@
-cdist-type__pf_apply(7)
-=======================
-
-NAME
-----
-cdist-type__pf_apply - Apply pf(4) ruleset on \*BSD
-
-
-DESCRIPTION
------------
-This type is used on \*BSD systems to manage the pf firewall's active ruleset.
-
-
-REQUIRED PARAMETERS
--------------------
-NONE
-
-
-OPTIONAL PARAMETERS
--------------------
-NONE
-
-
-EXAMPLES
---------
-
-.. code-block:: sh
-
- # Modify the ruleset on $__target_host:
- __pf_ruleset --state present --source /my/pf/ruleset.conf
- require="__pf_ruleset" \
- __pf_apply
-
- # Remove the ruleset on $__target_host (implies disabling pf(4):
- __pf_ruleset --state absent
- require="__pf_ruleset" \
- __pf_apply
-
-
-SEE ALSO
---------
-:strong:`pf`\ (4), :strong:`cdist-type__pf_ruleset`\ (7)
-
-
-AUTHORS
--------
-Jake Guffey
-
-
-COPYING
--------
-Copyright \(C) 2012 Jake Guffey. You can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
diff --git a/cdist/conf/type/__pf_apply_anchor/gencode-remote b/cdist/conf/type/__pf_apply_anchor/gencode-remote
new file mode 100755
index 00000000..36c26521
--- /dev/null
+++ b/cdist/conf/type/__pf_apply_anchor/gencode-remote
@@ -0,0 +1,33 @@
+#!/bin/sh -e
+#
+# 2016 Kamila Součková (coding at kamila.is)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+#
+# Apply pf(4) ruleset on *BSD
+#
+
+ANCHORS_DIR="/etc/pf.d"
+
+if [ -f "${__object}/parameter/anchor_name" ]; then
+ anchor_name="$(cat "${__object}/parameter/anchor_name")"
+else
+ anchor_name="${__object_id}"
+fi
+anchor_file="${ANCHORS_DIR}/${anchor_name}"
+
+echo "pfctl -a \"${anchor_name}\" -f \"${anchor_file}\""
diff --git a/cdist/conf/type/__pf_apply_anchor/man.rst b/cdist/conf/type/__pf_apply_anchor/man.rst
new file mode 100644
index 00000000..aef6cdf4
--- /dev/null
+++ b/cdist/conf/type/__pf_apply_anchor/man.rst
@@ -0,0 +1,62 @@
+cdist-type__pf_apply_anchor(7)
+==============================
+
+NAME
+----
+cdist-type__pf_apply_anchor - Apply a pf(4) anchor on $__target_host
+
+
+DESCRIPTION
+-----------
+This type is used on \*BSD systems to manage anchors for the pf firewall.
+
+Notice this type does not take care of copying the ruleset, that must be
+done by the user with, e.g. `__file`.
+
+
+OPTIONAL PARAMETERS
+-------------------
+anchor_name
+ The name of the anchor to apply. If not set, `${__object_id}` is used.
+ This type requires `/etc/pf.d/${anchor_name}` to exist on
+ `$__target_host`.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # Copy anchor file to ${__target_host}
+ __file "/etc/pf.d/80_dns" --source - <
+Kamila Součková
+Jake Guffey
+
+
+COPYING
+-------
+Copyright \(C) 2020 Evilham.
+Copyright \(C) 2016 Kamila Součková.
+Copyright \(C) 2012 Jake Guffey. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__pf_apply_anchor/parameter/optional b/cdist/conf/type/__pf_apply_anchor/parameter/optional
new file mode 100644
index 00000000..b9f61e28
--- /dev/null
+++ b/cdist/conf/type/__pf_apply_anchor/parameter/optional
@@ -0,0 +1 @@
+anchor_name
diff --git a/cdist/conf/type/__pf_ruleset/explorer/rcvar b/cdist/conf/type/__pf_ruleset/explorer/rcvar
index 20e9dfcc..7c8d535f 100755
--- a/cdist/conf/type/__pf_ruleset/explorer/rcvar
+++ b/cdist/conf/type/__pf_ruleset/explorer/rcvar
@@ -29,7 +29,7 @@
RC="/etc/rc.conf"
PFCONF="$(grep '^pf_rules=' ${RC} | cut -d= -f2 | sed 's/"//g')"
-echo ${PFCONF:-"/etc/pf.conf"}
+echo "${PFCONF:-"/etc/pf.conf"}"
# Debug
#set +x
diff --git a/cdist/conf/type/__pf_ruleset/gencode-local b/cdist/conf/type/__pf_ruleset/gencode-local
deleted file mode 100755
index b4bded98..00000000
--- a/cdist/conf/type/__pf_ruleset/gencode-local
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/bin/sh -e
-#
-# 2012 Jake Guffey (jake.guffey at eprotex.com)
-#
-# This file is part of cdist.
-#
-# cdist is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# cdist is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with cdist. If not, see .
-#
-#
-# Manage pf(4) on *BSD
-#
-
-# Debug
-#exec >&2
-#set -x
-
-# Send files to $__target_host via $__remote_copy
-
-uname=$(uname) # Need to know what the cdist host is running so we know how to compute the ruleset's checksum
-state=$(cat "$__object/parameter/state")
-
-if [ "$state" = "absent" ]; then # There is nothing more for a *local* script to do
- exit 0
-fi
-
-if [ -f "$__object/parameter/source" ]; then
- source=$(cat "$__object/parameter/source")
-fi
-
-rcvar=$(cat "$__object/explorer/rcvar")
-cksum=$(cat "$__object/explorer/cksum")
-
-
-cat <&2
- exit 1
- ;;
-esac
-
-# IPv6 fix
-if $(echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$')
-then
- my_target_host="[${__target_host}]"
-else
- my_target_host="${__target_host}"
-fi
-
-if [ -n "${cksum}" ]; then
- if [ ! "\${currentSum}" = "${cksum}" ]; then
- $__remote_copy "${source}" "${my_target_host}:${rcvar}.new"
- fi
-else # File just doesn't exist yet
- $__remote_copy "${source}" "${my_target_host}:${rcvar}.new"
-fi
-EOF
-
-# Debug
-#exec +x
-
diff --git a/cdist/conf/type/__pf_ruleset/man.rst b/cdist/conf/type/__pf_ruleset/man.rst
index 5719e94e..db8873ac 100644
--- a/cdist/conf/type/__pf_ruleset/man.rst
+++ b/cdist/conf/type/__pf_ruleset/man.rst
@@ -10,6 +10,9 @@ DESCRIPTION
-----------
This type is used on \*BSD systems to manage the pf firewall's ruleset.
+It will also enable and disable the pf firewall as requested in the `state`
+parameter.
+
REQUIRED PARAMETERS
-------------------
@@ -20,9 +23,8 @@ state
OPTIONAL PARAMETERS
-------------------
source
- If supplied, use to define the ruleset to load onto the $__target_host for pf(4).
- Note that this type is almost useless without a ruleset defined, but it's technically not
- needed, e.g. for the case of disabling the firewall temporarily.
+ Required when state is "present".
+ Defines the ruleset to load onto the $__target_host for `pf(4)`.
EXAMPLES
@@ -30,10 +32,10 @@ EXAMPLES
.. code-block:: sh
- # Remove the current ruleset in place
+ # Remove the current ruleset in place and disable pf
__pf_ruleset --state absent
- # Enable the firewall with the ruleset defined in $__manifest/files/pf.conf
+ # Enable pf with the ruleset defined in $__manifest/files/pf.conf
__pf_ruleset --state present --source $__manifest/files/pf.conf
@@ -44,11 +46,13 @@ SEE ALSO
AUTHORS
-------
+Kamila Součková
Jake Guffey
COPYING
-------
+Copyright \(C) 2016 Kamila Součková.
Copyright \(C) 2012 Jake Guffey. You can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
diff --git a/cdist/conf/type/__pf_ruleset/manifest b/cdist/conf/type/__pf_ruleset/manifest
new file mode 100755
index 00000000..27b35328
--- /dev/null
+++ b/cdist/conf/type/__pf_ruleset/manifest
@@ -0,0 +1,46 @@
+#!/bin/sh -e
+#
+# 2016 Kamila Součková (coding at kamila.is)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+#
+# Manage pf(4) on *BSD
+#
+
+rcvar="$(cat "${__object}/explorer/rcvar")"
+state="$(cat "${__object}/parameter/state")"
+if [ -f "${__object}/parameter/source" ]; then
+ source="$(cat "${__object}/parameter/source")"
+fi
+
+if [ "${state}" = "absent" ]; then
+ action="/etc/rc.d/pf stop"
+else
+ action="/etc/rc.d/pf reload || /etc/rc.d/pf start"
+fi
+
+__key_value __pf_ruleset/rcvar \
+ --state "${state}" \
+ --file /etc/rc.conf \
+ --delimiter "=" \
+ --key "pf_enable" \
+ --value "YES"
+
+require="__key_value/__pf_ruleset/rcvar" __config_file "${rcvar}" \
+ --source "${source}" \
+ --state "${state}" \
+ --onchange "${action}"
diff --git a/cdist/conf/type/__ping/gencode-remote b/cdist/conf/type/__ping/gencode-remote
new file mode 100644
index 00000000..1341b954
--- /dev/null
+++ b/cdist/conf/type/__ping/gencode-remote
@@ -0,0 +1,12 @@
+#!/bin/sh -e
+#
+# Copyright (C) 2018 Olliver Schinagl
+#
+# SPDX-License-Identifier: GPL-3.0+
+#
+
+set -eu
+
+echo "echo 'pong'"
+
+exit 0
diff --git a/cdist/conf/type/__ping/man.rst b/cdist/conf/type/__ping/man.rst
new file mode 100644
index 00000000..e08643dc
--- /dev/null
+++ b/cdist/conf/type/__ping/man.rst
@@ -0,0 +1,43 @@
+cdist-type__ping(7)
+==================================
+
+NAME
+----
+cdist-type__ping - Try to connect to host and return 'pong' on success
+
+
+DESCRIPTION
+-----------
+A simple type which tries to connect to a remote host and runs a simple command
+to ensure everything is working.
+
+
+REQUIRED PARAMETERS
+-------------------
+None.
+
+
+OPTIONAL PARAMETERS
+-------------------
+None.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ __ping
+
+
+AUTHORS
+-------
+Olliver Schinagl
+
+
+COPYING
+-------
+Copyright \(C) 2018 Schinagl. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__ping/singleton b/cdist/conf/type/__ping/singleton
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__postfix/manifest b/cdist/conf/type/__postfix/manifest
index 1aea53a1..121bba96 100755
--- a/cdist/conf/type/__postfix/manifest
+++ b/cdist/conf/type/__postfix/manifest
@@ -1,6 +1,7 @@
#!/bin/sh -e
#
# 2012-2014 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2019 Nico Schottelius (nico-cdist at schottelius.org)
#
# This file is part of cdist.
#
@@ -18,16 +19,4 @@
# along with cdist. If not, see .
#
-
-os=$(cat "$__global/explorer/os")
-
-case "$os" in
- ubuntu|debian|archlinux|suse|scientific|centos|devuan)
- __package postfix --state present
- ;;
- *)
- echo "Your operating system ($os) is currently not supported by this type (${__type##*/})." >&2
- echo "Please contribute an implementation for it if you can." >&2
- exit 1
- ;;
-esac
+__package postfix --state present
diff --git a/cdist/conf/type/__postfix_master/gencode-remote b/cdist/conf/type/__postfix_master/gencode-remote
index 7c109a69..73de1088 100755
--- a/cdist/conf/type/__postfix_master/gencode-remote
+++ b/cdist/conf/type/__postfix_master/gencode-remote
@@ -67,7 +67,7 @@ case "$state_should" in
remove_entry
fi
cat << DONE
-cat >> "$config" << ${__type##*/}_DONE
+cat >> "$config" << "${__type##*/}_DONE"
$(cat "$entry")
${__type##*/}_DONE
DONE
diff --git a/cdist/conf/type/__postfix_master/manifest b/cdist/conf/type/__postfix_master/manifest
index 4991a13d..0960ea41 100755
--- a/cdist/conf/type/__postfix_master/manifest
+++ b/cdist/conf/type/__postfix_master/manifest
@@ -36,7 +36,6 @@ __postfix
# Default to object_id
service="$(cat "$__object/parameter/service" 2>/dev/null || echo "$__object_id")"
-state="$(cat "$__object/parameter/state")"
# NOTE: keep variables in sync in manifest,explorer,gencode-*
prefix="#cdist:$__object_name"
@@ -51,7 +50,6 @@ entry="$__object/files/entry"
echo "# $(cat "$__object/parameter/comment")"
fi
printf "%s " "$service"
- printf "%s " "$type"
for parameter in type private unpriv chroot wakeup maxproc; do
printf "%s " "$(cat "$__object/parameter/$parameter")"
done
diff --git a/cdist/conf/type/__postfix_master/parameter/optional b/cdist/conf/type/__postfix_master/parameter/optional
index 792b42c5..410482b8 100644
--- a/cdist/conf/type/__postfix_master/parameter/optional
+++ b/cdist/conf/type/__postfix_master/parameter/optional
@@ -4,6 +4,5 @@ unpriv
chroot
wakeup
maxproc
-option
comment
state
diff --git a/cdist/conf/type/__postfix_master/parameter/optional_multiple b/cdist/conf/type/__postfix_master/parameter/optional_multiple
new file mode 100644
index 00000000..01925a15
--- /dev/null
+++ b/cdist/conf/type/__postfix_master/parameter/optional_multiple
@@ -0,0 +1 @@
+option
diff --git a/cdist/conf/type/__postfix_postconf/explorer/value b/cdist/conf/type/__postfix_postconf/explorer/value
index 17126c94..67dacad8 100755
--- a/cdist/conf/type/__postfix_postconf/explorer/value
+++ b/cdist/conf/type/__postfix_postconf/explorer/value
@@ -22,7 +22,7 @@
os=$("$__explorer/os")
case "$os" in
- ubuntu|debian|archlinux|suse|scientific|centos|devuan)
+ alpine|ubuntu|debian|archlinux|suse|scientific|centos|devuan)
:
;;
*)
diff --git a/cdist/conf/type/__postfix_postconf/gencode-remote b/cdist/conf/type/__postfix_postconf/gencode-remote
index 6df0da7f..279dddd4 100755
--- a/cdist/conf/type/__postfix_postconf/gencode-remote
+++ b/cdist/conf/type/__postfix_postconf/gencode-remote
@@ -1,6 +1,7 @@
#!/bin/sh -e
#
# 2012-2014 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2019 Nico Schottelius (nico-cdist at schottelius.org)
#
# This file is part of cdist.
#
@@ -21,7 +22,7 @@
os=$(cat "$__global/explorer/os")
case "$os" in
- ubuntu|debian|archlinux|suse|scientific|centos|devuan)
+ alpine|archlinux|centos|debian|devuan|suse|scientific|ubuntu)
:
;;
*)
diff --git a/cdist/conf/type/__postgres_database/explorer/state b/cdist/conf/type/__postgres_database/explorer/state
index dc9659e2..d68d4120 100755
--- a/cdist/conf/type/__postgres_database/explorer/state
+++ b/cdist/conf/type/__postgres_database/explorer/state
@@ -18,10 +18,25 @@
# along with cdist. If not, see .
#
+case "$("${__explorer}/os")"
+in
+ netbsd)
+ postgres_user='pgsql'
+ ;;
+ openbsd)
+ postgres_user='_postgresql'
+ ;;
+ *)
+ postgres_user='postgres'
+ ;;
+esac
+
+
name="$__object_id"
-if su - postgres -c "echo '\q' | psql '$name'" 2>/dev/null; then
- echo "present"
+if test -n "$(su - "$postgres_user" -c "psql postgres -twAc \"SELECT 1 FROM pg_database WHERE datname='$name'\"")"
+then
+ echo 'present'
else
- echo "absent"
+ echo 'absent'
fi
diff --git a/cdist/conf/type/__postgres_database/gencode-remote b/cdist/conf/type/__postgres_database/gencode-remote
index 92301fb8..0f11cff4 100755
--- a/cdist/conf/type/__postgres_database/gencode-remote
+++ b/cdist/conf/type/__postgres_database/gencode-remote
@@ -18,6 +18,20 @@
# along with cdist. If not, see .
#
+case "$(cat "${__global}/explorer/os")"
+in
+ netbsd)
+ postgres_user='pgsql'
+ ;;
+ openbsd)
+ postgres_user='_postgresql'
+ ;;
+ *)
+ postgres_user='postgres'
+ ;;
+esac
+
+
name="$__object_id"
state_should="$(cat "$__object/parameter/state")"
state_is="$(cat "$__object/explorer/state")"
@@ -27,12 +41,37 @@ if [ "$state_should" != "$state_is" ]; then
present)
owner=""
if [ -f "$__object/parameter/owner" ]; then
- owner="-O '$(cat "$__object/parameter/owner")'"
+ owner="-O \"$(cat "$__object/parameter/owner")\""
fi
- echo "su - postgres -c \"createdb $owner '$name'\""
+
+ template=""
+ if [ -f "$__object/parameter/template" ]; then
+ template="--template \"$(cat "$__object/parameter/template")\""
+ fi
+
+ encoding=""
+ if [ -f "$__object/parameter/encoding" ]; then
+ encoding="--encoding \"$(cat "$__object/parameter/encoding")\""
+ fi
+
+ lc_collate=""
+ if [ -f "$__object/parameter/lc-collate" ]; then
+ lc_collate="--lc-collate \"$(cat "$__object/parameter/lc-collate")\""
+ fi
+
+ lc_ctype=""
+ if [ -f "$__object/parameter/lc-ctype" ]; then
+ lc_ctype="--lc-ctype \"$(cat "$__object/parameter/lc-ctype")\""
+ fi
+
+ cat << EOF
+su - '$postgres_user' -c "createdb $owner \"$name\" $template $encoding $lc_collate $lc_ctype"
+EOF
;;
absent)
- echo "su - postgres -c \"dropdb '$name'\""
+ cat << EOF
+su - '$postgres_user' -c "dropdb \"$name\""
+EOF
;;
esac
fi
diff --git a/cdist/conf/type/__postgres_database/man.rst b/cdist/conf/type/__postgres_database/man.rst
index acceec9b..870b4917 100644
--- a/cdist/conf/type/__postgres_database/man.rst
+++ b/cdist/conf/type/__postgres_database/man.rst
@@ -14,10 +14,22 @@ This cdist type allows you to create or drop postgres databases.
OPTIONAL PARAMETERS
-------------------
state
- either 'present' or 'absent', defaults to 'present'.
+ Either 'present' or 'absent', defaults to 'present'.
owner
- the role owning this database
+ Specifies the database user who will own the new database.
+
+encoding
+ Specifies the character encoding scheme to be used in this database.
+
+lc-collate
+ Specifies the LC_COLLATE setting to be used in this database.
+
+lc-ctype
+ Specifies the LC_CTYPE setting to be used in this database.
+
+template
+ Specifies the template database from which to build this database.
EXAMPLES
diff --git a/cdist/conf/type/__postgres_database/parameter/optional b/cdist/conf/type/__postgres_database/parameter/optional
index d86b6469..877fbf32 100644
--- a/cdist/conf/type/__postgres_database/parameter/optional
+++ b/cdist/conf/type/__postgres_database/parameter/optional
@@ -1,2 +1,6 @@
state
owner
+encoding
+lc-collate
+lc-ctype
+template
diff --git a/cdist/conf/type/__postgres_extension/gencode-remote b/cdist/conf/type/__postgres_extension/gencode-remote
index f7895103..af9c97f1 100755
--- a/cdist/conf/type/__postgres_extension/gencode-remote
+++ b/cdist/conf/type/__postgres_extension/gencode-remote
@@ -22,6 +22,20 @@
# along with cdist. If not, see .
#
+case "$(cat "${__global}/explorer/os")"
+in
+ netbsd)
+ postgres_user='pgsql'
+ ;;
+ openbsd)
+ postgres_user='_postgresql'
+ ;;
+ *)
+ postgres_user='postgres'
+ ;;
+esac
+
+
dbname=$( echo "$__object_id" | cut -d":" -f1 )
extension=$( echo "$__object_id" | cut -d":" -f2 )
@@ -30,10 +44,10 @@ state_should=$( cat "$__object/parameter/state" )
case "$state_should" in
present)
cmd="CREATE EXTENSION IF NOT EXISTS $extension"
- echo "su - postgres -c 'psql -c \"$cmd\" \"$dbname\"'"
+ echo "su - '$postgres_user' -c 'psql -c \"$cmd\" \"$dbname\"'"
;;
absent)
- cmd="DROP EXTENSION IF EXISTS $extenstion"
- echo "su - postgres -c 'psql -c \"$cmd\" \"$dbname\"'"
+ cmd="DROP EXTENSION IF EXISTS $extension"
+ echo "su - '$postgres_user' -c 'psql -c \"$cmd\" \"$dbname\"'"
;;
esac
diff --git a/cdist/conf/type/__postgres_role/explorer/state b/cdist/conf/type/__postgres_role/explorer/state
index 8c102df9..34069de9 100755
--- a/cdist/conf/type/__postgres_role/explorer/state
+++ b/cdist/conf/type/__postgres_role/explorer/state
@@ -1,6 +1,7 @@
-#!/bin/sh
+#!/bin/sh -e
#
# 2011 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -11,17 +12,140 @@
#
# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see .
#
-name="$__object_id"
+case $("${__explorer:?}/os")
+in
+ (netbsd)
+ postgres_user='pgsql'
+ ;;
+ (openbsd)
+ postgres_user='_postgresql'
+ ;;
+ (*)
+ postgres_user='postgres'
+ ;;
+esac
-if su - postgres -c "psql -c '\du' | grep -q '^ *$name *|'"; then
- echo "present"
+rolename=${__object_id:?}
+
+
+psql_query() {
+ su -l "${postgres_user}" -c "$(
+ printf "psql -q -F '\034' -R '\036' -wAc '%s'" \
+ "$(printf %s "$*" | sed "s/'/'\\\\''/g")"
+ )"
+}
+
+password_check_login() (
+ PGPASSWORD=$(cat "${__object:?}/parameter/password"; printf .)
+ PGPASSWORD=${PGPASSWORD%?.}
+ export PGPASSWORD
+ psql -q -w -h localhost -U "${rolename}" template1 -c '\q' >/dev/null 2>&1
+)
+
+role_properties=$(
+ psql_query "SELECT * FROM pg_roles WHERE rolname = '${rolename}'" \
+ | awk '
+ BEGIN { RS = "\036"; FS = "\034" }
+ /^\([0-9]+ rows?\)/ { exit }
+ NR == 1 { for (i = 1; i <= NF; i++) cols[i] = $i; next }
+ NR == 2 { for (i = 1; i <= NF; i++) printf "%s=%s\n", cols[i], $i }
+ '
+)
+
+if test -n "${role_properties}"
+then
+ # Check if the user's properties match the parameters
+ for prop in login createdb createrole superuser
+ do
+ bool_should=$(test -f "${__object:?}/parameter/${prop}" && echo 't' || echo 'f')
+ bool_is=$(
+ printf '%s\n' "${role_properties}" |
+ awk -F '=' -v key="${prop}" '
+ BEGIN {
+ if (key == "login")
+ key = "canlogin"
+ else if (key == "superuser")
+ key = "super"
+ key = "rol" key
+ }
+ $1 == key {
+ sub(/^[^=]*=/, "")
+ print
+ }
+ '
+ )
+
+ test "${bool_is}" = "${bool_should}" || {
+ state='different properties'
+ }
+ done
+
+ # Check password
+ passwd_stored=$(
+ psql_query "SELECT rolpassword FROM pg_authid WHERE rolname = '${rolename}'" \
+ | awk 'BEGIN { RS = "\036" } NR == 2'
+ printf .
+ )
+ passwd_stored=${passwd_stored%?.}
+
+ if test -f "${__object:?}/parameter/password"
+ then
+ passwd_should=$(cat "${__object:?}/parameter/password"; printf .)
+ fi
+ passwd_should=${passwd_should%?.}
+
+ if test -z "${passwd_stored}"
+ then
+ test -z "${passwd_should}" || state="${state:-different} password"
+ elif expr "${passwd_stored}" : 'SCRAM-SHA-256\$.*$' >/dev/null
+ then
+ # SCRAM-SHA-256 "encrypted" password
+ # NOTE: There is currently no easy way to check SCRAM passwords without
+ # logging in
+ password_check_login || state="${state:-different} password"
+ elif expr "${passwd_stored}" : 'md5[0-9a-f]\{32\}$' >/dev/null
+ then
+ # MD5 "encrypted" password
+ if command -v md5sum >/dev/null 2>&1
+ then
+ should_md5=$(
+ printf '%s%s' "${passwd_should}" "${rolename}" \
+ | md5sum - | sed -e 's/[^0-9a-f]*$//')
+ elif command -v gmd5sum >/dev/null 2>&1
+ then
+ should_md5=$(
+ printf '%s%s' "${passwd_should}" "${rolename}" \
+ | gmd5sum - | sed -e 's/[^0-9a-f]*$//')
+ elif command -v openssl >/dev/null 2>&1
+ then
+ should_md5=$(
+ printf '%s%s' "${passwd_should}" "${rolename}" \
+ | openssl dgst -md5 | sed 's/^.* //')
+ fi
+
+ if test -n "${should_md5}"
+ then
+ test "${passwd_stored}" = "md5${should_md5}" \
+ || state="${state:-different} password"
+ else
+ password_check_login || state="${state:-different} password"
+ fi
+ else
+ # unencrypted password (unsupported since PostgreSQL 10)
+ test "${passwd_stored}" = "${passwd_should}" \
+ || state="${state:-different} password"
+ fi
+
+ test -n "${state}" || state='present'
else
- echo "absent"
+ state='absent'
fi
+
+echo "${state}"
diff --git a/cdist/conf/type/__postgres_role/gencode-remote b/cdist/conf/type/__postgres_role/gencode-remote
index 14240992..d7631fbd 100755
--- a/cdist/conf/type/__postgres_role/gencode-remote
+++ b/cdist/conf/type/__postgres_role/gencode-remote
@@ -1,6 +1,7 @@
#!/bin/sh -e
#
# 2011 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -11,39 +12,117 @@
#
# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see .
#
-name="$__object_id"
-state_is="$(cat "$__object/explorer/state")"
-state_should="$(cat "$__object/parameter/state")"
+quote() {
+ if test $# -gt 0
+ then
+ printf '%s' "$*"
+ else
+ cat -
+ fi | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/"
+}
-[ "$state_is" = "$state_should" ] && exit 0
-
-case "$state_should" in
- present)
- if [ -f "$__object/parameter/password" ]; then
- password="$(cat "$__object/parameter/password")"
- fi
- booleans=""
- for boolean in login createdb createrole superuser; do
- if [ ! -f "$__object/parameter/$boolean" ]; then
- boolean="no${boolean}"
- fi
- upper=$(echo $boolean | tr '[a-z]' '[A-Z]')
- booleans="$booleans $upper"
- done
-
- [ -n "$password" ] && password="PASSWORD '$password'"
-
- cmd="CREATE ROLE $name WITH $password $booleans"
- echo "su - postgres -c \"psql -c \\\"$cmd\\\"\""
- ;;
- absent)
- echo "su - postgres -c \"dropuser \\\"$name\\\"\""
- ;;
+case $(cat "${__global:?}/explorer/os")
+in
+ (netbsd)
+ postgres_user='pgsql'
+ ;;
+ (openbsd)
+ postgres_user='_postgresql'
+ ;;
+ (*)
+ postgres_user='postgres'
+ ;;
+esac
+
+
+rolename=${__object_id:?}
+state_is=$(cat "${__object:?}/explorer/state")
+state_should=$(cat "${__object:?}/parameter/state")
+
+if test "${state_is}" = "${state_should}"
+then
+ exit 0
+fi
+
+psql_query() {
+ printf 'su -l %s -c %s\n' \
+ "$(quote "${postgres_user}")" \
+ "$(quote "psql postgres -q -w -c $(quote "$1")")"
+}
+
+psql_set_password() {
+ # NOTE: Always make sure that the password does not end up in psql_history!
+ # NOTE: Never set an empty string as the password, because they can be
+ # interpreted differently by different tooling.
+ if test -s "${__object:?}/parameter/password"
+ then
+ cat <<-EOF
+ exec 3< "\${__object:?}/parameter/password"
+ su -l '${postgres_user}' -c 'psql -q -w postgres' <<'SQL'
+ \set HISTFILE /dev/null
+ \set pw \`cat <&3\`
+ ALTER ROLE "${rolename}" WITH PASSWORD :'pw';
+ SQL
+ exec 3<&-
+ EOF
+ else
+ psql_query "ALTER ROLE \"${rolename}\" WITH PASSWORD NULL;"
+ fi
+}
+
+role_properties_should() {
+ _props=
+ for _prop in login createdb createrole superuser
+ do
+ _props="${_props}${_props:+ }$(
+ if test -f "${__object:?}/parameter/${_prop}"
+ then
+ echo "${_prop}"
+ else
+ echo "no${_prop}"
+ fi \
+ | tr '[:lower:]' '[:upper:]')"
+ done
+ printf '%s\n' "${_props}"
+ unset _prop _props
+}
+
+case ${state_should}
+in
+ (present)
+ case ${state_is}
+ in
+ (absent)
+ psql_query "CREATE ROLE \"${rolename}\" WITH $(role_properties_should);"
+ psql_set_password
+ ;;
+ (different*)
+ if expr "${state_is}" : 'different.*properties' >/dev/null
+ then
+ psql_query "ALTER ROLE \"${rolename}\" WITH $(role_properties_should);"
+ fi
+
+ if expr "${state_is}" : 'different.*password' >/dev/null
+ then
+ psql_set_password
+ fi
+ ;;
+ (*)
+ printf 'Invalid state reported by state explorer: %s\n' "${state_is}" >&2
+ exit 1
+ ;;
+ esac
+ ;;
+ (absent)
+ printf 'su -l %s -c %s\n' \
+ "$(quote "${postgres_user}")" \
+ "$(quote "dropuser $(quote "${rolename}")")"
+ ;;
esac
diff --git a/cdist/conf/type/__process/gencode-remote b/cdist/conf/type/__process/gencode-remote
index fc491321..ec9691b9 100755
--- a/cdist/conf/type/__process/gencode-remote
+++ b/cdist/conf/type/__process/gencode-remote
@@ -52,7 +52,7 @@ case "$state_should" in
if [ -f "$__object/parameter/stop" ]; then
cat "$__object/parameter/stop"
else
- echo kill "${runs}"
+ echo kill "$(cat "$__object/parameter/runs")"
fi
echo "stopped" >> "$__messages_out"
;;
diff --git a/cdist/conf/type/__prometheus_alertmanager/man.rst b/cdist/conf/type/__prometheus_alertmanager/man.rst
index ba99e7c8..67e97eaf 100644
--- a/cdist/conf/type/__prometheus_alertmanager/man.rst
+++ b/cdist/conf/type/__prometheus_alertmanager/man.rst
@@ -10,27 +10,27 @@ DESCRIPTION
-----------
Install and configure Prometheus Alertmanager (https://prometheus.io/docs/alerting/alertmanager/).
-This type create a daemontools-compatible service directory under /service/prometheus.
-Daemontools (or something compatible) must be installed (in particular, the command `svc` must be executable).
+Note that due to significant differences between Prometheus 1.x and 2.x, only 2.x is supported. It is your responsibility to make sure that your package manager installs 2.x. (On Devuan Ascii, the parameter `--install-from-backports` helps.)
REQUIRED PARAMETERS
-------------------
config
Alertmanager configuration file. It will be saved as /etc/alertmanager/alertmanager.yml on the target.
-listen-address
- Passed as web.listen-address.
OPTIONAL PARAMETERS
-------------------
storage-path
Where to put data. Default: /data/alertmanager. (Directory will be created if needed.)
+retention-days
+ How long to retain data. Default: 90 days.
BOOLEAN PARAMETERS
------------------
-None
+install-from-backports
+ Valid on Devuan only. Will enable the backports apt source and install the package from there. Useful for getting a newer version.
EXAMPLES
@@ -38,21 +38,15 @@ EXAMPLES
.. code-block:: sh
- ALERTPORT=9093
-
- __daemontools
- __golang_from_vendor --version 1.8.1 # required for prometheus and many exporters
-
- require="__daemontools __golang_from_vendor" __prometheus_alertmanager \
- --with-daemontools \
- --config "$__manifest/files/alertmanager.yml" \
- --storage-path /data/alertmanager \
- --listen-address "[::]:$ALERTPORT"
+ __prometheus_alertmanager \
+ --install-from-backports \
+ --config "$__manifest/files/alertmanager.yml" \
+ --storage-path /data/alertmanager
SEE ALSO
--------
-:strong:`cdist-type__prometheus_server`\ (7), :strong:`cdist-type__daemontools`\ (7),
+:strong:`cdist-type__prometheus_server`\ (7), :strong:`cdist-type__grafana_dashboard`\ (7),
Prometheus alerting documentation: https://prometheus.io/docs/alerting/overview/
AUTHORS
@@ -61,7 +55,7 @@ Kamila Součková
COPYING
-------
-Copyright \(C) 2017 Kamila Součková. You can redistribute it
+Copyright \(C) 2018 Kamila Součková. You can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
diff --git a/cdist/conf/type/__prometheus_alertmanager/manifest b/cdist/conf/type/__prometheus_alertmanager/manifest
index 0dbce3c2..cf410c44 100755
--- a/cdist/conf/type/__prometheus_alertmanager/manifest
+++ b/cdist/conf/type/__prometheus_alertmanager/manifest
@@ -1,34 +1,65 @@
#!/bin/sh -e
-GOBIN=/opt/gocode/bin # where to find go binaries
+##### HARD-CODED CONFIG #####################################################
+
CONF_DIR=/etc/prometheus
-LOGLEVEL=info
CONF=$CONF_DIR/alertmanager.yml
-### Prometheus server #######################################################
+##### GET SETTINGS ##########################################################
config="$(cat "$__object/parameter/config")"
+retention_days="$(cat "$__object/parameter/retention-days")"
storage_path="$(cat "$__object/parameter/storage-path")"
-listen_address="$(cat "$__object/parameter/listen-address")"
+# listen_address="$(cat "$__object/parameter/listen-address")"
-FLAGS="config.file '$CONF'
-storage.path '$storage_path'
-web.listen-address '$listen_address'
-log.level $LOGLEVEL"
+##### INSTALL THE PACKAGE ###################################################
-REAL_FLAGS="$(echo "$FLAGS" | sed -nE 's/^([^#]+).*/ --\1 \\/p')"
+require_pkg="" # what to require if I want to require "the package"
+require=""
+if [ -f "$__object/parameter/install-from-backports" ]; then
+ os=$(cat "$__global/explorer/os")
+ os_version=$(cat "$__global/explorer/os_version")
-__go_get github.com/prometheus/alertmanager/cmd/...
+ case $os in
+ devuan)
+ [ "$os_version" = "ascii/ceres" ] && os_version='ascii' # "ascii" used in the repo URLs
+ __apt_source backports --uri http://auto.mirror.devuan.org/merged --distribution $os_version-backports --component main
+ require="$require __apt_source/backports" __package_apt prometheus-alertmanager --target-release $os_version-backports
+ require_pkg="__package_apt/prometheus-alertmanager"
+ ;;
+ *)
+ echo "--install-from-backports is only supported on Devuan -- ignoring." >&2
+ echo "Send a pull request if you require it." >&2
+ exit 1
+ ;;
+ esac
+else
+ __package prometheus-alertmanager
+ require_pkg="__package/prometheus-alertmanager"
+fi
-__user prometheus --system
-require="__user/prometheus" __directory "$storage_path" --owner prometheus --parents
-require="__user/prometheus" __directory "$CONF_DIR" --owner prometheus --parents
+##### PREPARE PATHS AND SUCH ################################################
-__daemontools_service alertmanager --run "setuidgid prometheus $GOBIN/alertmanager $REAL_FLAGS"
+require="$require $require_pkg" __directory "$storage_path" --owner prometheus --parents
-require="$require __directory/$storage_path __user/prometheus" \
+# TODO this is a bug in the init script, patching it like this is awful and it should be reported
+require="$require $require_pkg" \
+__key_value alertmanager_fix_init_script --file /etc/init.d/prometheus-alertmanager \
+ --key "NAME" --value "prometheus-alertmanager" --delimiter "=" \
+ --onchange "service prometheus-alertmanager restart"
+
+##### CONFIGURE #############################################################
+
+FLAGS="--storage.path $storage_path --data.retention $((retention_days*24))h --web.listen-address [::]:9093 --cluster.advertise-address [::]:9093"
+
+require="$require $require_pkg" \
+__key_value alertmanager_args --file /etc/default/prometheus-alertmanager \
+ --key "ARGS" --value "\"$FLAGS\"" --delimiter "=" \
+ --onchange "service prometheus-alertmanager restart"
+
+require="$require __directory/$storage_path $require_pkg" \
__config_file $CONF \
- --source $config \
+ --source "$config" \
--group prometheus --mode 640 \
- --onchange "svc -h /service/alertmanager" # TODO when a config-check tool is available, check config here
+ --onchange "service prometheus-alertmanager restart" # TODO when a config-check tool is available, check config here
diff --git a/cdist/conf/type/__prometheus_alertmanager/parameter/boolean b/cdist/conf/type/__prometheus_alertmanager/parameter/boolean
new file mode 100644
index 00000000..5d15e93d
--- /dev/null
+++ b/cdist/conf/type/__prometheus_alertmanager/parameter/boolean
@@ -0,0 +1 @@
+install-from-backports
diff --git a/cdist/conf/type/__prometheus_alertmanager/parameter/default/retention-days b/cdist/conf/type/__prometheus_alertmanager/parameter/default/retention-days
new file mode 100644
index 00000000..d61f00d8
--- /dev/null
+++ b/cdist/conf/type/__prometheus_alertmanager/parameter/default/retention-days
@@ -0,0 +1 @@
+90
diff --git a/cdist/conf/type/__prometheus_alertmanager/parameter/optional b/cdist/conf/type/__prometheus_alertmanager/parameter/optional
index f99d0d37..7fe79009 100644
--- a/cdist/conf/type/__prometheus_alertmanager/parameter/optional
+++ b/cdist/conf/type/__prometheus_alertmanager/parameter/optional
@@ -1 +1,2 @@
storage-path
+retention-days
diff --git a/cdist/conf/type/__prometheus_alertmanager/parameter/required b/cdist/conf/type/__prometheus_alertmanager/parameter/required
index 02cb49d0..04204c7c 100644
--- a/cdist/conf/type/__prometheus_alertmanager/parameter/required
+++ b/cdist/conf/type/__prometheus_alertmanager/parameter/required
@@ -1,2 +1 @@
config
-listen-address
diff --git a/cdist/conf/type/__prometheus_exporter/manifest b/cdist/conf/type/__prometheus_exporter/manifest
index ae4ed94a..f3930ac6 100644
--- a/cdist/conf/type/__prometheus_exporter/manifest
+++ b/cdist/conf/type/__prometheus_exporter/manifest
@@ -2,11 +2,14 @@
export GOBIN=/opt/gocode/bin # where to find go binaries
-exporter="$(cat $__object/parameter/exporter)"
+exporter="$(cat "$__object/parameter/exporter")"
[ -z "$exporter" ] && exporter="$__object_id"
-__user prometheus --system
+__user prometheus
+require="__user/prometheus" __group prometheus
+require="__group/prometheus" __user_groups prometheus --group prometheus
+require="__user_groups/prometheus"
case $exporter in
node)
TEXTFILES=/service/node-exporter/textfiles # path for the textfiles collector
@@ -18,7 +21,7 @@ case $exporter in
;;
blackbox)
require="$require __daemontools_service/${exporter}-exporter __user/prometheus" __config_file "/service/${exporter}-exporter/blackbox.yml" \
- --source $__type/files/blackbox.yml \
+ --source "$__type/files/blackbox.yml" \
--group prometheus --mode 640 \
--onchange "svc -h /service/${exporter}-exporter"
require="$require __golang_from_vendor" __go_get github.com/prometheus/blackbox_exporter
@@ -39,9 +42,9 @@ case $exporter in
;;
esac
-require="$require __daemontools" __daemontools_service ${exporter}-exporter --run "$run"
+require="$require __daemontools" __daemontools_service "${exporter}-exporter" --run "$run"
if [ -f "$__object/parameter/add-consul-service" ]; then
- __consul_service ${exporter}-exporter --port $port --check-http "http://localhost:$port/metrics" --check-interval 10s
+ __consul_service "${exporter}-exporter" --port "$port" --check-http "http://localhost:$port/metrics" --check-interval 10s
fi
#__daemontools --install-init-script
diff --git a/cdist/conf/type/__prometheus_server/man.rst b/cdist/conf/type/__prometheus_server/man.rst
index fadebd3f..ab6a3c9b 100644
--- a/cdist/conf/type/__prometheus_server/man.rst
+++ b/cdist/conf/type/__prometheus_server/man.rst
@@ -10,18 +10,12 @@ DESCRIPTION
-----------
Install and configure Prometheus (https://prometheus.io/).
-This type creates a daemontools-compatible service directory under /service/prometheus.
-Daemontools (or something compatible) must be installed (in particular, the command `svc` must be executable).
-
+Note that due to significant differences between Prometheus 1.x and 2.x, only 2.x is supported. It is your responsibility to make sure that your package manager installs 2.x. (On Devuan Ascii, the parameter `--install-from-backports` helps.)
REQUIRED PARAMETERS
-------------------
config
Prometheus configuration file. It will be saved as /etc/prometheus/prometheus.yml on the target.
-listen-address
- Passed as web.listen-address.
-alertmanager-url
- Passed as alertmanager.url
OPTIONAL PARAMETERS
@@ -32,13 +26,12 @@ rule-files
Path to rule files. They will be installed under /etc/prometheus/. You need to include `rule_files: [/etc/prometheus/]` in the config file if you use this.
storage-path
Where to put data. Default: /data/prometheus. (Directory will be created if needed.)
-target-heap-size
- Passed as storage.local.target-heap-size. Default: 1/2 of RAM.
BOOLEAN PARAMETERS
------------------
-None
+install-from-backports
+ Valid on Devuan only. Will enable the backports apt source and install the package from there. Useful for getting a newer version.
EXAMPLES
@@ -49,22 +42,17 @@ EXAMPLES
PROMPORT=9090
ALERTPORT=9093
- __daemontools
- __golang_from_vendor --version 1.8.1 # required for prometheus and many exporters
-
- require="__daemontools __golang_from_vendor" __prometheus_server \
- --with-daemontools \
+ __prometheus_server \
+ --install-from-backports \
--config "$__manifest/files/prometheus.yml" \
--retention-days 14 \
--storage-path /data/prometheus \
- --listen-address "[::]:$PROMPORT" \
- --rule-files "$__manifest/files/*.rules" \
- --alertmanager-url "http://monitoring1.node.consul:$ALERTPORT,http://monitoring2.node.consul:$ALERTPORT"
+ --rule-files "$__manifest/files/*.rules"
SEE ALSO
--------
-:strong:`cdist-type__prometheus_alertmanager`\ (7), :strong:`cdist-type__daemontools`\ (7),
+:strong:`cdist-type__prometheus_alertmanager`\ (7), :strong:`cdist-type__grafana_dashboard`\ (7),
Prometheus documentation: https://prometheus.io/docs/introduction/overview/
AUTHORS
@@ -73,7 +61,7 @@ Kamila Součková
COPYING
-------
-Copyright \(C) 2017 Kamila Součková. You can redistribute it
+Copyright \(C) 2018 Kamila Součková. You can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
diff --git a/cdist/conf/type/__prometheus_server/manifest b/cdist/conf/type/__prometheus_server/manifest
index 96717ed6..9756169e 100755
--- a/cdist/conf/type/__prometheus_server/manifest
+++ b/cdist/conf/type/__prometheus_server/manifest
@@ -1,52 +1,73 @@
#!/bin/sh -e
-GOBIN=/opt/gocode/bin # where to find go binaries
+##### HARD-CODED CONFIG #####################################################
+
CONF_DIR=/etc/prometheus
CONF=$CONF_DIR/prometheus.yml
-LOGLEVEL=info
+
+##### GET SETTINGS ##########################################################
config="$(cat "$__object/parameter/config")"
retention_days="$(cat "$__object/parameter/retention-days")"
storage_path="$(cat "$__object/parameter/storage-path")"
-listen_address="$(cat "$__object/parameter/listen-address")"
-alertmanager_url="$(cat "$__object/parameter/alertmanager-url")"
-target_heap_size="$(cat "$__object/parameter/target-heap-size")"
rule_files="$(cat "$__object/parameter/rule-files")"
# explorer in kB => convert; by default we go with 1/2 RAM
-[ "$target_heap_size" = "auto" ] && target_heap_size=$(($(cat $__global/explorer/memory)*1024/2))
+[ "$target_heap_size" = "auto" ] && target_heap_size=$(($(cat "$__global/explorer/memory")*1024/2))
+##### INSTALL THE PACKAGE ###################################################
-FLAGS="config.file '$CONF'
-storage.local.path '$storage_path'
-storage.local.target-heap-size $(($target_heap_size)) # in bytes; should be 2/3 of available memory because it may be hungry
-storage.local.retention $(($retention_days*24))h # golang doesn't have days :D
-web.listen-address '$listen_address'
-alertmanager.url '$alertmanager_url'
-log.level $LOGLEVEL"
+require_pkg="" # what to require if I want to require "the package"
+require=""
+if [ -f "$__object/parameter/install-from-backports" ]; then
+ os=$(cat "$__global/explorer/os")
+ os_version=$(cat "$__global/explorer/os_version")
-REAL_FLAGS="$(echo "$FLAGS" | sed -nE 's/^([^#]+).*/ --\1 \\/p')"
+ case $os in
+ devuan)
+ [ "$os_version" = "ascii/ceres" ] && os_version='ascii' # "ascii" used in the repo URLs
+ __apt_source backports --uri http://auto.mirror.devuan.org/merged --distribution $os_version-backports --component main
+ require="$require __apt_source/backports" __package_apt prometheus --target-release $os_version-backports
+ require_pkg="__package_apt/prometheus"
+ ;;
+ *)
+ echo "--install-from-backports is only supported on Devuan -- ignoring." >&2
+ echo "Send a pull request if you require it." >&2
+ exit 1
+ ;;
+ esac
+else
+ __package prometheus
+ __package prometheus-blackbox-exporter
+ require_pkg="__package/prometheus __package/prometheus-blackbox-exporter"
+fi
-__go_get github.com/prometheus/prometheus/cmd/...
+##### PREPARE PATHS AND SUCH ################################################
-__user prometheus --system
-require="__user/prometheus" __directory "$storage_path" --owner prometheus --parents
-require="__user/prometheus" __directory "$CONF_DIR" --owner prometheus --parents
+require="$require $require_pkg" __directory "$storage_path" --owner prometheus --parents
-__daemontools_service prometheus --run "setuidgid prometheus $GOBIN/prometheus $REAL_FLAGS"
+##### CONFIGURE #############################################################
-require="$require __directory/$storage_path __user/prometheus" \
+FLAGS="--storage.tsdb.path $storage_path --storage.tsdb.retention $((retention_days*24))h --web.listen-address [::]:9090"
+
+# TODO it would be neat to restart prometheus on change -- __key_value really should have an --onchange parameter
+require="$require $require_pkg" \
+__key_value prometheus_args --file /etc/default/prometheus \
+ --key "ARGS" --value "\"$FLAGS\"" --delimiter "=" \
+ --onchange "service prometheus restart"
+
+require="$require __directory/$storage_path $require_pkg" \
__config_file $CONF \
- --source $config \
+ --source "$config" \
--group prometheus --mode 640 \
- --onchange "$GOBIN/promtool check-config $CONF && svc -h /service/prometheus"
+ --onchange "promtool check config $CONF && service prometheus restart"
for file in $rule_files; do
- dest=$CONF_DIR/$(basename $file)
- require="$require __directory/$CONF_DIR __user/prometheus" \
+ dest=$CONF_DIR/$(basename "$file")
+ require="$require $require_pkg" \
__config_file "$dest" \
--source "$file" \
--owner prometheus \
- --onchange "$GOBIN/promtool check-rules '$dest' && svc -h /service/prometheus"
+ --onchange "promtool check rules '$dest' && service prometheus restart"
done
diff --git a/cdist/conf/type/__prometheus_server/parameter/boolean b/cdist/conf/type/__prometheus_server/parameter/boolean
new file mode 100644
index 00000000..5d15e93d
--- /dev/null
+++ b/cdist/conf/type/__prometheus_server/parameter/boolean
@@ -0,0 +1 @@
+install-from-backports
diff --git a/cdist/conf/type/__prometheus_server/parameter/default/target-heap-size b/cdist/conf/type/__prometheus_server/parameter/default/target-heap-size
deleted file mode 100644
index 865faf10..00000000
--- a/cdist/conf/type/__prometheus_server/parameter/default/target-heap-size
+++ /dev/null
@@ -1 +0,0 @@
-auto
diff --git a/cdist/conf/type/__prometheus_server/parameter/optional b/cdist/conf/type/__prometheus_server/parameter/optional
index 4d8d8f3e..cb437211 100644
--- a/cdist/conf/type/__prometheus_server/parameter/optional
+++ b/cdist/conf/type/__prometheus_server/parameter/optional
@@ -1,4 +1,3 @@
-target-heap-size
retention-days
rule-files
storage-path
diff --git a/cdist/conf/type/__prometheus_server/parameter/required b/cdist/conf/type/__prometheus_server/parameter/required
index 49abf924..04204c7c 100644
--- a/cdist/conf/type/__prometheus_server/parameter/required
+++ b/cdist/conf/type/__prometheus_server/parameter/required
@@ -1,3 +1 @@
-alertmanager-url
config
-listen-address
diff --git a/cdist/conf/type/__pyvenv/explorer/group b/cdist/conf/type/__pyvenv/explorer/group
index ff072c5e..a655bda7 100755
--- a/cdist/conf/type/__pyvenv/explorer/group
+++ b/cdist/conf/type/__pyvenv/explorer/group
@@ -2,4 +2,4 @@
destination="/$__object_id"
-stat --print "%G" ${destination} 2>/dev/null || exit 0
+stat --print "%G" "${destination}" 2>/dev/null || exit 0
diff --git a/cdist/conf/type/__pyvenv/explorer/owner b/cdist/conf/type/__pyvenv/explorer/owner
index b77e3c6e..8b3c7f8e 100755
--- a/cdist/conf/type/__pyvenv/explorer/owner
+++ b/cdist/conf/type/__pyvenv/explorer/owner
@@ -2,4 +2,4 @@
destination="/$__object_id"
-stat --print "%U" ${destination} 2>/dev/null || exit 0
+stat --print "%U" "${destination}" 2>/dev/null || exit 0
diff --git a/cdist/conf/type/__pyvenv/gencode-remote b/cdist/conf/type/__pyvenv/gencode-remote
index a4f078c5..c5b64eff 100755
--- a/cdist/conf/type/__pyvenv/gencode-remote
+++ b/cdist/conf/type/__pyvenv/gencode-remote
@@ -1,6 +1,7 @@
#!/bin/sh -e
#
# 2016 Darko Poljak (darko.poljak at gmail.com)
+# 2020 Nico Schotetlius (nico.schottelius at ungleich.ch)
#
# This file is part of cdist.
#
@@ -29,28 +30,38 @@ owner="$(cat "$__object/parameter/owner")"
group="$(cat "$__object/parameter/group")"
mode="$(cat "$__object/parameter/mode")"
-[ "$state_should" = "$state_is" -a \
- "$owner" = "$owner_is" -a \
- "$group" = "$group_is" -a \
- -n "$mode" ] && exit 0
+[ "$state_should" = "$state_is" ] && \
+[ "$owner" = "$owner_is" ] && \
+[ "$group" = "$group_is" ] && \
+[ -n "$mode" ] && exit 0
destination="/$__object_id"
venvparams="$(cat "$__object/parameter/venvparams")"
pyvenvparam="$__object/parameter/pyvenv"
+
+os=$(cat "$__global/explorer/os")
+
if [ -f "$pyvenvparam" ]
then
pyvenv=$(cat "$pyvenvparam")
else
- pyvenv="pyvenv"
+ case "$os" in
+ alpine|ubuntu) # no pyvenv on alpine - I assume others will follow
+ pyvenv="python3 -m venv"
+ ;;
+ *)
+ pyvenv="pyvenv"
+ ;;
+ esac
fi
case $state_should in
present)
if [ "$state_should" != "$state_is" ]; then
- echo $pyvenv $venvparams "$destination"
+ echo "$pyvenv $venvparams $destination"
fi
- if [ \( -n "$owner" -a "$owner_is" != "$owner" \) -o \
- \( -n "$group" -a "$group_is" != "$group" \) ]; then
+ if { [ -n "$owner" ] && [ "$owner_is" != "$owner" ]; } || \
+ { [ -n "$group" ] && [ "$group_is" != "$group" ]; }; then
echo chown -R "${owner}:${group}" "$destination"
fi
if [ -n "$mode" ]; then
diff --git a/cdist/conf/type/__pyvenv/man.rst b/cdist/conf/type/__pyvenv/man.rst
index d7de92fa..e2e4a1e6 100644
--- a/cdist/conf/type/__pyvenv/man.rst
+++ b/cdist/conf/type/__pyvenv/man.rst
@@ -9,7 +9,7 @@ cdist-type__pyvenv - Create or remove python virtual environment
DESCRIPTION
-----------
This cdist type allows you to create or remove python virtual
-environment using pyvenv.
+environment using pyvenv on python3 -m venv.
It assumes pyvenv is already installed. Concrete package depends
on concrete OS and/or OS version/distribution.
Ensure this for e.g. in your init manifest as in the following example:
@@ -57,11 +57,11 @@ EXAMPLES
__pyvenv /home/services/djangoenv
- # Use specific pyvenv
+ # Use specific pyvenv
__pyvenv /home/foo/fooenv --pyvenv /usr/local/bin/pyvenv-3.4
# Create python virtualenv for user foo.
- __pyvenv /home/foo/fooenv --group foo --user foo
+ __pyvenv /home/foo/fooenv --group foo --owner foo
# Create python virtualenv with specific parameters.
__pyvenv /home/services/djangoenv --venvparams "--copies --system-site-packages"
@@ -76,4 +76,3 @@ COPYING
-------
Copyright \(C) 2016 Darko Poljak. Free use of this software is
granted under the terms of the GNU General Public License v3 or later (GPLv3+).
-
diff --git a/cdist/conf/type/__qemu_img/gencode-remote b/cdist/conf/type/__qemu_img/gencode-remote
index 9127e5ef..94816f58 100755
--- a/cdist/conf/type/__qemu_img/gencode-remote
+++ b/cdist/conf/type/__qemu_img/gencode-remote
@@ -18,4 +18,4 @@ format="$(cat "$__object/parameter/format")"
size="$(cat "$__object/parameter/size")"
diskimage="/$__object_id"
-echo qemu-img create -f \"$format\" \"$diskimage\" \"$size\"
+echo "qemu-img create -f '$format' '$diskimage' '$size'"
diff --git a/cdist/conf/type/__qemu_img/manifest b/cdist/conf/type/__qemu_img/manifest
index e7417389..55f3bf16 100755
--- a/cdist/conf/type/__qemu_img/manifest
+++ b/cdist/conf/type/__qemu_img/manifest
@@ -4,7 +4,6 @@
# Default settings
#
-format="$(cat "$__object/parameter/format")"
state_should="$(cat "$__object/parameter/state")"
diskimage="/$__object_id"
diff --git a/cdist/conf/type/__rsync/gencode-local b/cdist/conf/type/__rsync/gencode-local
index 155f3a3a..e36ded2f 100755
--- a/cdist/conf/type/__rsync/gencode-local
+++ b/cdist/conf/type/__rsync/gencode-local
@@ -29,9 +29,9 @@ fi
set --
if [ -f "$__object/parameter/rsync-opts" ]; then
- while read opts; do
+ while read -r opts; do
set -- "$@" "--$opts"
- done < $__object/parameter/rsync-opts
+ done < "$__object/parameter/rsync-opts"
fi
echo rsync -a \
diff --git a/cdist/conf/type/__rvm/explorer/state b/cdist/conf/type/__rvm/explorer/state
index f43f5509..74d17048 100755
--- a/cdist/conf/type/__rvm/explorer/state
+++ b/cdist/conf/type/__rvm/explorer/state
@@ -28,7 +28,7 @@ if [ "$user" = "root" ]; then
echo absent
fi
else
- if su - $user -c "[ -d \"\$HOME/.rvm\" ]" ; then
+ if su - "$user" -c "[ -d \"\$HOME/.rvm\" ]" ; then
echo "present"
else
echo "absent"
diff --git a/cdist/conf/type/__rvm/gencode-remote b/cdist/conf/type/__rvm/gencode-remote
index 494c8fd8..993191c1 100755
--- a/cdist/conf/type/__rvm/gencode-remote
+++ b/cdist/conf/type/__rvm/gencode-remote
@@ -34,7 +34,7 @@ DONE
absent)
cat << DONE
su - $user -c "rm -Rf \"\\\$HOME/.rvm\";
-sed '/rvm\/scripts\/rvm/d' \"\\\$HOME/.bashrc\" > \"\\\$HOME/.bashrc.cdist-tmp\"
+sed '/rvm\\/scripts\\/rvm/d' \"\\\$HOME/.bashrc\" > \"\\\$HOME/.bashrc.cdist-tmp\"
mv \"\\\$HOME/.bashrc.cdist-tmp\" \"\\\$HOME/.bashrc\""
DONE
;;
diff --git a/cdist/conf/type/__rvm_gem/gencode-remote b/cdist/conf/type/__rvm_gem/gencode-remote
index 1fe6e78e..9212de91 100755
--- a/cdist/conf/type/__rvm_gem/gencode-remote
+++ b/cdist/conf/type/__rvm_gem/gencode-remote
@@ -20,8 +20,6 @@
gem="$__object_id"
gemset="$(cat "$__object/parameter/gemset")"
-ruby="$(echo "$gemset" | cut -d '@' -f 1)"
-gemsetname="$(echo "$gemset" | cut -d '@' -f 2)"
state_is="$(cat "$__object/explorer/state")"
user="$(cat "$__object/parameter/user")"
state_should="$(cat "$__object/parameter/state")"
diff --git a/cdist/conf/type/__rvm_gemset/explorer/state b/cdist/conf/type/__rvm_gemset/explorer/state
index fa643a6e..e300453b 100755
--- a/cdist/conf/type/__rvm_gemset/explorer/state
+++ b/cdist/conf/type/__rvm_gemset/explorer/state
@@ -18,9 +18,6 @@
# along with cdist. If not, see .
#
-gemset="$__object_id"
-ruby="$(echo "$gemset" | cut -d '@' -f 1)"
-gemsetname="$(echo "$gemset" | cut -d '@' -f2)"
user="$(cat "$__object/parameter/user")"
if [ ! -e "~$user/.rvm/scripts/rvm" ] ; then
@@ -28,7 +25,9 @@ if [ ! -e "~$user/.rvm/scripts/rvm" ] ; then
exit 0
fi
+# shellcheck disable=SC2016
if su - "$user" -c 'source ~/.rvm/scripts/rvm; rvm list strings | grep -q "^$ruby\$"'; then
+ # shellcheck disable=SC2016
if su - "$user" -c 'source ~/.rvm/scripts/rvm; rvm use "$ruby" > /dev/null; rvm gemset list strings | cut -f 1 -d " " | grep -q "^$gemsetname\$"'; then
echo "present"
exit 0
diff --git a/cdist/conf/type/__rvm_gemset/gencode-remote b/cdist/conf/type/__rvm_gemset/gencode-remote
index 78851f9a..3cdc66a6 100755
--- a/cdist/conf/type/__rvm_gemset/gencode-remote
+++ b/cdist/conf/type/__rvm_gemset/gencode-remote
@@ -33,7 +33,7 @@ case "$state_should" in
cat << DONE
su - "$user" -c "source ~/.rvm/scripts/rvm; rvm $gemset --create"
DONE
- if -f "$__object/parameter/default"; then
+ if [ -f "$__object/parameter/default" ]; then
cat << DONE
su - "$user" -c "source ~/.rvm/scripts/rvm; rvm use --default $gemset"
DONE
diff --git a/cdist/conf/type/__rvm_ruby/gencode-remote b/cdist/conf/type/__rvm_ruby/gencode-remote
index 9bbc6031..f2fd41ef 100755
--- a/cdist/conf/type/__rvm_ruby/gencode-remote
+++ b/cdist/conf/type/__rvm_ruby/gencode-remote
@@ -21,7 +21,6 @@
ruby="$__object_id"
state_is="$(cat "$__object/explorer/state")"
user="$(cat "$__object/parameter/user")"
-default="$(cat "$__object/parameter/default" 2>/dev/null || true)"
state_should="$(cat "$__object/parameter/state")"
[ "$state_is" = "$state_should" ] && exit 0
diff --git a/cdist/conf/type/__sensible_editor/explorer/editor_path b/cdist/conf/type/__sensible_editor/explorer/editor_path
new file mode 100644
index 00000000..dcf63c9b
--- /dev/null
+++ b/cdist/conf/type/__sensible_editor/explorer/editor_path
@@ -0,0 +1,131 @@
+#!/bin/sh -e
+#
+# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+#
+# Check if the given editor is present on the target system and determine its
+# absolute path.
+#
+
+die() {
+ echo "$@" >&2
+ exit 1
+}
+
+editor_missing() { die "Editor '$1' is missing on the target system."; }
+editor_no_alternative() {
+ die "Editor '$1' is not in the alternatives list of the target system." \
+ "$(test -n "${editors}" && printf '\nPlease choose one of:\n\n%s\n' "${editors}")"
+}
+
+# No need to check for the path if the file is supposed to be removed.
+test "$(cat "${__object}/parameter/state")" != 'absent' || exit 0
+
+
+case $("${__explorer}/os")
+in
+ debian|devuan|ubuntu)
+ has_alternatives=true
+
+ # NOTE: Old versions do not support `--list`, in this case ignore the errors.
+ # This will require an absolute path to be provided, though.
+ editors=$(update-alternatives --list editor 2>/dev/null)
+ ;;
+ *)
+ # NOTE: RedHat has an alternatives system but it doesn't usually track
+ # editors and it is a pain to extract the list.
+ has_alternatives=false
+ ;;
+esac
+
+# Read --editor parameter and check its value since it is "optional"
+editor=$(cat "${__object}/parameter/editor" 2>/dev/null) || true
+test -n "${editor}" || die 'Please provide an --editor to configure.'
+
+case $editor
+in
+ /*)
+ is_abspath=true
+ ;;
+ */*)
+ die 'Relative editor paths are not supported'
+ ;;
+ *)
+ is_abspath=false
+ ;;
+esac
+
+
+if $has_alternatives && test -n "${editors}"
+then
+ IFS='
+'
+ if ! $is_abspath
+ then
+ # First, try to resolve the absolute path using $editors.
+ while true
+ do
+ for e in $editors
+ do
+ if test "$(basename "${e}")" = "${editor}"
+ then
+ editor="${e}"
+ break 2 # break out of both loops
+ fi
+ done
+
+ # Iterating through alternatives did not yield a result
+ editor_no_alternative "${editor}"
+ break
+ done
+ fi
+
+ # Check if editor is present
+ test -f "${editor}" || editor_missing "${editor}"
+
+ for e in $editors
+ do
+ if test "${editor}" = "${e}"
+ then
+ # Editor is part of the alternatives list -> use it!
+ echo "${editor}"
+ exit 0
+ fi
+ done
+
+ editor_no_alternative "${editor}"
+else
+ # NOTE: This branch is mostly for RedHat-based systems which do
+ # not track editor alternatives. To make this type useful
+ # on RedHat at all we allow an absoloute path to be provided
+ # in any case.
+
+ if $is_abspath
+ then
+ test -x "${editor}" || editor_missing "${editor}"
+
+ echo "${editor}"
+ exit 0
+ else
+ die "The target doesn't list any editor alternatives. " \
+ "Please specify an absolute path or populate the alternatives list."
+ fi
+fi
+
+# The script should never reach this statement!
+exit 1
diff --git a/cdist/conf/type/__sensible_editor/explorer/group b/cdist/conf/type/__sensible_editor/explorer/group
new file mode 100644
index 00000000..5d288189
--- /dev/null
+++ b/cdist/conf/type/__sensible_editor/explorer/group
@@ -0,0 +1,26 @@
+#!/bin/sh -e
+#
+# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+#
+# Determines the primary group of the user.
+#
+
+user=$__object_id
+
+id -gn "${user}" 2>/dev/null
diff --git a/cdist/conf/type/__sensible_editor/explorer/user_home b/cdist/conf/type/__sensible_editor/explorer/user_home
new file mode 100644
index 00000000..b88243f7
--- /dev/null
+++ b/cdist/conf/type/__sensible_editor/explorer/user_home
@@ -0,0 +1,33 @@
+#!/bin/sh -e
+#
+# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+#
+# Determines the home folder of the target user.
+#
+
+user=$__object_id
+home=$(getent passwd "${user}" | cut -d':' -f6)
+
+if ! test -d "${home}"
+then
+ echo "Cannot find home directory of user ${user}" >&2
+ exit 1
+fi
+
+echo "${home}"
diff --git a/cdist/conf/type/__sensible_editor/man.rst b/cdist/conf/type/__sensible_editor/man.rst
new file mode 100644
index 00000000..9b805e06
--- /dev/null
+++ b/cdist/conf/type/__sensible_editor/man.rst
@@ -0,0 +1,78 @@
+cdist-type__sensible_editor(7)
+==============================
+
+NAME
+----
+cdist-type__sensible_editor - Select the sensible-editor
+
+
+DESCRIPTION
+-----------
+This cdist type allows you to select the :strong:`sensible-editor` for
+a given user.
+
+
+REQUIRED PARAMETERS
+-------------------
+editor
+ Name or path of the editor to be selected.
+ On systems other than Debian derivatives an absolute path is required.
+
+ It is permissible to omit this parameter if --state is absent.
+
+
+OPTIONAL PARAMETERS
+-------------------
+state
+ 'present', 'absent', or 'exists'. Defaults to 'present', where:
+
+ present
+ the sensible-editor is exactly what is specified in --editor.
+ absent
+ no sensible-editor configuration is present.
+ exists
+ the sensible-editor will be set to what is specified in --editor,
+ unless there already is a configuration on the target system.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ __sensible_editor root --editor /bin/ed # ed(1) is the standard
+ __sensible_editor noob --editor nano
+
+
+LIMITATIONS
+-----------
+
+This type depends upon the :strong:`sensible-editor`\ (1) script which
+is part of the sensible-utils package.
+
+Therefore, the following operating systems are supported:
+ * Debian 8 (jessie) or later
+ * Devuan
+ * Ubuntu 8.10 (intrepid) or later
+ * RHEL/CentOS 7 or later (EPEL repo required)
+ * Fedora 21 or later
+
+Note: on old versions of Ubuntu the sensible-* utils are part of the
+debianutils package.
+
+SEE ALSO
+--------
+:strong:`select-editor`\ (1), :strong:`sensible-editor`\ (1).
+
+
+AUTHOR
+-------
+Dennis Camera
+
+
+COPYING
+-------
+Copyright \(C) 2019 Dennis Camera.
+You can redistribute it and/or modify it under the terms of the GNU General
+Public License as published by the Free Software Foundation, either version 3 of
+the License, or (at your option) any later version.
diff --git a/cdist/conf/type/__sensible_editor/manifest b/cdist/conf/type/__sensible_editor/manifest
new file mode 100644
index 00000000..1cdb0c2c
--- /dev/null
+++ b/cdist/conf/type/__sensible_editor/manifest
@@ -0,0 +1,94 @@
+#!/bin/sh -e
+# -*- mode: sh; indent-tabs-mode: t -*-
+#
+# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+version_ge() {
+ awk -F '[^0-9.]' -v target="${1:?}" '
+ function max(x, y) { return x > y ? x : y; }
+ BEGIN {
+ getline;
+ nx = split($1, x, ".");
+ ny = split(target, y, ".");
+ for (i = 1; i <= max(nx, ny); ++i) {
+ diff = int(x[i]) - int(y[i]);
+ if (diff < 0) exit 1;
+ else if (diff > 0) exit 0;
+ else continue;
+ }
+ }'
+}
+
+not_supported() {
+ echo "OS ${os} does not support __sensible_editor." >&2
+ echo 'If it does, please provide a patch.' >&2
+ exit 1
+}
+
+os=$(cat "${__global}/explorer/os")
+os_version=$(cat "${__global}/explorer/os_version")
+
+state=$(cat "${__object}/parameter/state")
+user=$__object_id
+
+if test "${state}" != 'present' && test "${state}" != 'exists' && test "${state}" != 'absent'
+then
+ echo 'Only "present", "exists", and "absent" are allowed for --state' >&2
+ exit 1
+fi
+
+package_name='sensible-utils'
+
+case $os
+in
+ debian)
+ pkg_type='apt'
+ ;;
+ devuan)
+ pkg_type='apt'
+ ;;
+ ubuntu)
+ (echo "${os_version}" | version_ge 10.04) || package_name='debianutils'
+ pkg_type='apt'
+ ;;
+ centos|fedora|redhat|scientific)
+ pkg_type='yum'
+ ;;
+ *)
+ not_supported
+ ;;
+esac
+
+if test "${state}" != 'absent'
+then
+ __package "${package_name}" --state present \
+ --type "${pkg_type}"
+ export require="__package/${package_name}"
+fi
+
+editor_path=$(cat "${__object}/explorer/editor_path")
+user_home=$(cat "${__object}/explorer/user_home")
+group=$(cat "${__object}/explorer/group")
+
+__file "${user_home}/.selected_editor" --state "${state}" \
+ --owner "${user}" --group "${group}" --mode 0644 \
+ --source - <
+
+
+COPYING
+-------
+Copyright \(C) 2019 Timothée Floure. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__service/manifest b/cdist/conf/type/__service/manifest
new file mode 100644
index 00000000..beb0713c
--- /dev/null
+++ b/cdist/conf/type/__service/manifest
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+manager="$(cat "$__object/explorer/service-manager")"
+
+name=$__object_id
+action="$(cat "$__object/parameter/action")"
+
+case "$manager" in
+ systemd)
+ test "$action" = "start" && action="running"
+ test "$action" = "stop" && action="stopped"
+ __systemd_service "$name" --state "$action"
+ ;;
+ *)
+ # Unknown: handled by `service $NAME $action` in gencode-remote.
+ ;;
+esac
diff --git a/cdist/conf/type/__service/parameter/required b/cdist/conf/type/__service/parameter/required
new file mode 100644
index 00000000..a9f84d41
--- /dev/null
+++ b/cdist/conf/type/__service/parameter/required
@@ -0,0 +1 @@
+action
diff --git a/cdist/conf/type/__ssh_authorized_key/explorer/entry b/cdist/conf/type/__ssh_authorized_key/explorer/entry
index 1535d348..aca0f2b9 100755
--- a/cdist/conf/type/__ssh_authorized_key/explorer/entry
+++ b/cdist/conf/type/__ssh_authorized_key/explorer/entry
@@ -19,13 +19,17 @@
#
# extract the keytype and base64 encoded key ignoring any options and comment
-type_and_key="$(cat "$__object/parameter/key" | tr ' ' '\n' | awk '/^(ssh|ecdsa)-[^ ]+/ { printf $1" "; getline; printf $1 }')"
+type_and_key="$(tr ' ' '\n' < "$__object/parameter/key"| awk '/^(ssh|ecdsa)-[^ ]+/ { printf $1" "; getline; printf $1 }')"
# If type_and_key is empty, which is the case with an invalid key, do not grep $file because it results
# in greping everything in file and all entries from file are removed.
if [ -n "${type_and_key}" ]
then
- file="$(cat $__object/parameter/file)"
+ file="$(cat "$__object/parameter/file")"
+ test -e "$file" || exit 0
# get any entries that match the type and key
- grep ".*$type_and_key\([ \n]\|$\)" "$file" || true
+
+ # NOTE: Do not match from the beginning of the line as there may be options
+ # preceeding the key.
+ grep "${type_and_key}\\([ \\n].*\\)*$" "$file" || true
fi
diff --git a/cdist/conf/type/__ssh_authorized_key/gencode-remote b/cdist/conf/type/__ssh_authorized_key/gencode-remote
index 7ded7dc6..61c77fb9 100755
--- a/cdist/conf/type/__ssh_authorized_key/gencode-remote
+++ b/cdist/conf/type/__ssh_authorized_key/gencode-remote
@@ -37,9 +37,9 @@ tmpfile=\$(mktemp ${file}.cdist.XXXXXXXXXX)
# preserve ownership and permissions of existing file
if [ -f "$file" ]; then
cp -p "$file" "\$tmpfile"
+ grep -v -F -x '$line' '$file' >\$tmpfile
fi
-grep -v -F -x '$line' '$file' > \$tmpfile || true
-mv -f "\$tmpfile" "$file"
+cat "\$tmpfile" >"$file"
DONE
}
@@ -48,7 +48,7 @@ add_line() {
line="$2"
# escape single quotes
line_sanitised=$(echo "$line" | sed -e "s/'/'\"'\"'/g")
- printf '%s' "printf '%s\n' '$line_sanitised' >> $file"
+ printf '%s' "printf '%s\\n' '$line_sanitised' >> $file"
}
@@ -59,7 +59,7 @@ mkdir "$__object/files"
(
if [ -f "$__object/parameter/option" ]; then
# comma seperated list of options
- options="$(cat "$__object/parameter/option" | tr '\n' ',')"
+ options="$(tr '\n' ',' < "$__object/parameter/option")"
printf '%s ' "${options%*,}"
fi
if [ -f "$__object/parameter/comment" ]; then
@@ -78,7 +78,7 @@ if [ -s "$__object/explorer/entry" ]; then
# Note that the files have to be sorted for comparison with `comm`.
sort "$__object/explorer/entry" > "$__object/files/is"
comm -13 "$__object/files/should" "$__object/files/is" | {
- while read entry; do
+ while read -r entry; do
remove_line "$file" "$entry"
done
}
@@ -88,7 +88,7 @@ fi
entry="$(cat "$__object/files/should")"
state_should="$(cat "$__object/parameter/state")"
num_existing_entries=$(grep -c -F -x "$entry" "$__object/explorer/entry" || true)
-if [ $num_existing_entries -eq 1 ]; then
+if [ "$num_existing_entries" -eq 1 ]; then
state_is="present"
else
# Posix grep does not define the -m option, so we can not remove a single
diff --git a/cdist/conf/type/__ssh_authorized_key/man.rst b/cdist/conf/type/__ssh_authorized_key/man.rst
index 087a3dae..5bae02aa 100644
--- a/cdist/conf/type/__ssh_authorized_key/man.rst
+++ b/cdist/conf/type/__ssh_authorized_key/man.rst
@@ -15,25 +15,27 @@ This type was created to be used by the __ssh_authorized_keys type.
REQUIRED PARAMETERS
-------------------
file
- the authorized_keys file to which the given key should be added
+ The authorized_keys file where the given key should be managed.
key
- a string containing the ssh keytype, base 64 encoded key and optional
- trailing comment which shall be added to the given authorized_keys file.
+ The ssh key which shall be managed in this authorized_keys file.
+ Must be a string containing the ssh keytype, base 64 encoded key and
+ optional trailing comment which shall be added to the given
+ authorized_keys file.
OPTIONAL PARAMETERS
-------------------
comment
- explicit comment instead of the one which may be trailing the given key
+ Use this comment instead of the one which may be trailing in the key.
option
- an option to set for this authorized_key entry.
+ An option to set for this authorized_key entry.
Can be specified multiple times.
See sshd(8) for available options.
state
- if the given keys should be 'present' or 'absent', defaults to 'present'.
+ If the managed key should be 'present' or 'absent', defaults to 'present'.
MESSAGES
@@ -64,7 +66,7 @@ EXAMPLES
SEE ALSO
--------
-:strong:`cdist__ssh_authorized_keys`\ (7), :strong:`sshd`\ (8)
+:strong:`cdist-type__ssh_authorized_keys`\ (7), :strong:`sshd`\ (8)
AUTHORS
diff --git a/cdist/conf/type/__ssh_authorized_keys/explorer/file b/cdist/conf/type/__ssh_authorized_keys/explorer/file
index 5a02721a..017bcb38 100755
--- a/cdist/conf/type/__ssh_authorized_keys/explorer/file
+++ b/cdist/conf/type/__ssh_authorized_keys/explorer/file
@@ -1,6 +1,7 @@
#!/bin/sh
#
# 2014 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -19,9 +20,42 @@
#
if [ -f "$__object/parameter/file" ]; then
- cat "$__object/parameter/file"
+ cat "$__object/parameter/file"
else
- owner="$(cat "$__object/parameter/owner" 2>/dev/null || echo "$__object_id")"
- home=$(getent passwd "$owner" | cut -d':' -f 6)
- echo "$home/.ssh/authorized_keys"
+ if [ -s "$__object/parameter/owner" ]
+ then
+ owner=$(cat "$__object/parameter/owner")
+ else
+ owner="$__object_id"
+ fi
+
+ if command -v getent >/dev/null
+ then
+ owner_line=$(getent passwd "$owner")
+ elif [ -f /etc/passwd ]
+ then
+ case $owner
+ in
+ [0-9][0-9]*)
+ owner_line=$(awk -F: "\$3 == \"${owner}\" { print }" /etc/passwd)
+ ;;
+ *)
+ owner_line=$(awk -F: "\$1 == \"${owner}\" { print }" /etc/passwd)
+ ;;
+ esac
+ fi
+
+ if [ "$owner_line" ]
+ then
+ home=$(echo "$owner_line" | cut -d':' -f6)
+ fi
+
+ if [ ! -d "$home" ]
+ then
+ # Don't know how to determine user's home directory, fall back to ~
+ home="~$owner"
+ command -v realpath >/dev/null && home=$(realpath "$home")
+ fi
+
+ [ -d "$home" ] && echo "$home/.ssh/authorized_keys"
fi
diff --git a/cdist/conf/type/__ssh_authorized_keys/explorer/group b/cdist/conf/type/__ssh_authorized_keys/explorer/group
index 72a4e314..d259050f 100755
--- a/cdist/conf/type/__ssh_authorized_keys/explorer/group
+++ b/cdist/conf/type/__ssh_authorized_keys/explorer/group
@@ -1,6 +1,7 @@
#!/bin/sh
#
# 2014 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -18,6 +19,28 @@
# along with cdist. If not, see .
#
-owner="$(cat "$__object/parameter/owner" 2>/dev/null || echo "$__object_id")"
-gid="$(getent passwd "$owner" | cut -d':' -f 4)"
-getent group "$gid" || true
+if [ -s "$__object/parameter/owner" ]
+then
+ owner=$(cat "$__object/parameter/owner")
+else
+ owner="$__object_id"
+fi
+
+if command -v getent >/dev/null
+then
+ gid=$(getent passwd "$owner" | cut -d':' -f4)
+ getent group "$gid" || true
+else
+ # Fallback to local file scanning
+ case $owner
+ in
+ [0-9][0-9]*)
+ gid=$(awk -F: "\$3 == \"${owner}\" { print \$4 }" /etc/passwd)
+ ;;
+ *)
+ gid=$(awk -F: "\$1 == \"${owner}\" { print \$4 }" /etc/passwd)
+ ;;
+ esac
+
+ awk -F: "\$3 == \"$gid\" { print }" /etc/group
+fi
diff --git a/cdist/conf/type/__ssh_authorized_keys/explorer/keys b/cdist/conf/type/__ssh_authorized_keys/explorer/keys
new file mode 100755
index 00000000..cec25746
--- /dev/null
+++ b/cdist/conf/type/__ssh_authorized_keys/explorer/keys
@@ -0,0 +1,9 @@
+#!/bin/sh -e
+
+# shellcheck disable=SC1090
+file="$( . "$__type_explorer/file" )"
+
+if [ -f "$file" ]
+then
+ cat "$file"
+fi
diff --git a/cdist/conf/type/__ssh_authorized_keys/man.rst b/cdist/conf/type/__ssh_authorized_keys/man.rst
index ba310ff9..dac6adeb 100644
--- a/cdist/conf/type/__ssh_authorized_keys/man.rst
+++ b/cdist/conf/type/__ssh_authorized_keys/man.rst
@@ -20,42 +20,48 @@ then left to the user to ensure that the file exists and that ownership and
permissions work with ssh.
-REQUIRED PARAMETERS
--------------------
+REQUIRED MULTIPLE PARAMETERS
+----------------------------
key
- the ssh key which shall be added to this authorized_keys file.
- Must be a string and can be specified multiple times.
+ An ssh key which shall be managed in this authorized_keys file.
+ Must be a string containing the ssh keytype, base 64 encoded key and
+ optional trailing comment which shall be added to the given
+ authorized_keys file.
+ Can be specified multiple times.
OPTIONAL PARAMETERS
-------------------
comment
- explicit comment instead of the one which may be trailing the given key
+ Use this comment instead of the one which may be trailing in each key.
file
- an alternative destination file, defaults to ~$owner/.ssh/authorized_keys
+ An alternative destination file, defaults to ~$owner/.ssh/authorized_keys.
option
- an option to set for all created authorized_key entries.
+ An option to set for all authorized_key entries in the key parameter.
Can be specified multiple times.
See sshd(8) for available options.
owner
- the user owning the authorized_keys file, defaults to object_id.
+ The user owning the authorized_keys file, defaults to object_id.
state
- if the given keys should be 'present' or 'absent', defaults to 'present'.
+ If the given keys should be 'present' or 'absent', defaults to 'present'.
BOOLEAN PARAMETERS
------------------
noparent
- don't create or change ownership and permissions of the directory containing
- the authorized_keys file
+ Don't create or change ownership and permissions of the directory containing
+ the authorized_keys file.
nofile
- don't manage existence, ownership and permissions of the the authorized_keys
- file
+ Don't manage existence, ownership and permissions of the the authorized_keys
+ file.
+
+remove-unknown
+ Remove undefined keys.
EXAMPLES
@@ -67,6 +73,12 @@ EXAMPLES
__ssh_authorized_keys root \
--key "$(cat ~/.ssh/id_rsa.pub)"
+ # same as above, but make sure your key is only key in
+ # root's authorized_keys file
+ __ssh_authorized_keys root \
+ --key "$(cat ~/.ssh/id_rsa.pub)" \
+ --remove-unknown
+
# allow key to login as user-name
__ssh_authorized_keys user-name \
--key "ssh-rsa AXYZAAB3NzaC1yc2..."
diff --git a/cdist/conf/type/__ssh_authorized_keys/manifest b/cdist/conf/type/__ssh_authorized_keys/manifest
index 9fad8896..b319316b 100755
--- a/cdist/conf/type/__ssh_authorized_keys/manifest
+++ b/cdist/conf/type/__ssh_authorized_keys/manifest
@@ -23,7 +23,13 @@ owner="$(cat "$__object/parameter/owner" 2>/dev/null || echo "$__object_id")"
state="$(cat "$__object/parameter/state" 2>/dev/null)"
file="$(cat "$__object/explorer/file")"
-if [ ! -f "$__object/parameter/noparent" -o ! -f "$__object/parameter/nofile" ]; then
+if [ ! -f "$__object/parameter/nofile" ] && [ -z "$file" ]
+then
+ echo "Cannot determine path of authorized_keys file" >&2
+ exit 1
+fi
+
+if [ ! -f "$__object/parameter/noparent" ] || [ ! -f "$__object/parameter/nofile" ]; then
group="$(cut -d':' -f 1 "$__object/explorer/group")"
if [ -z "$group" ]; then
echo "Failed to get owners group from explorer." >&2
@@ -45,31 +51,24 @@ if [ ! -f "$__object/parameter/noparent" -o ! -f "$__object/parameter/nofile" ];
fi
fi
-# Remove legacy blocks created by old versions of this type
-# FIXME: remove me in 3.2+
-__block "$__object_name" \
- --file "$file" \
- --prefix "#cdist:$__object_name" \
- --suffix "#/cdist:$__object_name" \
- --state 'absent' \
- --text - << DONE
-remove legacy block
-DONE
-export require="__block/$__object_name"
-
_cksum() {
echo "$1" | cksum | cut -d' ' -f 1
}
-while read key; do
- type_and_key="$(echo "$key" | tr ' ' '\n' | awk '/^(ssh|ecdsa)-[^ ]+/ { printf $1" "; getline; printf $1 }')"
+_type_and_key() {
+ echo "$1" | tr ' ' '\n' | awk '/^(ssh|ecdsa)-[^ ]+/ { printf $1" "; getline; printf $1 }'
+}
+
+while read -r key; do
+ type_and_key="$( _type_and_key "$key" )"
object_id="$(_cksum "$file")-$(_cksum "$type_and_key")"
set -- "$object_id"
set -- "$@" --file "$file"
set -- "$@" --key "$key"
set -- "$@" --state "$state"
if [ -f "$__object/parameter/option" ]; then
- set -- "$@" --option "$(cat "$__object/parameter/option")"
+ # shellcheck disable=SC2046
+ set -- "$@" $(printf -- '--option %s ' $(cat "$__object/parameter/option"))
fi
if [ -f "$__object/parameter/comment" ]; then
set -- "$@" --comment "$(cat "$__object/parameter/comment")"
@@ -77,3 +76,24 @@ while read key; do
# Ensure __ssh_authorized_key does not read stdin
__ssh_authorized_key "$@" < /dev/null
done < "$__object/parameter/key"
+
+if [ -f "$__object/parameter/remove-unknown" ] &&
+ [ -s "$__object/explorer/keys" ]
+then
+ while read -r key
+ do
+ type_and_key="$( _type_and_key "$key" )"
+
+ if grep -Fq "$type_and_key" "$__object/parameter/key"
+ then
+ continue
+ fi
+
+ __ssh_authorized_key "remove-$( _cksum "$file$key" )" \
+ --file "$file" \
+ --key "$key" \
+ --state absent \
+ < /dev/null
+ done \
+ < "$__object/explorer/keys"
+fi
diff --git a/cdist/conf/type/__ssh_authorized_keys/parameter/boolean b/cdist/conf/type/__ssh_authorized_keys/parameter/boolean
index 4bb126fe..7388fed5 100644
--- a/cdist/conf/type/__ssh_authorized_keys/parameter/boolean
+++ b/cdist/conf/type/__ssh_authorized_keys/parameter/boolean
@@ -1,2 +1,3 @@
noparent
nofile
+remove-unknown
diff --git a/cdist/conf/type/__ssh_authorized_keys/parameter/optional b/cdist/conf/type/__ssh_authorized_keys/parameter/optional
index 21f9bc29..fa64fc43 100644
--- a/cdist/conf/type/__ssh_authorized_keys/parameter/optional
+++ b/cdist/conf/type/__ssh_authorized_keys/parameter/optional
@@ -1,5 +1,4 @@
comment
file
-option
owner
state
diff --git a/cdist/conf/type/__ssh_authorized_keys/parameter/optional_multiple b/cdist/conf/type/__ssh_authorized_keys/parameter/optional_multiple
new file mode 100644
index 00000000..01925a15
--- /dev/null
+++ b/cdist/conf/type/__ssh_authorized_keys/parameter/optional_multiple
@@ -0,0 +1 @@
+option
diff --git a/cdist/conf/type/__ssh_dot_ssh/explorer/group b/cdist/conf/type/__ssh_dot_ssh/explorer/group
index cdea6fe7..faf44cb8 100755
--- a/cdist/conf/type/__ssh_dot_ssh/explorer/group
+++ b/cdist/conf/type/__ssh_dot_ssh/explorer/group
@@ -1,6 +1,7 @@
#!/bin/sh
#
# 2014 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -18,5 +19,11 @@
# along with cdist. If not, see .
#
-gid="$("$__type_explorer/passwd" | cut -d':' -f 4)"
-getent group "$gid" || true
+gid=$("$__type_explorer/passwd" | cut -d':' -f4)
+
+if command -v getent >/dev/null
+then
+ getent group "$gid" || true
+else
+ awk -F: "\$3 == \"$gid\" { print }" /etc/group
+fi
diff --git a/cdist/conf/type/__ssh_dot_ssh/explorer/passwd b/cdist/conf/type/__ssh_dot_ssh/explorer/passwd
index 3fbad06f..42686b20 100755
--- a/cdist/conf/type/__ssh_dot_ssh/explorer/passwd
+++ b/cdist/conf/type/__ssh_dot_ssh/explorer/passwd
@@ -2,6 +2,7 @@
#
# 2012 Steven Armstrong (steven-cdist at armstrong.cc)
# 2014 Nico Schottelius (nico-cdist at schottelius.org)
+# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -21,4 +22,16 @@
owner="$__object_id"
-getent passwd "$owner" || true
+if command -v getent >/dev/null
+then
+ getent passwd "$owner" || true
+else
+ case $owner in
+ [0-9][0-9]*)
+ awk -F: "\$3 == \"$owner\" { print }" /etc/passwd
+ ;;
+ *)
+ grep "^$owner:" /etc/passwd || true
+ ;;
+ esac
+fi
diff --git a/cdist/conf/type/__sshd_config/explorer/state b/cdist/conf/type/__sshd_config/explorer/state
new file mode 100644
index 00000000..75c68b8a
--- /dev/null
+++ b/cdist/conf/type/__sshd_config/explorer/state
@@ -0,0 +1,121 @@
+#!/bin/sh -e
+#
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+# Determines the current state of the config option.
+# Possible output:
+# - present: "should" option present in config file
+# - default: the "should" option is the default -> don’t know if present
+# - absent: no such option present in config file
+#
+
+joinlines() { sed -n -e H -e "\${x;s/^\\n//;s/\\n/${1:?}/g;p;}"; }
+trlower() { tr '[:upper:]' '[:lower:]'; }
+tolower() { printf '%s' "$*" | trlower; }
+
+default_value() {
+ sshd -T -f /dev/null -C "$(make_conn_spec)" \
+ | sed -n -e 's/^'"$(tolower "${1:?}")"'[[:blank:]]\{1,\}//p'
+}
+
+make_conn_spec() {
+ if test -s "${__object:?}/parameter/match"
+ then
+ _match_file="${__object:?}/parameter/match"
+ else
+ _match_file='/dev/null'
+ fi
+
+ for _kw in \
+ addr=Address \
+ user=User \
+ host=Host \
+ laddr=LocalAddress \
+ lport=LocalPort \
+ rdomain=RDomain
+ do
+ _specname=${_kw%%=*}
+ _confname=$(tolower "${_kw#*=}")
+ while read -r _k _v
+ do
+ if test "$(tolower "${_k}")" = "${_confname}"
+ then
+ printf '%s=%s\n' "${_specname}" "${_v}"
+ continue 2
+ fi
+ done <"${_match_file}"
+
+ # NOTE: Print test spec even for empty keys to suppress errors like:
+ # 'Match User' in configuration but 'user' not in connection test specification.
+ # except lport:
+ # Invalid port '' in test mode specification lport=
+ test "${_specname}" = 'lport' || printf '%s=\n' "${_specname}"
+ done \
+ | joinlines ','
+ unset _match_file
+}
+
+sshd_config_file=$(cat "${__object:?}/parameter/file")
+state_should=$(cat "${__object:?}/parameter/state")
+
+if test -s "${__object:?}/parameter/option"
+then
+ option_name=$(cat "${__object:?}/parameter/option")
+else
+ option_name=${__object_id:?}
+fi
+
+value_should=$(cat "${__object:?}/parameter/value" 2>/dev/null) \
+|| test "${state_should}" = absent || exit 0 # param optional if --state absent
+
+command -v sshd >/dev/null 2>&1 || {
+ echo 'Cannot find sshd.' >&2
+ exit 1
+}
+
+test -e "${sshd_config_file}" || {
+ echo 'absent'
+ exit 0
+}
+
+value_is=$(
+ sshd -T -f "${sshd_config_file}" -C "$(make_conn_spec)" \
+ | sed -n -e 's/^'"$(tolower "${option_name}")"'[[:blank:]]\{1,\}//p')
+
+if printf '%s\n' "${value_is}" | {
+ if test -n "${value_should}"
+ then
+ grep -q -x -F "${value_should}"
+ else
+ # if no value provided, assume "any" value
+ grep -q -e .
+ fi
+ }
+then
+ if default_value "${option_name}" | grep -q -x -F "${value_is}"
+ then
+ # Might produce false positives for default values.
+ # TODO: Manual checking should be done, but for simplicity, this case is
+ # currently ignored here.
+ echo default
+ else
+ echo present
+ fi
+else
+ echo absent
+fi
diff --git a/cdist/conf/type/__sshd_config/files/update_sshd_config.awk b/cdist/conf/type/__sshd_config/files/update_sshd_config.awk
new file mode 100644
index 00000000..f7f30e87
--- /dev/null
+++ b/cdist/conf/type/__sshd_config/files/update_sshd_config.awk
@@ -0,0 +1,293 @@
+# -*- mode: awk; indent-tabs-mode: t -*-
+
+function usage() {
+ print_err("Usage: awk -f update_sshd_config.awk -- -o set|unset [-m 'User git'] -l 'X11Forwarding no' /etc/ssh/sshd_config")
+}
+
+function print_err(s) { print s | "cat >&2" }
+
+function alength(a, i) {
+ for (i = 0; (i + 1) in a; ++i);
+ return i
+}
+
+function join(sep, a, i, s) {
+ for (i = i ? i : 1; i in a; i++)
+ s = s sep a[i]
+ return substr(s, 2)
+}
+
+function getopt(opts, argv, target, files, i, c, lv, idx, nf) {
+ # trivial getopt(3) implementation; only basic functionality
+ if (argv[1] == "--") i++
+ for (i += 1; i in argv; i++) {
+ if (lv) { target[c] = argv[i]; lv = 0; continue }
+ if (argv[i] ~ /^-/) {
+ c = substr(argv[i], 2, 1)
+ idx = index(opts, c)
+ if (!idx) {
+ print_err(sprintf("invalid option -%c\n", c))
+ continue
+ }
+ if (substr(opts, idx + 1, 1) == ":") {
+ # option takes argument
+ if (length(argv[i]) > 2)
+ target[c] = substr(argv[i], 3)
+ else
+ lv = 1
+ } else {
+ target[c] = 1
+ }
+ } else
+ files[++nf] = argv[i]
+ }
+}
+
+# tokenise configuration line
+# this function mimics the counterpart in OpenSSH (misc.c)
+# but it returns two (next token SUBSEP rest) because I didn’t want to have to
+# simulate any pointer magic.
+function strdelim_internal(s, split_equals, old) {
+ if (!s)
+ return ""
+
+ old = s
+
+ if (!match(s, WHITESPACE "|" QUOTE "" (split_equals ? "|" EQUALS : "")))
+ return s
+
+ s = substr(s, RSTART)
+ old = substr(old, 1, RSTART - 1)
+
+ if (s ~ "^" QUOTE) {
+ old = substr(old, 2)
+
+ # Find matching quote
+ if (match(s, QUOTE)) {
+ old = substr(old, 1, RSTART)
+ # s = substr()
+ if (match(s, "^" WHITESPACE "*"))
+ s = substr(s, RLENGTH)
+ return old
+ } else {
+ # no matching quote
+ return ""
+ }
+ }
+
+ if (match(s, "^" WHITESPACE "+")) {
+ sub("^" WHITESPACE "+", "", s)
+ if (split_equals)
+ sub(EQUALS WHITESPACE "*", "", s)
+ } else if (s ~ "^" EQUALS) {
+ s = substr(s, 2)
+ }
+
+ return old SUBSEP s
+}
+function strdelim(s) { return strdelim_internal(s, 1) }
+function strdelimw(s) { return strdelim_internal(s, 0) }
+
+function singleton_option(opt) {
+ return tolower(opt) !~ /^(acceptenv|allowgroups|allowusers|denygroups|denyusers|hostcertificate|hostkey|listenaddress|logverbose|permitlisten|permitopen|port|setenv|subsystem)$/
+}
+
+function print_update() {
+ if (mode) {
+ if (match_only) printf "\t"
+ printf "%s\n", line_should
+ updated = 1
+ }
+}
+
+BEGIN {
+ FS = "\n" # disable field splitting
+
+ WHITESPACE = "[ \t]" # servconf.c, misc.c:strdelim_internal (without line breaks, cf. bugs)
+ QUOTE = "[\"]" # misc.c:strdelim_internal
+ EQUALS = "[=]"
+
+ split("", opts)
+ split("", files)
+ getopt("ho:l:m:", ARGV, opts, files)
+
+ if (opts["h"]) { usage(); exit (e="0") }
+
+ line_should = opts["l"]
+ match_only = opts["m"]
+ num_files = alength(files)
+
+ if (num_files != 1 || !opts["o"] || !line_should) {
+ usage()
+ exit (e=126)
+ }
+
+ if (opts["o"] == "set") {
+ mode = 1
+ } else if (opts["o"] == "unset") {
+ mode = 0
+ } else {
+ print_err(sprintf("invalid mode %s\n", mode))
+ exit (e=1)
+ }
+
+ if (mode) {
+ # loop over sshd_config twice!
+ ARGV[2] = ARGV[1] = files[1]
+ ARGC = 3
+ } else {
+ # only loop once
+ ARGV[1] = files[1]
+ ARGC = 2
+ }
+
+ split(strdelim(line_should), should, SUBSEP)
+ option_should = tolower(should[1])
+ value_should = should[2]
+}
+
+{
+ line = $0
+
+ # Strip trailing whitespace. Allow \f (form feed) at EOL only
+ sub("(" WHITESPACE "|\f)*$", "", line)
+
+ # Strip leading whitespace
+ sub("^" WHITESPACE "*", "", line)
+
+ if (match(line, "^#" WHITESPACE "*")) {
+ prefix = substr(line, RSTART, RLENGTH)
+ line = substr(line, RSTART + RLENGTH)
+ } else {
+ prefix = ""
+ }
+
+ line_type = "invalid"
+ option_is = value_is = ""
+
+ if (line) {
+ split(strdelim(line), toks, SUBSEP)
+
+ if (tolower(toks[1]) == "match") {
+ MATCH = (prefix ~ /^#/ ? "#" : "") join(" ", toks, 2)
+ line_type = "match"
+ } else if (toks[1] ~ /^[A-Za-z][A-Za-z0-9]+$/) {
+ # This could be an option line
+ line_type = "option"
+ option_is = tolower(toks[1])
+ value_is = toks[2]
+ }
+ } else {
+ line_type = "empty"
+ }
+}
+
+# mode: unset
+
+!mode {
+ # delete matching config
+ if (prefix !~ /^#/)
+ if (MATCH == match_only && option_is == option_should)
+ if (!value_should || value_should == value_is)
+ next
+
+ print
+ next
+}
+
+
+# mode: set
+
+mode && NR == FNR {
+ if (line_type == "option") {
+ if (MATCH !~ /^#/) {
+ if (prefix ~ /^#/) {
+ # comment line
+ last_occ[MATCH, "#" option_is] = FNR
+ } else {
+ # option line
+ last_occ[MATCH, option_is] = FNR
+ }
+ last_occ[MATCH] = FNR
+ }
+ } else if (line_type == "invalid" && !prefix) {
+ # INVALID LINE
+ print_err(sprintf("%s: syntax error on line %u\n", ARGV[0], FNR))
+ }
+
+ next
+}
+
+# before second pass prepare hashes containing location information to be used
+# in the second pass.
+mode && NR > FNR && FNR == 1 {
+ # First we drop the locations of commented-out options if a non-commented
+ # option is available. If a non-commented option is available, we will
+ # append new config options there to have them all at one place.
+ for (k in last_occ) {
+ if (k ~ /^#/) {
+ # delete entries of commented out match blocks
+ delete last_occ[k]
+ continue
+ }
+
+ split(k, parts, SUBSEP)
+
+ if (parts[2] ~ /^#/ && ((parts[1], substr(parts[2], 2)) in last_occ))
+ delete last_occ[k]
+ }
+
+ # Reverse the option => line mapping. The line_map allows for easier lookups
+ # in the second pass.
+ # We only keep options, not top-level keywords, because we can only have
+ # one entry per line and there are conflicts with last lines of "sections".
+ for (k in last_occ) {
+ if (!index(k, SUBSEP)) continue
+ line_map[last_occ[k]] = k
+ }
+}
+
+# Second pass
+mode && line_map[FNR] == match_only SUBSEP option_should && !updated {
+ split(line_map[FNR], parts, SUBSEP)
+
+ # If option allows multiple values, print current value
+ if (!singleton_option(parts[2])) {
+ if (value_should != value_is)
+ print
+ }
+
+ print_update()
+
+ next
+}
+
+mode { print }
+
+# Is a comment option
+mode && line_map[FNR] == match_only SUBSEP "#" option_should && !updated {
+ print_update()
+}
+
+# Last line of the should match section
+mode && last_occ[match_only] == FNR && !updated {
+ # NOTE: Inserting empty lines is only cosmetic. It is only done if
+ # different options are next to each other and not in a match block
+ # (match blocks are usually not in the default config and thus don’t
+ # contain commented blocks.)
+ if (line && option_is != option_should && !MATCH)
+ print ""
+ print_update()
+}
+
+END {
+ if (e) exit e
+
+ if (mode && !updated) {
+ if (match_only && MATCH != match_only) {
+ printf "\nMatch %s\n", match_only
+ }
+
+ print_update()
+ }
+}
diff --git a/cdist/conf/type/__sshd_config/gencode-remote b/cdist/conf/type/__sshd_config/gencode-remote
new file mode 100755
index 00000000..275db4aa
--- /dev/null
+++ b/cdist/conf/type/__sshd_config/gencode-remote
@@ -0,0 +1,98 @@
+#!/bin/sh -e
+#
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+joinlines() { sed -n -e H -e "\${x;s/^\\n//;s/\\n/${1:?}/g;p;}"; }
+
+state_is=$(cat "${__object:?}/explorer/state")
+state_should=$(cat "${__object:?}/parameter/state")
+
+if test "${state_is}" = "${state_should}" -o "${state_is}" = 'default'
+then
+ # nothing to do (if the value is the default, ignore its state)
+ exit 0
+fi
+
+case ${state_should}
+in
+ (present)
+ mode='set'
+ ;;
+ (absent)
+ mode='unset'
+ ;;
+ (*)
+ printf 'Invalid --state: %s\n' "${state_should}" >&2
+ exit 1
+ ;;
+esac
+
+sshd_config_file=$(cat "${__object:?}/parameter/file")
+
+quote() { printf "'%s'" "$(printf '%s' "$*" | sed -e "s/'/'\\\\''/g")"; }
+drop_awk_comments() { quote "$(sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@")"; }
+
+# Ensure the sshd_config file is there
+cat <$(quote "${sshd_config_file}")
+ chown 0:0 $(quote "${sshd_config_file}")
+ chmod 0644 $(quote "${sshd_config_file}")
+}
+
+EOF
+
+match_only=
+if test -s "${__object:?}/parameter/match"
+then
+ match_only=$(joinlines ' ' <"${__object:?}/parameter/match")
+fi
+
+if test -s "${__object:?}/parameter/option"
+then
+ option_line=$(cat "${__object:?}/parameter/option")
+else
+ option_line=${__object_id:?}
+fi
+
+if test -s "${__object:?}/parameter/value"
+then
+ option_line="${option_line} $(cat "${__object:?}/parameter/value")"
+fi
+
+# Send message on config update
+printf '%s%s %s\n' "${mode}" "${match_only:+ [${match_only}]}" \
+ "${option_line}" >>"${__messages_out:?}"
+
+# Update sshd_config (remote code)
+cat <$(quote "${sshd_config_file}.tmp") \\
+|| exit
+
+cmp -s $(quote "${sshd_config_file}") $(quote "${sshd_config_file}.tmp") || {
+ sshd -t -f $(quote "${sshd_config_file}.tmp") \\
+ && cat $(quote "${sshd_config_file}.tmp") >$(quote "${sshd_config_file}") \\
+ || exit # stop if sshd_config file check fails
+}
+rm -f $(quote "${sshd_config_file}.tmp")
+EOF
diff --git a/cdist/conf/type/__sshd_config/man.rst b/cdist/conf/type/__sshd_config/man.rst
new file mode 100644
index 00000000..c8e6b8ad
--- /dev/null
+++ b/cdist/conf/type/__sshd_config/man.rst
@@ -0,0 +1,98 @@
+cdist-type__sshd_config(7)
+==========================
+
+NAME
+----
+cdist-type__sshd_config - Manage options in sshd_config
+
+
+DESCRIPTION
+-----------
+This space intentionally left blank.
+
+
+REQUIRED PARAMETERS
+-------------------
+None.
+
+
+OPTIONAL PARAMETERS
+-------------------
+file
+ The path to the sshd_config file to edit.
+ Defaults to ``/etc/ssh/sshd_config``.
+match
+ Restrict this option to apply only for certain connections.
+ Allowed values are what would be allowed to be written after a ``Match``
+ keyword in ``sshd_config``, e.g. ``--match 'User anoncvs'``.
+
+ Can be used multiple times. All of the values are ANDed together.
+option
+ The name of the option to manipulate. Defaults to ``__object_id``.
+state
+ Can be:
+
+ - ``present``: ensure a matching config line is present (or the default
+ value).
+ - ``absent``: ensure no matching config line is present.
+value
+ The option's value to be assigned to the option (if ``--state present``) or
+ removed (if ``--state absent``).
+
+ This option is required if ``--state present``. If not specified and
+ ``--state absent``, all values for the given option are removed.
+
+
+BOOLEAN PARAMETERS
+------------------
+None.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # Disallow root logins with password
+ __sshd_config PermitRootLogin --value without-password
+
+ # Disallow password-based authentication
+ __sshd_config PasswordAuthentication --value no
+
+ # Accept the EDITOR environment variable
+ __sshd_config AcceptEnv:EDITOR --option AcceptEnv --value EDITOR
+
+ # Force command for connections as git user
+ __sshd_config git@ForceCommand --match 'User git' --option ForceCommand \
+ --value 'cd ~git && exec git-shell ${SSH_ORIGINAL_COMMAND:+-c "${SSH_ORIGINAL_COMMAND}"}'
+
+
+SEE ALSO
+--------
+:strong:`sshd_config`\ (5)
+
+
+BUGS
+----
+- This type assumes a nicely formatted config file,
+ i.e. no config options spanning multiple lines.
+- ``Include`` directives are ignored.
+- Config options are not added/removed to/from the config file if their value is
+ the default value.
+- | The explorer will incorrectly report ``absent`` if OpenSSH internally
+ transforms one value to another (e.g. ``permitrootlogin prohibit-password``
+ is transformed to ``permitrootlogin without-password``).
+ | Workaround: Use the value that OpenSSH uses internally.
+
+
+AUTHORS
+-------
+Dennis Camera
+
+
+COPYING
+-------
+Copyright \(C) 2020 Dennis Camera. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__sshd_config/manifest b/cdist/conf/type/__sshd_config/manifest
new file mode 100755
index 00000000..e37afebb
--- /dev/null
+++ b/cdist/conf/type/__sshd_config/manifest
@@ -0,0 +1,55 @@
+#!/bin/sh -e
+#
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+os=$(cat "${__global:?}/explorer/os")
+
+state_should=$(cat "${__object:?}/parameter/state")
+
+case ${os}
+in
+ (alpine|centos|fedora|redhat|scientific|debian|devuan|ubuntu)
+ if test "${state_should}" != 'absent'
+ then
+ __package openssh-server --state present
+ fi
+ ;;
+ (archlinux|gentoo|slackware|suse)
+ if test "${state_should}" != 'absent'
+ then
+ __package openssh --state present
+ fi
+ ;;
+ (freebsd|netbsd|openbsd)
+ # whitelist
+ ;;
+ (openbmc-phosphor)
+ # whitelist
+ # OpenBMC can be configured with dropbear and OpenSSH.
+ # If dropbear is used, the state explorer will already fail because it
+ # cannot find the sshd binary.
+ ;;
+ (*)
+ : "${__type:?}" # make shellcheck happy
+ printf 'Your operating system (%s) is currently not supported by this type (%s)\n' \
+ "${os}" "${__type##*/}" >&2
+ printf 'Please contribute an implementation for it if you can.\n' >&2
+ exit 1
+ ;;
+esac
diff --git a/cdist/conf/type/__sshd_config/parameter/default/file b/cdist/conf/type/__sshd_config/parameter/default/file
new file mode 100644
index 00000000..d8ea5dfc
--- /dev/null
+++ b/cdist/conf/type/__sshd_config/parameter/default/file
@@ -0,0 +1 @@
+/etc/ssh/sshd_config
diff --git a/cdist/conf/type/__sshd_config/parameter/default/state b/cdist/conf/type/__sshd_config/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__sshd_config/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__sshd_config/parameter/optional b/cdist/conf/type/__sshd_config/parameter/optional
new file mode 100644
index 00000000..922ab093
--- /dev/null
+++ b/cdist/conf/type/__sshd_config/parameter/optional
@@ -0,0 +1,4 @@
+file
+option
+state
+value
diff --git a/cdist/conf/type/__sshd_config/parameter/optional_multiple b/cdist/conf/type/__sshd_config/parameter/optional_multiple
new file mode 100644
index 00000000..02b1d1a9
--- /dev/null
+++ b/cdist/conf/type/__sshd_config/parameter/optional_multiple
@@ -0,0 +1 @@
+match
diff --git a/cdist/conf/type/__staged_file/gencode-local b/cdist/conf/type/__staged_file/gencode-local
index 8e2003af..ba9e8798 100755
--- a/cdist/conf/type/__staged_file/gencode-local
+++ b/cdist/conf/type/__staged_file/gencode-local
@@ -23,7 +23,6 @@
destination="$__object_id"
source="$(cat "$__object/parameter/source")"
-cksum="$(cat "$__object/parameter/cksum")"
stage_dir="$(cat "$__object/parameter/stage-dir")"
state="$(cat "$__object/parameter/state")"
fetch_command="$(cat "$__object/parameter/fetch-command")"
@@ -57,24 +56,30 @@ get_file() {
}
fetch_file() {
+ # shellcheck disable=SC2059
printf "$fetch_command" "$source"
printf ' > "%s"\n' "$stage_file"
}
fetch_and_prepare_file() {
- printf 'tmpdir="$(mktemp -d --tmpdir="/tmp" "%s")"\n' "${__type##*/}.XXXXXXXXXX"
+ # shellcheck disable=SC2016
+ printf 'tmpdir="$(mktemp -d -p "/tmp" "%s")"\n' "${__type##*/}.XXXXXXXXXX"
+ # shellcheck disable=SC2016
printf 'cd "$tmpdir"\n'
- printf "$fetch_command > \"%s\"\n" "$source" "$source_file_name"
+ # shellcheck disable=SC2059
+ printf "$fetch_command > \"%s\"\\n" "$source" "$source_file_name"
prepare_command="$(cat "$__object/parameter/prepare-command")"
- printf "$prepare_command > \"%s\"\n" "$source_file_name" "$stage_file"
+ # shellcheck disable=SC2059
+ printf "$prepare_command > \"%s\"\\n" "$source_file_name" "$stage_file"
printf 'cd - >/dev/null\n'
+ # shellcheck disable=SC2016
printf 'rm -rf "$tmpdir"\n'
}
cat << DONE
verify_cksum() {
cksum_is="\$(cksum "$stage_file" | cut -d' ' -f1,2)"
- cksum_should="$(cat "$__object/parameter/cksum" | cut -d' ' -f1,2)"
+ cksum_should="$(cut -d' ' -f1,2 "$__object/parameter/cksum")"
if [ "\$cksum_is" = "\$cksum_should" ]; then
return 0
else
diff --git a/cdist/conf/type/__staged_file/manifest b/cdist/conf/type/__staged_file/manifest
index 1654e1d9..c8e1fbbb 100755
--- a/cdist/conf/type/__staged_file/manifest
+++ b/cdist/conf/type/__staged_file/manifest
@@ -19,11 +19,7 @@
#
destination="$__object_id"
-source="$(cat "$__object/parameter/source")"
-cksum="$(cat "$__object/parameter/cksum")"
stage_dir="$(cat "$__object/parameter/stage-dir")"
-state="$(cat "$__object/parameter/state")"
-fetch_command="$(cat "$__object/parameter/fetch-command")"
stage_file="${stage_dir}/${destination}"
set -- "/${destination}"
diff --git a/cdist/conf/type/__start_on_boot/explorer/state b/cdist/conf/type/__start_on_boot/explorer/state
index d49f01c7..b7a6cf0f 100644
--- a/cdist/conf/type/__start_on_boot/explorer/state
+++ b/cdist/conf/type/__start_on_boot/explorer/state
@@ -1,6 +1,6 @@
#!/bin/sh
#
-# 2012-2015 Nico Schottelius (nico-cdist at schottelius.org)
+# 2012-2019 Nico Schottelius (nico-cdist at schottelius.org)
# 2013 Daniel Heule (hda at sfs.biz)
#
# This file is part of cdist.
@@ -38,12 +38,27 @@ if [ "$init" = 'systemd' ]; then
else
case "$os" in
debian|openwrt|devuan)
- state="present"
- [ -f "/etc/rc$runlevel.d/S"??"$name" ] || state="absent"
+ state="absent"
+ for file in "/etc/rc$runlevel.d/S"??"$name"
+ do
+ if [ -f "$file" ]
+ then
+ state="present"
+ break
+ fi
+ done
;;
ubuntu)
state="absent"
- [ -f "/etc/rc$runlevel.d/S"??"$name" ] && state="present"
+ for file in "/etc/rc$runlevel.d/S"??"$name"
+ do
+ if [ -f "$file" ]
+ then
+ state="present"
+ break
+ fi
+ done
+
[ -f "/etc/init/${name}.conf" ] && state="present"
;;
@@ -60,10 +75,25 @@ else
state=$(chkconfig --check "$name" "$runlevel" || echo absent)
[ "$state" ] || state="present"
;;
- gentoo)
- state="present"
- [ -f "/etc/runlevels/${target_runlevel}/${name}" ] || state="absent"
+ gentoo|alpine)
+ state="absent"
+ for d in /etc/runlevels/*; do
+ if [ -f "/etc/runlevels/${d}/${name}" ];then
+ state="present"
+ break
+ fi
+ done
;;
+ freebsd)
+ state="absent"
+ service -e | grep "/$name$" && state="present"
+ ;;
+ openbsd)
+ state='absent'
+ # OpenBSD 5.7 and higher
+ rcctl ls on | grep "^${name}$" && state='present'
+ ;;
+
*)
echo "Unsupported os: $os" >&2
exit 1
diff --git a/cdist/conf/type/__start_on_boot/gencode-remote b/cdist/conf/type/__start_on_boot/gencode-remote
index 63f0ba3c..c900933f 100755
--- a/cdist/conf/type/__start_on_boot/gencode-remote
+++ b/cdist/conf/type/__start_on_boot/gencode-remote
@@ -37,16 +37,16 @@ case "$state_should" in
if [ "$init" = 'systemd' ]; then
# this handles ALL linux distros with systemd
# e.g. archlinux, gentoo in some cases, new RHEL and SLES versions
- echo "systemctl -q enable \"$name\""
+ echo "systemctl -q enable '$name'"
else
case "$os" in
debian)
case "$os_version" in
[1-7]*)
- echo "update-rc.d \"$name\" defaults >/dev/null"
+ echo "update-rc.d '$name' defaults >/dev/null"
;;
8*)
- echo "systemctl enable \"$name\""
+ echo "systemctl enable '$name'"
;;
*)
echo "Unsupported version $os_version of $os" >&2
@@ -55,26 +55,35 @@ case "$state_should" in
esac
;;
devuan)
- echo "update-rc.d \"$name\" defaults >/dev/null"
+ echo "update-rc.d '$name' defaults >/dev/null"
;;
- gentoo)
- echo rc-update add \"$name\" \"$target_runlevel\"
+ alpine|gentoo)
+ echo "rc-update add '$name' '$target_runlevel'"
;;
amazon|scientific|centos|fedora|owl|redhat|suse)
- echo chkconfig \"$name\" on
+ echo "chkconfig '$name' on"
;;
openwrt)
# 'enable' can be successful and still return a non-zero exit
# code, deal with it by checking for success ourselves in that
# case (the || ... part).
- echo "/etc/init.d/\"$name\" enable || [ -f /etc/rc.d/S??\"$name\" ]"
+ echo "'/etc/init.d/$name' enable || [ -f /etc/rc.d/S??'$name' ]"
;;
ubuntu)
- echo "update-rc.d \"$name\" defaults >/dev/null"
+ echo "update-rc.d '$name' defaults >/dev/null"
+ ;;
+
+ freebsd)
+ : # handled in manifest
+ ;;
+
+ openbsd)
+ # OpenBSD 5.7 and higher
+ echo "rcctl enable '$name'"
;;
*)
@@ -89,24 +98,29 @@ case "$state_should" in
if [ "$init" = 'systemd' ]; then
# this handles ALL linux distros with systemd
# e.g. archlinux, gentoo in some cases, new RHEL and SLES versions
- echo "systemctl -q disable \"$name\""
+ echo "systemctl -q disable '$name'"
else
case "$os" in
debian|ubuntu|devuan)
- echo update-rc.d -f \"$name\" remove
+ echo "update-rc.d -f '$name' remove"
;;
- gentoo)
- echo rc-update del \"$name\" \"$target_runlevel\"
+ alpine|gentoo)
+ echo "rc-update del '$name' '$target_runlevel'"
;;
centos|fedora|owl|redhat|suse)
- echo chkconfig \"$name\" off
+ echo "chkconfig '$name' off"
;;
openwrt)
- echo "\"/etc/init.d/$name\" disable"
+ echo "'/etc/init.d/$name' disable"
+ ;;
+
+ openbsd)
+ # OpenBSD 5.7 and higher
+ echo "rcctl disable '$name'"
;;
*)
diff --git a/cdist/conf/type/__start_on_boot/man.rst b/cdist/conf/type/__start_on_boot/man.rst
index 851d1a89..f8afe94b 100644
--- a/cdist/conf/type/__start_on_boot/man.rst
+++ b/cdist/conf/type/__start_on_boot/man.rst
@@ -12,7 +12,7 @@ This cdist type allows you to enable or disable stuff to be started
at boot of your operating system.
Warning: This type has not been tested intensively and is not fully
-supported (i.e. \*BSD are not implemented).
+supported.
REQUIRED PARAMETERS
@@ -55,7 +55,7 @@ Nico Schottelius
COPYING
-------
-Copyright \(C) 2012 Nico Schottelius. You can redistribute it
+Copyright \(C) 2012-2019 Nico Schottelius. You can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
diff --git a/cdist/conf/type/__start_on_boot/manifest b/cdist/conf/type/__start_on_boot/manifest
new file mode 100644
index 00000000..c1c983ec
--- /dev/null
+++ b/cdist/conf/type/__start_on_boot/manifest
@@ -0,0 +1,28 @@
+#!/bin/sh -e
+
+state_should="$(cat "$__object/parameter/state")"
+state_is=$(cat "$__object/explorer/state")
+name="$__object_id"
+
+# Short circuit if nothing is to be done
+[ "$state_should" = "$state_is" ] && exit 0
+
+os=$(cat "$__global/explorer/os")
+
+case "$os" in
+ freebsd)
+ if [ "$state_should" = 'present' ]; then
+ value='YES'
+ else
+ value='NO'
+ fi
+ __key_value "rcconf-$name-enable" \
+ --file /etc/rc.conf \
+ --key "${name}_enable" \
+ --value "\"$value\"" \
+ --delimiter '='
+ ;;
+ *)
+ : # handled in gencode-remote
+ ;;
+esac
diff --git a/cdist/conf/type/__hostname/explorer/hostname_sysconfig b/cdist/conf/type/__sysctl/explorer/conf-path
similarity index 76%
rename from cdist/conf/type/__hostname/explorer/hostname_sysconfig
rename to cdist/conf/type/__sysctl/explorer/conf-path
index d0d7b4e7..ba35c4c6 100755
--- a/cdist/conf/type/__hostname/explorer/hostname_sysconfig
+++ b/cdist/conf/type/__sysctl/explorer/conf-path
@@ -1,6 +1,6 @@
#!/bin/sh
#
-# 2014 Nico Schottelius (nico-cdist at schottelius.org)
+# 2018 Darko Poljak (darko.poljak at gmail.com)
#
# This file is part of cdist.
#
@@ -17,10 +17,9 @@
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see .
#
-#
-# Retrieve the contents of /etc/hostname
-#
-if [ -f /etc/sysconfig/network ]; then
- awk -F= '/^HOSTNAME=/ { print $2 }' /etc/sysconfig/network
+if [ -d "/etc/sysctl.d" ]; then
+ echo "/etc/sysctl.d/99-Z-sysctl-cdist.conf";
+else
+ echo "/etc/sysctl.conf";
fi
diff --git a/cdist/conf/type/__sysctl/explorer/value b/cdist/conf/type/__sysctl/explorer/value
index fc85b3d8..3e93c151 100755
--- a/cdist/conf/type/__sysctl/explorer/value
+++ b/cdist/conf/type/__sysctl/explorer/value
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/sh -e
#
# 2014 Steven Armstrong (steven-cdist at armstrong.cc)
#
@@ -18,5 +18,10 @@
# along with cdist. If not, see .
#
+if test "$(uname -s)" = NetBSD
+then
+ PATH=$(getconf PATH)
+fi
+
# get the current runtime value
-sysctl -n "$__object_id" || true
+sysctl -n "${__object_id}" || true
diff --git a/cdist/conf/type/__sysctl/gencode-remote b/cdist/conf/type/__sysctl/gencode-remote
index b7fb02c8..f0f6deef 100755
--- a/cdist/conf/type/__sysctl/gencode-remote
+++ b/cdist/conf/type/__sysctl/gencode-remote
@@ -1,6 +1,7 @@
#!/bin/sh -e
#
# 2014 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2018 Takashi Yoshi (takashi at yoshi.email)
#
# This file is part of cdist.
#
@@ -26,5 +27,31 @@ if [ "$value_should" = "$value_is" ]; then
exit 0
fi
+os=$(cat "$__global/explorer/os")
+case "$os" in
+ # Linux
+ redhat|centos|ubuntu|debian|devuan|archlinux|gentoo|coreos)
+ flag='-w'
+ ;;
+ # BusyBox
+ alpine|openwrt)
+ flag='-w'
+ ;;
+ macosx)
+ # NOTE: Older versions of Mac OS X require the -w option.
+ # Even though the flag is not mentioned in new man pages anymore,
+ # it still works.
+ flag='-w'
+ ;;
+ netbsd)
+ # shellcheck disable=SC2016
+ echo 'PATH=$(getconf PATH)'
+ flag='-w'
+ ;;
+ freebsd|openbsd)
+ flag=''
+ ;;
+esac
+
# set the current runtime value
-printf 'sysctl -w %s="%s"\n' "$__object_id" "$value_should"
+printf 'sysctl %s %s="%s"\n' "$flag" "$__object_id" "$value_should"
diff --git a/cdist/conf/type/__sysctl/man.rst b/cdist/conf/type/__sysctl/man.rst
index 6873003e..dbb9a1ac 100644
--- a/cdist/conf/type/__sysctl/man.rst
+++ b/cdist/conf/type/__sysctl/man.rst
@@ -26,6 +26,13 @@ EXAMPLES
__sysctl net.ipv4.ip_forward --value 1
+ # On some operating systems, e.g. NetBSD, to prevent an error if the
+ # MIB style name does not exist (e.g. optional kernel components),
+ # name and value can be separated by `?=`. The same effect can be achieved
+ # in cdist by appending a `?` to the key:
+
+ __sysctl ddb.onpanic? --value -1
+
AUTHORS
-------
diff --git a/cdist/conf/type/__sysctl/manifest b/cdist/conf/type/__sysctl/manifest
index 39a2e53c..71dea7f7 100755
--- a/cdist/conf/type/__sysctl/manifest
+++ b/cdist/conf/type/__sysctl/manifest
@@ -1,6 +1,8 @@
#!/bin/sh -e
#
# 2014 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2018 Takashi Yoshi (takashi at yoshi.email)
+# 2019 Nico Schottelius (nico-cdist at schottelius.org)
#
# This file is part of cdist.
#
@@ -22,7 +24,12 @@
os=$(cat "$__global/explorer/os")
case "$os" in
- redhat|centos|ubuntu|debian|devuan|archlinux|coreos)
+ # Linux
+ alpine|redhat|centos|ubuntu|debian|devuan|archlinux|coreos)
+ :
+ ;;
+ # BSD
+ freebsd|macosx|netbsd|openbsd)
:
;;
*)
@@ -32,8 +39,10 @@ case "$os" in
;;
esac
+conf_path=$(cat "$__object/explorer/conf-path")
+
__key_value "$__object_name" \
--key "$__object_id" \
- --file /etc/sysctl.conf \
+ --file "${conf_path}" \
--value "$(cat "$__object/parameter/value")" \
--delimiter '='
diff --git a/cdist/conf/type/__systemd_service/explorer/state b/cdist/conf/type/__systemd_service/explorer/state
new file mode 100755
index 00000000..f5f751d4
--- /dev/null
+++ b/cdist/conf/type/__systemd_service/explorer/state
@@ -0,0 +1,43 @@
+#!/bin/sh -e
+# explorer/state
+#
+# 2020 Matthias Stecher
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+# Check if the service is running or stopped.
+#
+# The explorer must check before if the service exist, because 'systemctl is-active'
+# will return "inactive" even if there is no service there:
+# systemctl cat foo # does not exist
+# systemctl is-active foo # is "inactive"
+
+
+# get name of the service
+if [ -f "$__object/parameter/name" ]; then
+ name="$(cat "$__object/parameter/name")"
+else
+ name="$__object_id"
+fi
+
+
+# check if the service exist, else exit without output (also if systemd doesn't exist)
+# do not exit here with an error code, will be done in the gencode-remote script
+systemctl cat "$name" > /dev/null 2>&1 || exit 0
+
+# print if the service is running or not
+systemctl is-active -q "$name" && printf "running" || printf "stopped"
diff --git a/cdist/conf/type/__systemd_service/gencode-remote b/cdist/conf/type/__systemd_service/gencode-remote
new file mode 100755
index 00000000..c867ff22
--- /dev/null
+++ b/cdist/conf/type/__systemd_service/gencode-remote
@@ -0,0 +1,98 @@
+#!/bin/sh -e
+# gencode-remote
+#
+# 2020 Matthias Stecher
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+# Checks the given state of the service and set it to the given
+# state. Optionally, it executes the action if service running.
+
+
+# get name of the service
+name="$__object/parameter/name"
+if [ -f "$name" ]; then
+ name="$(cat "$name")"
+else
+ name="$__object_id"
+fi
+
+
+# read current status and parameters
+state="$(cat "$__object/explorer/state")"
+should="$(cat "$__object/parameter/state")"
+
+# if systemd/service does not exist
+if [ -z "$state" ]; then
+ printf "systemd or service '%s' does not exist!\n" "$name" >&2
+ exit 1
+fi
+
+
+# save the action required
+required_action=""
+
+# check the state of the service that should be
+if [ "$state" != "$should" ]; then
+ # select what to do to get the $should state
+ case "$should" in
+ running)
+ if [ "$state" = "stopped" ]; then required_action="start"; fi
+ ;;
+
+ stopped)
+ if [ "$state" = "running" ]; then required_action="stop"; fi
+ ;;
+ esac
+fi
+
+# check if the action can be achieved if given
+if [ -f "$__object/parameter/action" ] \
+ && [ -z "$required_action" ] && [ "$state" = "running" ]; then
+
+ # there must be an action
+ action="$(cat "$__object/parameter/action")"
+
+ # select the action to the required element
+ case "$action" in
+ restart)
+ required_action="restart"
+ ;;
+
+ reload)
+ required_action="reload"
+ ;;
+
+ *)
+ printf "action '%s' does not exist!" "$action" >&2
+ exit 2
+ esac
+
+ # Make a special check: only do this action if a dependency did something
+ # it is required that the dependencies write there action to $__messages_in
+ if [ -f "$__object/parameter/if-required" ]; then
+ # exit here if there are no changes from the dependencies affected (nothing to do)
+ if ! grep -q -f "$__object/require" "$__messages_in"; then exit 0; fi
+ fi
+fi
+
+# print the execution command if a action given
+if [ -n "$required_action" ]; then
+ # also print it as message
+ echo "$required_action" >> "$__messages_out"
+ echo "systemctl $required_action '$name'"
+fi
diff --git a/cdist/conf/type/__systemd_service/man.rst b/cdist/conf/type/__systemd_service/man.rst
new file mode 100644
index 00000000..cd14c985
--- /dev/null
+++ b/cdist/conf/type/__systemd_service/man.rst
@@ -0,0 +1,117 @@
+cdist-type__systemd_service(7)
+==============================
+
+NAME
+----
+cdist-type__systemd_service - Controls a systemd service state
+
+
+DESCRIPTION
+-----------
+This type controls systemd services to define a state of the service,
+or an action like reloading or restarting. It is useful to reload a
+service after configuration applied or shutdown one service.
+
+The activation or deactivation is out of scope. Look for the
+:strong:`cdist-type__systemd_util`\ (7) type instead.
+
+
+REQUIRED PARAMETERS
+-------------------
+None.
+
+
+OPTIONAL PARAMETERS
+-------------------
+
+name
+ String which will used as name instead of the object id.
+
+state
+ The state which the service should be in:
+
+ running
+ Service should run (default)
+
+ stopped
+ Service should be stopped
+
+action
+ Executes an action on on the service. It will only execute it if the
+ service keeps the state ``running``. There are following actions, where:
+
+ reload
+ Reloads the service
+
+ restart
+ Restarts the service
+
+BOOLEAN PARAMETERS
+------------------
+
+if-required
+ Only execute the action if at minimum one required type outputs a message
+ to ``$__messages_out``. Through this, the action should only executed if a
+ dependency did something. The action will not executed if no dependencies
+ given.
+
+
+MESSAGES
+--------
+
+start
+ Started the service
+
+stop
+ Stopped the service
+
+restart
+ Restarted the service
+
+reload
+ Reloaded the service
+
+
+ABORTS
+------
+Aborts in following cases:
+
+systemd or the service does not exist
+
+
+EXAMPLES
+--------
+.. code-block:: sh
+
+ # service must run
+ __systemd_service nginx
+
+ # service must stopped
+ __systemd_service sshd \
+ --state stopped
+
+ # restart the service
+ __systemd_service apache2 \
+ --action restart
+
+ # makes sure the service exist with an alternative name
+ __systemd_service foo \
+ --name sshd
+
+ # reload the service for a modified configuration file
+ # only reloads the service if the file really changed
+ require="__file/etc/foo.conf" __systemd_service foo \
+ --action reload --if-required
+
+
+AUTHORS
+-------
+Matthias Stecher
+
+
+COPYRIGHT
+---------
+Copyright \(C) 2020 Matthias Stecher. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__systemd_service/parameter/boolean b/cdist/conf/type/__systemd_service/parameter/boolean
new file mode 100644
index 00000000..a4bccb66
--- /dev/null
+++ b/cdist/conf/type/__systemd_service/parameter/boolean
@@ -0,0 +1 @@
+if-required
diff --git a/cdist/conf/type/__systemd_service/parameter/default/state b/cdist/conf/type/__systemd_service/parameter/default/state
new file mode 100644
index 00000000..a2ae71b3
--- /dev/null
+++ b/cdist/conf/type/__systemd_service/parameter/default/state
@@ -0,0 +1 @@
+running
diff --git a/cdist/conf/type/__systemd_service/parameter/optional b/cdist/conf/type/__systemd_service/parameter/optional
new file mode 100644
index 00000000..fc78265f
--- /dev/null
+++ b/cdist/conf/type/__systemd_service/parameter/optional
@@ -0,0 +1,3 @@
+name
+state
+action
diff --git a/cdist/conf/type/__hostname/explorer/hostname_file b/cdist/conf/type/__systemd_unit/explorer/unit-status
old mode 100755
new mode 100644
similarity index 73%
rename from cdist/conf/type/__hostname/explorer/hostname_file
rename to cdist/conf/type/__systemd_unit/explorer/unit-status
index 6a00aa9f..b68e5169
--- a/cdist/conf/type/__hostname/explorer/hostname_file
+++ b/cdist/conf/type/__systemd_unit/explorer/unit-status
@@ -1,6 +1,6 @@
#!/bin/sh
#
-# 2014 Nico Schottelius (nico-cdist at schottelius.org)
+# 2017 Ľubomír Kučera
#
# This file is part of cdist.
#
@@ -17,14 +17,5 @@
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see .
#
-#
-# Retrieve the contents of /etc/hostname
-#
-# Almost any distribution
-if [ -f /etc/hostname ]; then
- cat /etc/hostname
-# SuSE
-elif [ -f /etc/HOSTNAME ]; then
- cat /etc/HOSTNAME
-fi
+systemctl is-active "${__object_id}" || true
diff --git a/cdist/conf/type/__systemd_unit/gencode-remote b/cdist/conf/type/__systemd_unit/gencode-remote
index 6ea3ddaa..967a6c87 100644
--- a/cdist/conf/type/__systemd_unit/gencode-remote
+++ b/cdist/conf/type/__systemd_unit/gencode-remote
@@ -18,28 +18,39 @@
# along with cdist. If not, see .
#
-systemctl_present=$(cat "${__object}/explorer/systemctl-present")
-
-if [ "${systemctl_present}" -ne 0 ]; then
- echo "systemctl does not seem to be present on this system" >&2
-
- exit 1
-fi
-
name="${__object_id}"
state=$(cat "${__object}/parameter/state")
current_enablement_state=$(cat "${__object}/explorer/enablement-state")
if [ "${state}" = "absent" ]; then
- if [ ! -z "${current_enablement_state}" ]; then
+ if [ -n "${current_enablement_state}" ]; then
echo "systemctl --now disable ${name}"
+ echo "rm -f /etc/systemd/system/${name}"
+ echo "systemctl daemon-reload"
fi
exit 0
fi
+unit_status=$(cat "${__object}/explorer/unit-status")
desired_enablement_state=$(cat "${__object}/parameter/enablement-state")
+if [ "${current_enablement_state}" = "masked" ] && \
+ [ "${desired_enablement_state}" != "masked" ]; then
+ echo "systemctl unmask ${name}"
+fi
+
+if [ -f "${__object}/parameter/restart" ]; then
+ if [ "${desired_enablement_state}" = "masked" ]; then
+ if [ "${unit_status}" = "active" ]; then
+ echo "systemctl stop ${name}"
+ fi
+ elif grep -q "^__file/etc/systemd/system/${name}" "${__messages_in}" || \
+ [ "${unit_status}" != "active" ]; then
+ echo "systemctl restart ${name} || true"
+ fi
+fi
+
if [ "${current_enablement_state}" = "${desired_enablement_state}" ]; then
exit 0
fi
@@ -55,6 +66,9 @@ case "${desired_enablement_state}" in
disabled)
echo "systemctl disable ${name}"
;;
+ masked)
+ echo "systemctl mask ${name}"
+ ;;
*)
echo "Unsupported unit status: ${desired_enablement_state}" >&2
exit 1
diff --git a/cdist/conf/type/__systemd_unit/man.rst b/cdist/conf/type/__systemd_unit/man.rst
index c624e91e..25a4e501 100644
--- a/cdist/conf/type/__systemd_unit/man.rst
+++ b/cdist/conf/type/__systemd_unit/man.rst
@@ -9,9 +9,10 @@ cdist-type__systemd_unit - Install a systemd unit
DESCRIPTION
-----------
-This type can install, enable and start a systemd unit. This is particularly
-useful on systems which take advantage of systemd heavily (e.g., CoreOS). For
-more information about systemd units, see SYSTEMD.UNIT(5).
+This type manages systemd units in ``/etc/systemd/system/``. It can install,
+enable and start a systemd unit. This is particularly useful on systems which
+take advantage of systemd heavily (e.g., CoreOS). For more information about
+systemd units, see SYSTEMD.UNIT(5).
REQUIRED PARAMETERS
-------------------
@@ -22,12 +23,14 @@ OPTIONAL PARAMETERS
-------------------
enablement-state
- 'enabled' or 'disabled', where:
+ 'enabled', 'disabled' or 'masked', where:
enabled
enables the unit
disabled
disables the unit
+ masked
+ masks the unit
source
Path to the config file. If source is '-' (dash), take what was written to
@@ -37,15 +40,17 @@ state
'present' or 'absent', defaults to 'present' where:
present
- the unit is installed, enabled and started
+ the unit (or its mask) is installed
absent
- the unit is stopped, disabled and uninstalled
+ The unit is stopped, disabled and uninstalled. If the unit was masked,
+ the mask is removed.
BOOLEAN PARAMETERS
------------------
restart
- Restart the unit on change.
+ Start the unit if it was inactive. Restart the unit if the unit file
+ changed. Stop the unit if new ``enablement-state`` is ``masked``.
MESSAGES
--------
diff --git a/cdist/conf/type/__systemd_unit/manifest b/cdist/conf/type/__systemd_unit/manifest
index 10dc4f0c..688a00b1 100644
--- a/cdist/conf/type/__systemd_unit/manifest
+++ b/cdist/conf/type/__systemd_unit/manifest
@@ -18,22 +18,41 @@
# along with cdist. If not, see .
#
+systemctl_present=$(cat "${__object}/explorer/systemctl-present")
+
+if [ "${systemctl_present}" -ne 0 ]; then
+ echo "systemctl does not seem to be present on this system" >&2
+
+ exit 1
+fi
+
name="${__object_id}"
source=$(cat "${__object}/parameter/source")
state=$(cat "${__object}/parameter/state")
+enablement_state=$(cat "${__object}/parameter/enablement-state")
-onchange() {
- echo -n "systemctl daemon-reload"
+# The unit must be disabled before removing its unit file. The unit file is
+# therefore removed by gencode-remote of this type, not here.
+if [ -z "${source}" ] || [ "${state}" = "absent" ]; then
+ exit 0
+fi
- if [ -f "${__object}/parameter/restart" ]; then
- echo -n " && (systemctl restart ${name} || true)"
- fi
+# stdin is not propagated automatically to sub-objects
+if [ "${source}" = "-" ]; then
+ source="${__object}/stdin"
+fi
- echo
-}
+unitfile_state="${state}"
+if [ "${enablement_state}" = "masked" ]; then
+ # Masking creates a symlink from /etc/systemd/system/ to /dev/null.
+ # This process fails with "Failed to execute operation: Invalid argument"
+ # if file /etc/systemd/system/ already exists. We must therefore
+ # remove it.
+ unitfile_state="absent"
+fi
__config_file "/etc/systemd/system/${name}" \
--mode 644 \
- --onchange "$(onchange)" \
+ --onchange "systemctl daemon-reload" \
--source "${source}" \
- --state "${state}"
+ --state "${unitfile_state}"
diff --git a/cdist/conf/type/__timezone/gencode-remote b/cdist/conf/type/__timezone/gencode-remote
index 1f3f0196..b685c990 100755
--- a/cdist/conf/type/__timezone/gencode-remote
+++ b/cdist/conf/type/__timezone/gencode-remote
@@ -1,6 +1,7 @@
#!/bin/sh -e
#
# 2012 Steven Armstrong (steven-cdist at armstrong.cc)
+# 2019 Nico Schottelius (nico-cdist at schottelius.org)
#
# This file is part of cdist.
#
@@ -21,7 +22,7 @@
# This type allows to configure the desired localtime timezone.
timezone_is=$(cat "$__object/explorer/timezone_is")
-timezone_should="$__object_id"
+timezone_should=$(cat "$__object/parameter/tz")
os=$(cat "$__global/explorer/os")
if [ "$timezone_is" = "$timezone_should" ]; then
@@ -29,7 +30,7 @@ if [ "$timezone_is" = "$timezone_should" ]; then
fi
case "$os" in
- ubuntu|debian|devuan|coreos)
+ ubuntu|debian|devuan|coreos|alpine)
echo "echo \"$timezone_should\" > /etc/timezone"
;;
esac
diff --git a/cdist/conf/type/__timezone/man.rst b/cdist/conf/type/__timezone/man.rst
index 8a945c16..6012c552 100644
--- a/cdist/conf/type/__timezone/man.rst
+++ b/cdist/conf/type/__timezone/man.rst
@@ -14,7 +14,8 @@ This type creates a symlink (/etc/localtime) to the selected timezone
REQUIRED PARAMETERS
-------------------
-None.
+tz
+ The name of timezone to set.
OPTIONAL PARAMETERS
@@ -27,19 +28,24 @@ EXAMPLES
.. code-block:: sh
- #Set up Europe/Andorra as our timezone.
- __timezone Europe/Andorra
+ # Set up Europe/Andorra as our timezone.
+ __timezone --tz Europe/Andorra
- #Set up US/Central as our timezone.
- __timezone US/Central
+ # Set up US/Central as our timezone.
+ __timezone --tz US/Central
AUTHORS
-------
-Ramon Salvadó
+| Steven Armstrong
+| Nico Schottelius
+| Ramon Salvadó
+| Dennis Camera
COPYING
-------
-Free use of this software is
-granted under the terms of the GNU General Public License version 3 (GPLv3).
+Copyright \(C) 2012-2020 the `AUTHORS`_. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__timezone/manifest b/cdist/conf/type/__timezone/manifest
index a20f5d32..0eb7fb9c 100755
--- a/cdist/conf/type/__timezone/manifest
+++ b/cdist/conf/type/__timezone/manifest
@@ -2,7 +2,7 @@
#
# 2011 Ramon Salvadó (rsalvado at gnuine dot com)
# 2012-2015 Steven Armstrong (steven-cdist at armstrong.cc)
-# 2012 Nico Schottelius (nico-cdist at schottelius.org)
+# 2012-2019 Nico Schottelius (nico-cdist at schottelius.org)
#
# This file is part of cdist.
#
@@ -22,11 +22,11 @@
#
# This type allows to configure the desired localtime timezone.
-timezone="$__object_id"
+timezone=$(cat "$__object/parameter/tz")
os=$(cat "$__global/explorer/os")
case "$os" in
- archlinux|debian|ubuntu|devuan)
+ archlinux|debian|ubuntu|devuan|alpine)
__package tzdata
export require="__package/tzdata"
;;
@@ -34,7 +34,11 @@ case "$os" in
__package timezone
export require="__package/timezone"
;;
- freebsd|netbsd|coreos)
+ freebsd|netbsd|openbsd)
+ # whitelist
+ :
+ ;;
+ coreos)
# whitelist
:
;;
diff --git a/cdist/conf/type/__timezone/parameter/required b/cdist/conf/type/__timezone/parameter/required
new file mode 100644
index 00000000..975445e4
--- /dev/null
+++ b/cdist/conf/type/__timezone/parameter/required
@@ -0,0 +1 @@
+tz
diff --git a/cdist/conf/type/__timezone/singleton b/cdist/conf/type/__timezone/singleton
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__uci/explorer/state b/cdist/conf/type/__uci/explorer/state
new file mode 100644
index 00000000..d7363dbf
--- /dev/null
+++ b/cdist/conf/type/__uci/explorer/state
@@ -0,0 +1,110 @@
+#!/bin/sh
+#
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+# This explorer retrieves the current state of the configuration option
+# The output of this explorer is one of these values:
+# present
+# The configuration option is present and has the value of the
+# parameter --value.
+# absent
+# The configuration option is not defined.
+# different
+# The configuration option is present but has a different value than the
+# parameter --value.
+# rearranged
+# The configuration option is present (a list) and has the same values as
+# the parameter --value, but in a different order.
+
+RS=$(printf '\036')
+
+option=${__object_id:?}
+
+values_is=$(uci -s -N -d "${RS}" get "${option}" 2>/dev/null) || {
+ echo absent
+ exit 0
+}
+
+if test -f "${__object:?}/parameter/value"
+then
+ should_file="${__object:?}/parameter/value"
+else
+ should_file='/dev/null'
+fi
+
+
+# strip off trailing newline
+printf '%s' "${values_is}" \
+| awk '
+function unquote(s) {
+ # simplified dequoting of single quoted strings
+ if (s ~ /^'\''.*'\''$/) {
+ s = substr(s, 2, length(s) - 2)
+ sub(/'"'\\\\''"'/, "'\''", s)
+ }
+ return s
+}
+
+BEGIN {
+ state = "present" # assume all is fine
+}
+NR == FNR {
+ # memoize "should" state
+ should[FNR] = $0
+ should_count++
+
+ # go to next line (important!)
+ next
+}
+
+# compare "is" state
+
+{ $0 = unquote($0) }
+
+$0 == should[FNR] { next }
+
+FNR > should_count {
+ # there are more "is" records than "should" -> definitely different
+ state = "different"
+ exit
+}
+
+{
+ # see if we can find the value somewhere in should
+ for (i in should) {
+ if ($0 == should[i]) {
+ # ... value found -> rearranged
+ # FIXME: Duplicate values are not properly handled here. Do they matter?
+ state = "rearranged"
+ next
+ }
+ }
+
+ state = "different"
+ exit
+}
+
+END {
+ if (FNR < should_count) {
+ # "is" was shorter than "should" -> different
+ state = "different"
+ }
+
+ print state
+}
+' "${should_file}" RS="${RS}" -
diff --git a/cdist/conf/type/__uci/files/functions.sh b/cdist/conf/type/__uci/files/functions.sh
new file mode 100644
index 00000000..277f648c
--- /dev/null
+++ b/cdist/conf/type/__uci/files/functions.sh
@@ -0,0 +1,73 @@
+# -*- mode: sh; indent-tabs-mode: t -*-
+
+in_list() {
+ printf '%s\n' "$@" | { grep -qxF "$(read -r ndl; echo "${ndl}")"; }
+}
+
+quote() {
+ for _arg
+ do
+ shift
+ if test -n "$(printf %s "${_arg}" | tr -d -c '\t\n \042-\047\050-\052\073-\077\133\\`|~' | tr -c '' '.')"
+ then
+ # needs quoting
+ set -- "$@" "$(printf "'%s'" "$(printf %s "${_arg}" | sed -e "s/'/'\\\\''/g")")"
+ else
+ set -- "$@" "${_arg}"
+ fi
+ done
+ unset _arg
+
+ # NOTE: Use printf because POSIX echo interprets escape sequences
+ printf '%s' "$*"
+}
+
+uci_cmd() {
+ # Usage: uci_cmd [UCI ARGUMENTS]...
+ mkdir -p "${__object:?}/files"
+ printf '%s\n' "$(quote "$@")" >>"${__object:?}/files/uci_batch.txt"
+}
+
+uci_validate_name() {
+ # like util.c uci_validate_name()
+ test -n "$*" && test -z "$(echo "$*" | tr -d '[:alnum:]_')"
+}
+
+uci_validate_tuple() (
+ tok=${1:?}
+ case $tok
+ in
+ (*.*.*)
+ # check option
+ option=${tok##*.}
+ uci_validate_name "${option}" || {
+ printf 'Invalid option: %s\n' "${option}" >&2
+ return 1
+ }
+ tok=${tok%.*}
+ ;;
+ (*.*)
+ # no option (section definition)
+ ;;
+ (*)
+ printf 'Invalid tuple: %s\n' "$1" >&2
+ return 1
+ ;;
+ esac
+
+ case ${tok#*.}
+ in
+ (@*) section=$(expr "${tok#*.}" : '@\(.*\)\[-*[0-9]*\]$') ;;
+ (*) section=${tok#*.} ;;
+ esac
+ uci_validate_name "${section}" || {
+ printf 'Invalid section: %s\n' "${1#*.}" >&2
+ return 1
+ }
+
+ config=${tok%%.*}
+ uci_validate_name "${config}" || {
+ printf 'Invalid config: %s\n' "${config}" >&2
+ return 1
+ }
+)
diff --git a/cdist/conf/type/__uci/files/uci_apply.sh b/cdist/conf/type/__uci/files/uci_apply.sh
new file mode 100644
index 00000000..63f94290
--- /dev/null
+++ b/cdist/conf/type/__uci/files/uci_apply.sh
@@ -0,0 +1,43 @@
+changes=$(uci changes)
+
+if test -n "${changes}"
+then
+ echo 'Uncommited UCI changes were found on the target:'
+ printf '%s\n\n' "${changes}"
+ echo 'This can be caused by manual changes or due to a previous failed run.'
+ echo 'Please investigate the situation, revert or commit the changes, and try again.'
+ exit 1
+fi >&2
+
+check_errors() {
+ # reads stdin and forwards non-empty lines to stderr.
+ # returns 0 if stdin is empty, else 1.
+ ! grep -e . >&2
+}
+
+commit() {
+ uci commit
+}
+
+rollback() {
+ printf '\nAn error occurred when trying to commit UCI transaction!\n' >&2
+
+ uci changes \
+ | sed -e 's/^-//' -e 's/\..*\$//' \
+ | sort -u \
+ | while read -r _package
+ do
+ uci revert "${_package}"
+ echo "${_package}" # for logging
+ done \
+ | awk '
+ BEGIN { printf "Reverted changes in: " }
+ { printf "%s%s", (FNR > 1 ? ", " : ""), $0 }
+ END { printf "\n" }' >&2
+
+ return 1
+}
+
+uci_apply() {
+ uci batch 2>&1 | check_errors && commit || rollback
+}
diff --git a/cdist/conf/type/__uci/gencode-remote b/cdist/conf/type/__uci/gencode-remote
new file mode 100755
index 00000000..70a3d3e0
--- /dev/null
+++ b/cdist/conf/type/__uci/gencode-remote
@@ -0,0 +1,101 @@
+#!/bin/sh -e
+#
+# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+# shellcheck source=cdist/conf/type/__uci/files/functions.sh
+. "${__type:?}/files/functions.sh"
+
+state_is=$(cat "${__object:?}/explorer/state")
+state_should=$(cat "${__object:?}/parameter/state")
+
+config=${__object_id:?}
+uci_validate_tuple "${config}"
+
+
+case ${state_should}
+in
+ (present)
+ if in_list "${state_is}" 'present' 'rearranged'
+ then
+ # NOTE: order is ignored so rearranged is also fine.
+ exit 0
+ fi
+
+ # Determine type
+ type=$(cat "${__object:?}/parameter/type" 2>/dev/null || true)
+ case ${type}
+ in
+ (option|list) ;;
+ ('')
+ # Guess type by the number of values
+ test "$(wc -l "${__object:?}/parameter/value")" -gt 1 \
+ && type=list \
+ || type=option
+ ;;
+ (*)
+ printf 'Invalid --type: %s\n' "${type}" >&2
+ exit 1
+ ;;
+ esac
+
+ case ${type}
+ in
+ (list)
+ printf 'set_list %s\n' "${config}" >>"${__messages_out:?}"
+
+ if test "${state_is}" != 'absent'
+ then
+ uci_cmd delete "${config}"
+ fi
+
+ while read -r value
+ do
+ uci_cmd add_list "${config}"="${value}"
+ done <"${__object:?}/parameter/value"
+ ;;
+ (option)
+ printf 'set %s\n' "${config}" >>"${__messages_out:?}"
+
+ value=$(cat "${__object:?}/parameter/value")
+ uci_cmd set "${config}"="${value}"
+ ;;
+ esac
+ ;;
+ (absent)
+ if in_list "${state_is}" 'absent'
+ then
+ exit 0
+ fi
+
+ printf 'delete %s\n' "${config}" >>"${__messages_out:?}"
+ uci_cmd delete "${config}"
+ ;;
+ (*)
+ printf 'Invalid --state: %s\n' "${state_should}" >&2
+ exit 1
+ ;;
+esac
+
+if test -s "${__object:?}/files/uci_batch.txt"
+then
+ cat "${__type:?}/files/uci_apply.sh"
+ printf "uci_apply <<'EOF'\n"
+ cat "${__object:?}/files/uci_batch.txt"
+ printf '\nEOF\n'
+fi
diff --git a/cdist/conf/type/__uci/man.rst b/cdist/conf/type/__uci/man.rst
new file mode 100644
index 00000000..81a53473
--- /dev/null
+++ b/cdist/conf/type/__uci/man.rst
@@ -0,0 +1,78 @@
+cdist-type__uci(7)
+==================
+
+NAME
+----
+cdist-type__uci - Manage configuration values in UCI
+
+
+DESCRIPTION
+-----------
+This cdist type can be used to alter configuration options in OpenWrt's
+Unified Configuration Interface (UCI) system.
+
+
+REQUIRED PARAMETERS
+-------------------
+value
+ The value to be set. Can be used multiple times.
+ This parameter is ignored if ``--state`` is ``absent``.
+
+ Due to the way cdist handles arguments, values **must not** contain newline
+ characters.
+
+ Values do not need special quoting for UCI. The only requirement is that the
+ value is passed to the type as a single shell argument.
+
+OPTIONAL PARAMETERS
+-------------------
+state
+ ``present`` or ``absent``, defaults to ``present``.
+type
+ If the type should generate an option or a list.
+ One of: ``option`` or ``list``.
+ Defaults to auto-detect based on the number of ``--value`` parameters.
+
+
+BOOLEAN PARAMETERS
+------------------
+None.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # Set the system hostname
+ __uci system.@system[0].hostname --value 'OpenWrt'
+
+ # Set DHCP option 252: tell DHCP clients to not ask for proxy information.
+ __uci dhcp.lan.dhcp_option --type list --value '252,"\n"'
+
+ # Enable NTP and NTPd (each is applied individually)
+ __uci system.ntp.enabled --value 1
+ __uci system.ntp.enable_server --value 1
+ __uci system.ntp.server --type list \
+ --value '0.openwrt.pool.ntp.org' \
+ --value '1.openwrt.pool.ntp.org' \
+ --value '2.openwrt.pool.ntp.org' \
+ --value '3.openwrt.pool.ntp.org'
+
+
+SEE ALSO
+--------
+- https://openwrt.org/docs/guide-user/base-system/uci
+
+
+AUTHORS
+-------
+Dennis Camera
+
+
+COPYING
+-------
+Copyright \(C) 2020 Dennis Camera. You can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
diff --git a/cdist/conf/type/__uci/manifest b/cdist/conf/type/__uci/manifest
new file mode 100755
index 00000000..26920011
--- /dev/null
+++ b/cdist/conf/type/__uci/manifest
@@ -0,0 +1,51 @@
+#!/bin/sh -e
+#
+# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+os=$(cat "${__global:?}/explorer/os")
+
+state_should=$(cat "${__object:?}/parameter/state")
+
+case ${os}
+in
+ (openwrt)
+ # okay
+ ;;
+ (*)
+ printf "Your operating system (%s) is currently not supported by this type (%s)\n" "${os}" "${__type##*/}" >&2
+ printf "Please contribute an implementation for it if you can.\n" >&2
+ exit 1
+ ;;
+esac
+
+case ${state_should}
+in
+ (present)
+ test -s "${__object:?}/parameter/value" || {
+ echo 'The parameter --value is required.' >&2
+ exit 1
+ }
+ ;;
+ (absent)
+ ;;
+ (*)
+ printf 'Invalid --state: %s\n' "${state_should}" >&2
+ exit 1
+ ;;
+esac
diff --git a/cdist/conf/type/__uci/nonparallel b/cdist/conf/type/__uci/nonparallel
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__uci/parameter/default/state b/cdist/conf/type/__uci/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__uci/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__uci/parameter/optional b/cdist/conf/type/__uci/parameter/optional
new file mode 100644
index 00000000..d9080e3a
--- /dev/null
+++ b/cdist/conf/type/__uci/parameter/optional
@@ -0,0 +1,2 @@
+state
+type
diff --git a/cdist/conf/type/__uci/parameter/optional_multiple b/cdist/conf/type/__uci/parameter/optional_multiple
new file mode 100644
index 00000000..6d4e1507
--- /dev/null
+++ b/cdist/conf/type/__uci/parameter/optional_multiple
@@ -0,0 +1 @@
+value
diff --git a/cdist/conf/type/__uci_section/explorer/match b/cdist/conf/type/__uci_section/explorer/match
new file mode 100644
index 00000000..0768e404
--- /dev/null
+++ b/cdist/conf/type/__uci_section/explorer/match
@@ -0,0 +1,103 @@
+#!/bin/sh -e
+#
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+# This explorer determines the "prefix" of the --type section matching --match
+# if set, or __object_id otherwise.
+
+RS=$(printf '\036')
+NL=$(printf '\n '); NL=${NL% }
+
+squote_values() {
+ sed -e '/=".*"$/{s/="/='\''/;s/"$/'\''/}' \
+ -e "/='.*'$/"'!{s/=/='\''/;s/$/'\''/}'
+}
+count_lines() (
+ IFS=${NL?}
+ # shellcheck disable=SC2048,SC2086
+ set -f -- $*; echo $#
+)
+
+echo "${__object_id:?}" | grep -q -e '^[^.]\{1,\}\.[^.]\{1,\}$' || {
+ echo 'Section identifiers are a package and section name separated by a "." (period).' >&2
+ exit 1
+}
+
+test -s "${__object:?}/parameter/match" || {
+ # If no --match is given, we take the __object_id as the section identifier.
+ echo "${__object_id:?}"
+ exit 0
+}
+test -s "${__object:?}/parameter/type" || {
+ echo 'Parameters --match and --type must be used together.' >&2
+ exit 1
+}
+
+sect_type_param=$(cat "${__object:?}/parameter/type")
+expr "${sect_type_param}" : '[^.]\{1,\}\.[^.]\{1,\}$' >/dev/null 2>&1 || {
+ echo 'Section types are a package name and section type separated by a "." (period).' >&2
+ exit 1
+}
+package_filter=${sect_type_param%%.*}
+section_filter=${sect_type_param#*.}
+
+# Find by --match
+# NOTE: Apart from section types all values are printed in single quotes by uci show.
+match=$(head -n 1 "${__object:?}/parameter/match" | squote_values)
+
+if uci -s -N get "${__object_id:?}" >/dev/null 2>&1
+then
+ # Named section exists: ensure if --match applies to it
+ # if the "matched" option does not exist (e.g. empty section) we use the
+ # section unconditionally.
+ if match_value_is=$(uci -s -N get "${__object_id:?}.${match%%=*}" 2>/dev/null)
+ then
+ match_value_should=$(expr "${match}" : ".*='\\(.*\\)'$")
+
+ test "${match_value_is}" = "${match_value_should}" || {
+ printf 'Named section "%s" does not match --match "%s"\n' \
+ "${__object_id:?}" "${match}" >&2
+ exit 1
+ }
+ fi
+
+ echo "${__object_id:?}"
+ exit 0
+fi
+
+# No correctly named section exists already: find one to which --match applies
+regex="^${package_filter}\\.@${section_filter}\\[[0-9]\\{1,\\}\\]\\.${match%%=*}="
+
+matched_sections=$(
+ uci -s -N -d "${RS}" show "${package_filter}" 2>/dev/null \
+ | grep -e "${regex}" \
+ | while read -r _line
+ do
+ if test "${_line#*=}" = "${match#*=}"
+ then
+ echo "${_line}"
+ fi
+ done \
+ | sed -e 's/\.[^.]*=.*$//')
+
+test "$(count_lines "${matched_sections}")" -le 1 || {
+ printf 'Found multiple matching sections:\n%s\n' "${matched_sections}" >&2
+ exit 1
+}
+
+echo "${matched_sections}"
diff --git a/cdist/conf/type/__uci_section/explorer/options b/cdist/conf/type/__uci_section/explorer/options
new file mode 100644
index 00000000..e1e60668
--- /dev/null
+++ b/cdist/conf/type/__uci_section/explorer/options
@@ -0,0 +1,48 @@
+#!/bin/sh -e
+#
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+# This explorer retrieves the current options of the configuration section.
+
+RS=$(printf '\036')
+
+section=$("${__type_explorer:?}/match")
+test -n "${section}" || exit 0
+
+uci -s -N -d "${RS}" show "${section}" 2>/dev/null \
+| awk -v VSEP="${RS}" '
+ {
+ # Strip off the config and section parts
+ is_opt = sub(/^([^.]*\.){2}/, "")
+
+ if (!is_opt) {
+ # this line represents the section -> skip
+ next
+ }
+
+ if (index($0, VSEP)) {
+ # Put values each on a line, like --option and --list parameters
+ opt = substr($0, 1, index($0, "=") - 1)
+ split(substr($0, length(opt) + 2), values, VSEP)
+ for (i in values) {
+ printf "%s=%s\n", opt, values[i]
+ }
+ } else {
+ print
+ }
+ }'
diff --git a/cdist/conf/type/__uci_section/explorer/type b/cdist/conf/type/__uci_section/explorer/type
new file mode 100644
index 00000000..1675c2e0
--- /dev/null
+++ b/cdist/conf/type/__uci_section/explorer/type
@@ -0,0 +1,25 @@
+#!/bin/sh -e
+#
+# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+# This explorer retrieves the current section type.
+
+section=$("${__type_explorer:?}/match")
+test -n "${section}" || exit 0
+
+uci -s -N get "${section}" 2>/dev/null || true
diff --git a/cdist/conf/type/__uci_section/files/functions.sh b/cdist/conf/type/__uci_section/files/functions.sh
new file mode 100644
index 00000000..60cb9148
--- /dev/null
+++ b/cdist/conf/type/__uci_section/files/functions.sh
@@ -0,0 +1,59 @@
+# -*- mode: sh; indent-tabs-mode: t -*-
+
+NL=$(printf '\n '); NL=${NL% }
+
+grep_line() {
+ { shift; printf '%s\n' "$@"; } | grep -qxF "$1"
+}
+
+print_errors() {
+ awk -v prefix="${1:-Found errors:}" -v suffix="${2-}" '
+ BEGIN {
+ if (getline) {
+ print prefix
+ print
+ rc = 1
+ }
+ }
+ { print }
+ END {
+ if (rc && suffix) print suffix
+ exit rc
+ }' >&2
+}
+
+quote() {
+ for _arg
+ do
+ shift
+ if test -n "$(printf %s "${_arg}" | tr -d -c '\t\n \042-\047\050-\052\073-\077\133\\`|~' | tr -c '' '.')"
+ then
+ # needs quoting
+ set -- "$@" "$(printf "'%s'" "$(printf %s "${_arg}" | sed -e "s/'/'\\\\''/g")")"
+ else
+ set -- "$@" "${_arg}"
+ fi
+ done
+ unset _arg
+ printf '%s' "$*"
+}
+
+uci_cmd() {
+ # Usage: uci_cmd [UCI ARGUMENTS]...
+ mkdir -p "${__object:?}/files"
+ printf '%s\n' "$(quote "$@")" >>"${__object:?}/files/uci_batch.txt"
+}
+
+uci_validate_name() {
+ # like util.c uci_validate_name()
+ test -n "$*" && test -z "$(printf %s "$*" | tr -d '[:alnum:]_' | tr -c '' .)"
+}
+
+unquote_lines() {
+ sed -e '/^".*"$/{s/^"//;s/"$//}' \
+ -e '/'"^'.*'"'$/{s/'"^'"'//;s/'"'$"'//}'
+}
+
+validate_options() {
+ grep -shv -e '^[[:alnum:]_]\{1,\}=' "$@"
+}
diff --git a/cdist/conf/type/__uci_section/files/option_state.awk b/cdist/conf/type/__uci_section/files/option_state.awk
new file mode 100644
index 00000000..97cd94fb
--- /dev/null
+++ b/cdist/conf/type/__uci_section/files/option_state.awk
@@ -0,0 +1,91 @@
+# -*- mode: awk; indent-tabs-mode:t -*-
+# Usage: awk -f option_state.awk option_type option_name
+# e.g. awk -f option_state.awk option title
+# awk -f option_state.awk list entry
+
+function unquote(s) {
+ # simplified dequoting of single quoted strings
+ if (s ~ /^'.*'$/) {
+ s = substr(s, 2, length(s) - 2)
+ sub(/'\\''/, "'", s)
+ }
+ return s
+}
+
+function valueof(line) {
+ if (line !~ /^[[:alpha:]_]+=/) return 0
+ return unquote(substr(line, index(line, "=") + 1))
+}
+
+BEGIN {
+ __object = ENVIRON["__object"]
+ if (!__object) exit 1
+
+ opttype = ARGV[1]
+ optname = ARGV[2]
+
+ if (opttype !~ /^(option|list)/ || !optname) {
+ print "invalid"
+ exit (e=1)
+ }
+
+ ARGV[1] = __object "/parameter/" opttype
+ ARGV[2] = __object "/explorer/options"
+
+ state = "present"
+}
+
+NR == FNR {
+ # memoize "should" state
+ if (index($0, optname "=") == 1) {
+ should[++should_count] = valueof($0)
+ }
+
+ # go to next line (important!)
+ next
+}
+
+{
+ # compare "is" state
+ if (index($0, optname "=") != 1)
+ next
+ ++is_count
+
+ v = valueof($0)
+
+ if (v == should[is_count]) {
+ # looks good, but can't say definitely just from this line
+ } else if (is_count > should_count) {
+ # there are more "is" records than "should" -> definitely different
+ state = "different"
+ exit
+ } else {
+ # see if we can find the "is" value somewhere in "should"
+ for (i in should) {
+ if (v == should[i]) {
+ # value found -> could be rearranged
+ # FIXME: Duplicate values are not properly handled here. Do they matter?
+ state = "rearranged"
+ next
+ }
+ }
+
+ # "is" value could not be found in "should" -> definitely different
+ state = "different"
+ exit
+ }
+}
+
+END {
+ if (e) exit
+
+ if (!is_count) {
+ # no "is" values -> absent
+ state = "absent"
+ } else if (is_count < should_count) {
+ # "is" was shorter than "should" -> different
+ state = "different"
+ }
+
+ print state
+}
diff --git a/cdist/conf/type/__uci_section/files/uci_apply.sh b/cdist/conf/type/__uci_section/files/uci_apply.sh
new file mode 120000
index 00000000..4209151f
--- /dev/null
+++ b/cdist/conf/type/__uci_section/files/uci_apply.sh
@@ -0,0 +1 @@
+../../__uci/files/uci_apply.sh
\ No newline at end of file
diff --git a/cdist/conf/type/__uci_section/gencode-remote b/cdist/conf/type/__uci_section/gencode-remote
new file mode 100755
index 00000000..50fdfa4e
--- /dev/null
+++ b/cdist/conf/type/__uci_section/gencode-remote
@@ -0,0 +1,174 @@
+#!/bin/sh -e
+#
+# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+# shellcheck source=cdist/conf/type/__uci_section/files/functions.sh
+. "${__type:?}/files/functions.sh"
+
+
+section=$(cat "${__object:?}/explorer/match")
+
+state_is=$(test -s "${__object:?}/explorer/type" && echo present || echo absent)
+state_should=$(cat "${__object:?}/parameter/state")
+
+case $state_should
+in
+ (present)
+ test -f "${__object:?}/parameter/type" || {
+ echo 'Parameter --type is required.' >&2
+ exit 1
+ }
+
+ type_is=$(cat "${__object:?}/explorer/type")
+ type_should=$(cat "${__object:?}/parameter/type")
+
+ if test -n "${type_is}"
+ then
+ sect_type=${type_is}
+ else
+ sect_type=${type_should##*.}
+ fi
+
+ if test -z "${section}"
+ then
+ # No section exists and --match was used.
+ # So we generate a new section identifier from $__object_id.
+ case ${__object_id:?}
+ in
+ (*.*) section=${__object_id:?} ;;
+ (*) section="${type_should%%.*}.${__object_id:?}" ;;
+ esac
+ fi
+
+ # Collect option names
+ if test -f "${__object:?}/parameter/list"
+ then
+ listnames_should=$(
+ sed -e 's/=.*$//' "${__object:?}/parameter/list" | sort -u)
+ fi
+
+ if test -f "${__object:?}/parameter/option"
+ then
+ optnames_should=$(
+ sed -e 's/=.*$//' "${__object:?}/parameter/option" | sort -u)
+ fi
+
+ # Make sure the section itself is present
+ if test "${state_is}" = absent \
+ || test "${type_is}" != "${type_should#*.}"
+ then
+ printf 'set %s\n' "${section}" >>"${__messages_out:?}"
+ # shellcheck disable=SC2140
+ uci_cmd set "${section}"="${sect_type}"
+ fi
+
+ # Delete options/lists not in "should"
+ sed -e 's/=.*$//' "${__object:?}/explorer/options" \
+ | while read -r _optname
+ do
+ grep_line "${_optname}" "${listnames_should}" "${optnames_should}" || {
+ printf 'delete %s\n' "${section}.${_optname}" >>"${__messages_out:?}"
+ uci_cmd delete "${section}.${_optname}"
+ } &2
+ exit 1
+ }
+
+ # Set "should" options
+ echo "${optnames_should}" \
+ | grep -e . \
+ | while read -r _optname
+ do
+ _opt_state=$(awk -f "${__type:?}/files/option_state.awk" option "${_optname}") \
+ || opt_proc_error "${_optname}"
+ case ${_opt_state}
+ in
+ (invalid)
+ opt_proc_error "${_optname}"
+ ;;
+ (present)
+ ;;
+ (*)
+ printf 'set %s\n' "${section}.${_optname}" >>"${__messages_out:?}"
+
+ # shellcheck disable=SC2140
+ uci_cmd set "${section}.${_optname}"="$(
+ grep -e "^${_optname}=" "${__object:?}/parameter/option" \
+ | sed -e 's/^.*=//' \
+ | unquote_lines \
+ | head -n 1)"
+ ;;
+ esac
+ done
+
+ echo "${listnames_should}" \
+ | grep -e . \
+ | while read -r _optname
+ do
+ _list_state=$(awk -f "${__type:?}/files/option_state.awk" list "${_optname}") \
+ || opt_proc_error "${_optname}"
+ case ${_list_state}
+ in
+ (invalid)
+ opt_proc_error "${_optname}"
+ ;;
+ (present)
+ ;;
+ (*)
+ printf 'set_list %s\n' "${section}.${_optname}" >>"${__messages_out:?}"
+
+ if test "${_list_state}" != absent
+ then
+ uci_cmd delete "${section}.${_optname}"
+ fi
+
+ grep "^${_optname}=" "${__object:?}/parameter/list" \
+ | sed -e 's/^.*=//' \
+ | unquote_lines \
+ | while read -r _value
+ do
+ # shellcheck disable=SC2140
+ uci_cmd add_list "${section}.${_optname}"="${_value}"
+ done
+ ;;
+ esac
+ done
+ ;;
+ (absent)
+ if test "${state_is}" = absent
+ then
+ # if explorer found no section there is nothing to delete
+ exit 0
+ fi
+
+ printf 'delete %s\n' "${section}" >>"${__messages_out:?}"
+ uci_cmd delete "${section}"
+ ;;
+esac
+
+if test -s "${__object:?}/files/uci_batch.txt"
+then
+ cat "${__type:?}/files/uci_apply.sh"
+ printf "uci_apply <<'EOF'\n"
+ cat "${__object:?}/files/uci_batch.txt"
+ printf '\nEOF\n'
+fi
diff --git a/cdist/conf/type/__uci_section/man.rst b/cdist/conf/type/__uci_section/man.rst
new file mode 100644
index 00000000..a0ab78e8
--- /dev/null
+++ b/cdist/conf/type/__uci_section/man.rst
@@ -0,0 +1,119 @@
+cdist-type__uci_section(7)
+==========================
+
+NAME
+----
+cdist-type__uci_section - Manage configuration sections in UCI
+
+
+DESCRIPTION
+-----------
+This cdist type can be used to replace whole configuration sections in OpenWrt's
+Unified Configuration Interface (UCI) system.
+It can be thought of as syntactic sugar for :strong:`cdist-type__uci`\ (7),
+as this type will generate the required `__uci` objects to make the section
+contain exactly the options specified using ``--option``.
+
+Since many default UCI sections are unnamed, this type allows to find the
+matching section by one of its options using the ``--match`` parameter.
+
+**NOTE:** Options already present on the target and not listed in ``--option``
+or ``--list`` will be deleted.
+
+
+REQUIRED PARAMETERS
+-------------------
+None.
+
+
+OPTIONAL PARAMETERS
+-------------------
+list
+ An option that is part of a list and should be present in the section (as
+ part of a list). Lists with multiple options can be expressed by using the
+ same ``