Merge juju-lint:update-snap into juju-lint:master
- Git
- lp:juju-lint
- update-snap
- Merge into master
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Eric Chen | ||||
Approved revision: | 57cd0951d0e5e453d304291142a69dabff5c560e | ||||
Merged at revision: | 14ad2b7c55802329a6be651caa8a79773f0b3c2d | ||||
Proposed branch: | juju-lint:update-snap | ||||
Merge into: | juju-lint:master | ||||
Diff against target: |
808 lines (+138/-279) 13 files modified
Makefile (+4/-1) dev/null (+0/-199) jujulint/check_spaces.py (+12/-7) jujulint/cli.py (+7/-5) jujulint/cloud.py (+7/-5) jujulint/config.py (+2/-1) jujulint/lint.py (+41/-27) jujulint/logging.py (+2/-1) snap/snapcraft.yaml (+3/-3) tests/conftest.py (+3/-2) tests/requirements.txt (+3/-1) tests/test_jujulint.py (+28/-18) tox.ini (+26/-9) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Eric Chen | Approve | ||
Martin Kalcok (community) | Approve | ||
Review via email: mp+424672@code.launchpad.net |
Commit message
update the snap
- added black to format the source code of the project and
applied it into the code.
- added isort to order imports and applied it into the code.
- changed python version from 3.6(deprecated) to 3.8.
- changed base to core20.
- added /snap/bin to $PATH
- removed Pipfile, Pipfile.lock and .python-version that is not
been used anymore.
Description of the change
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Martin Kalcok (martin-kalcok) wrote : | # |
I'm very much a fan of adding `black` and enforcing stricter rules. I have just few notes:
* I think that adding "black --check <targets>" to "lint" tox environment will help with CI integration
* Another useful tool is `isort` that manages format/ordering of python imports
If you want to check out example of other project that has these tools set up, you can check juju-verify [1].
Regarding the changes to snapcraft.yaml: Does juju-lint always depend on juju (snap)? If yes, maybe we should add it as `stage-snaps` [2]?
---
[1] https:/
[2] https:/
Garrett Neugent (thogarre) wrote (last edit ): | # |
This is not a blocker in anyway, but one other tool that is worth consideration is pre-commit. It can help enforce linting, unit tests, and other checks before engineers submit a new MR. Juju-backup-all has this implemented [0] if you think it's worth adding here. (Note that if you do consider adding this, you'll likely need to use v22.3.0 of black, not 21.8b0 as currently in juju-backup-all's config [1]).
[0] https:/
[1] https:/
Gabriel Cocenza (gabrielcocenza) wrote : | # |
Hi Martin. Thanks for your review. I've added isort and applied it in the project.
Regarding adding juju snap into `stage-snaps` I guess it's not necessary. I don't see a huge gain of having the binaries of juju snap into juju-lint. Maybe we should consider using pythonlibjuju instead of making subprocess calls to deal with juju?
Gabriel Cocenza (gabrielcocenza) wrote : | # |
Thanks for this suggestion Garret. I think it will be a very good thing for the project. I've added a bug[0] to add pre-commit in a future MR.
Robert Gildein (rgildein) wrote : | # |
I briefly look at this and how three questions/
- How do we use Pipenv here? Do you want to create a requirements.txt file? If
so, what do you say to add `pipenv lock -> requirements.txt` to tox.ini to
generate requirements from Pipfile? There is also simply way to check
requirements in Pipfile.lock and requirements.txt
`pipenv lock -r | diff requirements.txt -`
- If the above applies, we can use pipenv --dev to have testing requirements
there.
- You can add `--cov-fail-under <current-
function will need to have unittests.
Martin Kalcok (martin-kalcok) wrote : | # |
Thanks for including my suggestions. Just two more nitpicks
* you have misaligned indentations in the pytest command in `unit` tox environment
* shouldn't the isort line length config (in tox.ini) match the line length of `black` which is 88?
Gabriel Cocenza (gabrielcocenza) wrote : | # |
Thanks Martin. The problem in tox was mixed spaces and tabs. Regarding isort, you are right. I added the black profile [0] that will do the job.
Regarding the Pipfile(s), it looks like they are not been used (maybe just for those that use pipenv, so I removed then. The MR that added then it's this one [1].
[0] https:/
[1] https:/
Martin Kalcok (martin-kalcok) wrote : | # |
LGTM, thanks
Eric Chen (eric-chen) wrote : | # |
LGTM, please go ahead.
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Change successfully merged at revision 14ad2b7c5580232
Preview Diff
1 | diff --git a/.python-version b/.python-version | |||
2 | 0 | deleted file mode 100644 | 0 | deleted file mode 100644 |
3 | index a08ffae..0000000 | |||
4 | --- a/.python-version | |||
5 | +++ /dev/null | |||
6 | @@ -1 +0,0 @@ | |||
7 | 1 | 3.8.2 | ||
8 | diff --git a/Makefile b/Makefile | |||
9 | index 70a2762..85be332 100644 | |||
10 | --- a/Makefile | |||
11 | +++ b/Makefile | |||
12 | @@ -1,5 +1,5 @@ | |||
13 | 1 | lint: | 1 | lint: |
15 | 2 | tox -e py3-lint | 2 | tox -e lintverbose |
16 | 3 | 3 | ||
17 | 4 | dch: | 4 | dch: |
18 | 5 | if ! which gbp > /dev/null; then sudo apt-get install -y git-buildpackage; fi | 5 | if ! which gbp > /dev/null; then sudo apt-get install -y git-buildpackage; fi |
19 | @@ -11,6 +11,9 @@ deb-src: | |||
20 | 11 | test: | 11 | test: |
21 | 12 | tox -e unit | 12 | tox -e unit |
22 | 13 | 13 | ||
23 | 14 | format-code: | ||
24 | 15 | tox -e format-code | ||
25 | 16 | |||
26 | 14 | build: | 17 | build: |
27 | 15 | snapcraft --use-lxd --debug | 18 | snapcraft --use-lxd --debug |
28 | 16 | 19 | ||
29 | diff --git a/Pipfile b/Pipfile | |||
30 | 17 | deleted file mode 100644 | 20 | deleted file mode 100644 |
31 | index d2acf80..0000000 | |||
32 | --- a/Pipfile | |||
33 | +++ /dev/null | |||
34 | @@ -1,17 +0,0 @@ | |||
35 | 1 | [[source]] | ||
36 | 2 | url = "https://pypi.python.org/simple" | ||
37 | 3 | verify_ssl = true | ||
38 | 4 | name = "pypi" | ||
39 | 5 | |||
40 | 6 | [packages] | ||
41 | 7 | attrs = ">=18.1.0" | ||
42 | 8 | colorlog = ">=4.1.0" | ||
43 | 9 | confuse = ">=1.1.0" | ||
44 | 10 | fabric2 = ">=2.5.0" | ||
45 | 11 | setuptools = ">=46.1.3" | ||
46 | 12 | PyYAML = ">=5.3.1" | ||
47 | 13 | |||
48 | 14 | [dev-packages] | ||
49 | 15 | |||
50 | 16 | [requires] | ||
51 | 17 | python_version = "3.6" | ||
52 | diff --git a/Pipfile.lock b/Pipfile.lock | |||
53 | 18 | deleted file mode 100644 | 0 | deleted file mode 100644 |
54 | index c43f7b6..0000000 | |||
55 | --- a/Pipfile.lock | |||
56 | +++ /dev/null | |||
57 | @@ -1,199 +0,0 @@ | |||
58 | 1 | { | ||
59 | 2 | "_meta": { | ||
60 | 3 | "hash": { | ||
61 | 4 | "sha256": "e1534fcda4a023c68f70df4e53b34a9bafcd9ec48c4f8d93e20dda25b3c43cd4" | ||
62 | 5 | }, | ||
63 | 6 | "pipfile-spec": 6, | ||
64 | 7 | "requires": { | ||
65 | 8 | "python_version": "3.6" | ||
66 | 9 | }, | ||
67 | 10 | "sources": [ | ||
68 | 11 | { | ||
69 | 12 | "name": "pypi", | ||
70 | 13 | "url": "https://pypi.python.org/simple", | ||
71 | 14 | "verify_ssl": true | ||
72 | 15 | } | ||
73 | 16 | ] | ||
74 | 17 | }, | ||
75 | 18 | "default": { | ||
76 | 19 | "attrs": { | ||
77 | 20 | "hashes": [ | ||
78 | 21 | "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", | ||
79 | 22 | "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" | ||
80 | 23 | ], | ||
81 | 24 | "index": "pypi", | ||
82 | 25 | "version": "==19.3.0" | ||
83 | 26 | }, | ||
84 | 27 | "bcrypt": { | ||
85 | 28 | "hashes": [ | ||
86 | 29 | "sha256:0258f143f3de96b7c14f762c770f5fc56ccd72f8a1857a451c1cd9a655d9ac89", | ||
87 | 30 | "sha256:0b0069c752ec14172c5f78208f1863d7ad6755a6fae6fe76ec2c80d13be41e42", | ||
88 | 31 | "sha256:19a4b72a6ae5bb467fea018b825f0a7d917789bcfe893e53f15c92805d187294", | ||
89 | 32 | "sha256:5432dd7b34107ae8ed6c10a71b4397f1c853bd39a4d6ffa7e35f40584cffd161", | ||
90 | 33 | "sha256:6305557019906466fc42dbc53b46da004e72fd7a551c044a827e572c82191752", | ||
91 | 34 | "sha256:69361315039878c0680be456640f8705d76cb4a3a3fe1e057e0f261b74be4b31", | ||
92 | 35 | "sha256:6fe49a60b25b584e2f4ef175b29d3a83ba63b3a4df1b4c0605b826668d1b6be5", | ||
93 | 36 | "sha256:74a015102e877d0ccd02cdeaa18b32aa7273746914a6c5d0456dd442cb65b99c", | ||
94 | 37 | "sha256:763669a367869786bb4c8fcf731f4175775a5b43f070f50f46f0b59da45375d0", | ||
95 | 38 | "sha256:8b10acde4e1919d6015e1df86d4c217d3b5b01bb7744c36113ea43d529e1c3de", | ||
96 | 39 | "sha256:9fe92406c857409b70a38729dbdf6578caf9228de0aef5bc44f859ffe971a39e", | ||
97 | 40 | "sha256:a190f2a5dbbdbff4b74e3103cef44344bc30e61255beb27310e2aec407766052", | ||
98 | 41 | "sha256:a595c12c618119255c90deb4b046e1ca3bcfad64667c43d1166f2b04bc72db09", | ||
99 | 42 | "sha256:c9457fa5c121e94a58d6505cadca8bed1c64444b83b3204928a866ca2e599105", | ||
100 | 43 | "sha256:cb93f6b2ab0f6853550b74e051d297c27a638719753eb9ff66d1e4072be67133", | ||
101 | 44 | "sha256:ce4e4f0deb51d38b1611a27f330426154f2980e66582dc5f438aad38b5f24fc1", | ||
102 | 45 | "sha256:d7bdc26475679dd073ba0ed2766445bb5b20ca4793ca0db32b399dccc6bc84b7", | ||
103 | 46 | "sha256:ff032765bb8716d9387fd5376d987a937254b0619eff0972779515b5c98820bc" | ||
104 | 47 | ], | ||
105 | 48 | "version": "==3.1.7" | ||
106 | 49 | }, | ||
107 | 50 | "cffi": { | ||
108 | 51 | "hashes": [ | ||
109 | 52 | "sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff", | ||
110 | 53 | "sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b", | ||
111 | 54 | "sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac", | ||
112 | 55 | "sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0", | ||
113 | 56 | "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384", | ||
114 | 57 | "sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26", | ||
115 | 58 | "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6", | ||
116 | 59 | "sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b", | ||
117 | 60 | "sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e", | ||
118 | 61 | "sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd", | ||
119 | 62 | "sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2", | ||
120 | 63 | "sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66", | ||
121 | 64 | "sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc", | ||
122 | 65 | "sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8", | ||
123 | 66 | "sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55", | ||
124 | 67 | "sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4", | ||
125 | 68 | "sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5", | ||
126 | 69 | "sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d", | ||
127 | 70 | "sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78", | ||
128 | 71 | "sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa", | ||
129 | 72 | "sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793", | ||
130 | 73 | "sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f", | ||
131 | 74 | "sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a", | ||
132 | 75 | "sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f", | ||
133 | 76 | "sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30", | ||
134 | 77 | "sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f", | ||
135 | 78 | "sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3", | ||
136 | 79 | "sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c" | ||
137 | 80 | ], | ||
138 | 81 | "version": "==1.14.0" | ||
139 | 82 | }, | ||
140 | 83 | "colorlog": { | ||
141 | 84 | "hashes": [ | ||
142 | 85 | "sha256:30aaef5ab2a1873dec5da38fd6ba568fa761c9fa10b40241027fa3edea47f3d2", | ||
143 | 86 | "sha256:732c191ebbe9a353ec160d043d02c64ddef9028de8caae4cfa8bd49b6afed53e" | ||
144 | 87 | ], | ||
145 | 88 | "index": "pypi", | ||
146 | 89 | "version": "==4.1.0" | ||
147 | 90 | }, | ||
148 | 91 | "confuse": { | ||
149 | 92 | "hashes": [ | ||
150 | 93 | "sha256:adc1979ea6f4c0dd3d6fe06020c189843a649082ab8f6fb54db16f4ac5e5e1da" | ||
151 | 94 | ], | ||
152 | 95 | "index": "pypi", | ||
153 | 96 | "version": "==1.1.0" | ||
154 | 97 | }, | ||
155 | 98 | "cryptography": { | ||
156 | 99 | "hashes": [ | ||
157 | 100 | "sha256:091d31c42f444c6f519485ed528d8b451d1a0c7bf30e8ca583a0cac44b8a0df6", | ||
158 | 101 | "sha256:18452582a3c85b96014b45686af264563e3e5d99d226589f057ace56196ec78b", | ||
159 | 102 | "sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5", | ||
160 | 103 | "sha256:1e4014639d3d73fbc5ceff206049c5a9a849cefd106a49fa7aaaa25cc0ce35cf", | ||
161 | 104 | "sha256:22e91636a51170df0ae4dcbd250d318fd28c9f491c4e50b625a49964b24fe46e", | ||
162 | 105 | "sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b", | ||
163 | 106 | "sha256:651448cd2e3a6bc2bb76c3663785133c40d5e1a8c1a9c5429e4354201c6024ae", | ||
164 | 107 | "sha256:726086c17f94747cedbee6efa77e99ae170caebeb1116353c6cf0ab67ea6829b", | ||
165 | 108 | "sha256:844a76bc04472e5135b909da6aed84360f522ff5dfa47f93e3dd2a0b84a89fa0", | ||
166 | 109 | "sha256:88c881dd5a147e08d1bdcf2315c04972381d026cdb803325c03fe2b4a8ed858b", | ||
167 | 110 | "sha256:96c080ae7118c10fcbe6229ab43eb8b090fccd31a09ef55f83f690d1ef619a1d", | ||
168 | 111 | "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229", | ||
169 | 112 | "sha256:bb1f0281887d89617b4c68e8db9a2c42b9efebf2702a3c5bf70599421a8623e3", | ||
170 | 113 | "sha256:c447cf087cf2dbddc1add6987bbe2f767ed5317adb2d08af940db517dd704365", | ||
171 | 114 | "sha256:c4fd17d92e9d55b84707f4fd09992081ba872d1a0c610c109c18e062e06a2e55", | ||
172 | 115 | "sha256:d0d5aeaedd29be304848f1c5059074a740fa9f6f26b84c5b63e8b29e73dfc270", | ||
173 | 116 | "sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e", | ||
174 | 117 | "sha256:e993468c859d084d5579e2ebee101de8f5a27ce8e2159959b6673b418fd8c785", | ||
175 | 118 | "sha256:f118a95c7480f5be0df8afeb9a11bd199aa20afab7a96bcf20409b411a3a85f0" | ||
176 | 119 | ], | ||
177 | 120 | "version": "==2.9.2" | ||
178 | 121 | }, | ||
179 | 122 | "fabric2": { | ||
180 | 123 | "hashes": [ | ||
181 | 124 | "sha256:29edd7848420df589a49743394a0ae6874ccb6a9fe6413c0076d42cc290dcad6", | ||
182 | 125 | "sha256:8838d9641fd4e95bfc2568aa16fc683a600de860ac52a1dc9675a4db3c6cef7c" | ||
183 | 126 | ], | ||
184 | 127 | "index": "pypi", | ||
185 | 128 | "version": "==2.5.0" | ||
186 | 129 | }, | ||
187 | 130 | "invoke": { | ||
188 | 131 | "hashes": [ | ||
189 | 132 | "sha256:87b3ef9d72a1667e104f89b159eaf8a514dbf2f3576885b2bbdefe74c3fb2132", | ||
190 | 133 | "sha256:93e12876d88130c8e0d7fd6618dd5387d6b36da55ad541481dfa5e001656f134", | ||
191 | 134 | "sha256:de3f23bfe669e3db1085789fd859eb8ca8e0c5d9c20811e2407fa042e8a5e15d" | ||
192 | 135 | ], | ||
193 | 136 | "version": "==1.4.1" | ||
194 | 137 | }, | ||
195 | 138 | "paramiko": { | ||
196 | 139 | "hashes": [ | ||
197 | 140 | "sha256:920492895db8013f6cc0179293147f830b8c7b21fdfc839b6bad760c27459d9f", | ||
198 | 141 | "sha256:9c980875fa4d2cb751604664e9a2d0f69096643f5be4db1b99599fe114a97b2f" | ||
199 | 142 | ], | ||
200 | 143 | "version": "==2.7.1" | ||
201 | 144 | }, | ||
202 | 145 | "pycparser": { | ||
203 | 146 | "hashes": [ | ||
204 | 147 | "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", | ||
205 | 148 | "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" | ||
206 | 149 | ], | ||
207 | 150 | "version": "==2.20" | ||
208 | 151 | }, | ||
209 | 152 | "pynacl": { | ||
210 | 153 | "hashes": [ | ||
211 | 154 | "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4", | ||
212 | 155 | "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4", | ||
213 | 156 | "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574", | ||
214 | 157 | "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d", | ||
215 | 158 | "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25", | ||
216 | 159 | "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f", | ||
217 | 160 | "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505", | ||
218 | 161 | "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122", | ||
219 | 162 | "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7", | ||
220 | 163 | "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420", | ||
221 | 164 | "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f", | ||
222 | 165 | "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96", | ||
223 | 166 | "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6", | ||
224 | 167 | "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514", | ||
225 | 168 | "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff", | ||
226 | 169 | "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80" | ||
227 | 170 | ], | ||
228 | 171 | "version": "==1.4.0" | ||
229 | 172 | }, | ||
230 | 173 | "pyyaml": { | ||
231 | 174 | "hashes": [ | ||
232 | 175 | "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", | ||
233 | 176 | "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76", | ||
234 | 177 | "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2", | ||
235 | 178 | "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648", | ||
236 | 179 | "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf", | ||
237 | 180 | "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f", | ||
238 | 181 | "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2", | ||
239 | 182 | "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee", | ||
240 | 183 | "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d", | ||
241 | 184 | "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c", | ||
242 | 185 | "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a" | ||
243 | 186 | ], | ||
244 | 187 | "index": "pypi", | ||
245 | 188 | "version": "==5.3.1" | ||
246 | 189 | }, | ||
247 | 190 | "six": { | ||
248 | 191 | "hashes": [ | ||
249 | 192 | "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", | ||
250 | 193 | "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" | ||
251 | 194 | ], | ||
252 | 195 | "version": "==1.15.0" | ||
253 | 196 | } | ||
254 | 197 | }, | ||
255 | 198 | "develop": {} | ||
256 | 199 | } | ||
257 | diff --git a/jujulint/check_spaces.py b/jujulint/check_spaces.py | |||
258 | index 47c0c63..17b8d8c 100644 | |||
259 | --- a/jujulint/check_spaces.py | |||
260 | +++ b/jujulint/check_spaces.py | |||
261 | @@ -20,7 +20,9 @@ class Relation: | |||
262 | 20 | While Juju does define separate provider and requirer roles, we'll ignore | 20 | While Juju does define separate provider and requirer roles, we'll ignore |
263 | 21 | those here. | 21 | those here. |
264 | 22 | """ | 22 | """ |
266 | 23 | return set([self.endpoint1, self.endpoint2]) == set([other.endpoint1, other.endpoint2]) | 23 | return set([self.endpoint1, self.endpoint2]) == set( |
267 | 24 | [other.endpoint1, other.endpoint2] | ||
268 | 25 | ) | ||
269 | 24 | 26 | ||
270 | 25 | @property | 27 | @property |
271 | 26 | def endpoints(self): | 28 | def endpoints(self): |
272 | @@ -45,7 +47,8 @@ class SpaceMismatch: | |||
273 | 45 | def __str__(self): | 47 | def __str__(self): |
274 | 46 | """Stringify the object.""" | 48 | """Stringify the object.""" |
275 | 47 | return "SpaceMismatch({} (space {}) != {} (space {}))".format( | 49 | return "SpaceMismatch({} (space {}) != {} (space {}))".format( |
277 | 48 | self.endpoint1, self.space1, self.endpoint2, self.space2) | 50 | self.endpoint1, self.space1, self.endpoint2, self.space2 |
278 | 51 | ) | ||
279 | 49 | 52 | ||
280 | 50 | @property | 53 | @property |
281 | 51 | def relation(self): | 54 | def relation(self): |
282 | @@ -54,8 +57,8 @@ class SpaceMismatch: | |||
283 | 54 | 57 | ||
284 | 55 | def get_charm_relation(self, app_to_charm_map): | 58 | def get_charm_relation(self, app_to_charm_map): |
285 | 56 | """Return a relation object, mapping applications to charms.""" | 59 | """Return a relation object, mapping applications to charms.""" |
288 | 57 | app1, endpoint1 = self.endpoint1.split(':') | 60 | app1, endpoint1 = self.endpoint1.split(":") |
289 | 58 | app2, endpoint2 = self.endpoint2.split(':') | 61 | app2, endpoint2 = self.endpoint2.split(":") |
290 | 59 | charm1 = app_to_charm_map[app1] | 62 | charm1 = app_to_charm_map[app1] |
291 | 60 | charm2 = app_to_charm_map[app2] | 63 | charm2 = app_to_charm_map[app2] |
292 | 61 | return Relation(":".join([charm1, endpoint1]), ":".join([charm2, endpoint2])) | 64 | return Relation(":".join([charm1, endpoint1]), ":".join([charm2, endpoint2])) |
293 | @@ -92,7 +95,9 @@ def find_space_mismatches(parsed_yaml, debug=False): | |||
294 | 92 | space1 = get_relation_space(relation.endpoint1, app_spaces) | 95 | space1 = get_relation_space(relation.endpoint1, app_spaces) |
295 | 93 | space2 = get_relation_space(relation.endpoint2, app_spaces) | 96 | space2 = get_relation_space(relation.endpoint2, app_spaces) |
296 | 94 | if space1 != space2: | 97 | if space1 != space2: |
298 | 95 | mismatch = SpaceMismatch(relation.endpoint1, space1, relation.endpoint2, space2) | 98 | mismatch = SpaceMismatch( |
299 | 99 | relation.endpoint1, space1, relation.endpoint2, space2 | ||
300 | 100 | ) | ||
301 | 96 | mismatches.append(mismatch) | 101 | mismatches.append(mismatch) |
302 | 97 | 102 | ||
303 | 98 | if debug: | 103 | if debug: |
304 | @@ -112,7 +117,7 @@ def get_application_spaces(application_list, parsed_yaml): | |||
305 | 112 | app_spaces = {} | 117 | app_spaces = {} |
306 | 113 | for app in application_list: | 118 | for app in application_list: |
307 | 114 | app_spaces[app] = {} | 119 | app_spaces[app] = {} |
309 | 115 | bindings = parsed_yaml["applications"][app]['bindings'] | 120 | bindings = parsed_yaml["applications"][app]["bindings"] |
310 | 116 | for name, value in bindings.items(): | 121 | for name, value in bindings.items(): |
311 | 117 | app_spaces[app][name] = value | 122 | app_spaces[app][name] = value |
312 | 118 | return app_spaces | 123 | return app_spaces |
313 | @@ -129,7 +134,7 @@ def get_application_relations(parsed_yaml): | |||
314 | 129 | 134 | ||
315 | 130 | def get_relation_space(endpoint, app_spaces): | 135 | def get_relation_space(endpoint, app_spaces): |
316 | 131 | """Get space for specified app and service.""" | 136 | """Get space for specified app and service.""" |
318 | 132 | app, service = endpoint.split(':') | 137 | app, service = endpoint.split(":") |
319 | 133 | if app in app_spaces: | 138 | if app in app_spaces: |
320 | 134 | if service not in app_spaces[app]: | 139 | if service not in app_spaces[app]: |
321 | 135 | return app_spaces[app][""] | 140 | return app_spaces[app][""] |
322 | diff --git a/jujulint/cli.py b/jujulint/cli.py | |||
323 | index af5190a..2d76832 100755 | |||
324 | --- a/jujulint/cli.py | |||
325 | +++ b/jujulint/cli.py | |||
326 | @@ -17,15 +17,17 @@ | |||
327 | 17 | # You should have received a copy of the GNU General Public License | 17 | # You should have received a copy of the GNU General Public License |
328 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
329 | 19 | """Main entrypoint for the juju-lint CLI.""" | 19 | """Main entrypoint for the juju-lint CLI.""" |
330 | 20 | from jujulint.config import Config | ||
331 | 21 | from jujulint.lint import Linter | ||
332 | 22 | from jujulint.logging import Logger | ||
333 | 23 | from jujulint.openstack import OpenStack | ||
334 | 24 | import logging | 20 | import logging |
335 | 25 | import os.path | 21 | import os.path |
336 | 22 | import sys | ||
337 | 23 | |||
338 | 26 | import pkg_resources | 24 | import pkg_resources |
339 | 27 | import yaml | 25 | import yaml |
341 | 28 | import sys | 26 | |
342 | 27 | from jujulint.config import Config | ||
343 | 28 | from jujulint.lint import Linter | ||
344 | 29 | from jujulint.logging import Logger | ||
345 | 30 | from jujulint.openstack import OpenStack | ||
346 | 29 | 31 | ||
347 | 30 | 32 | ||
348 | 31 | class Cli: | 33 | class Cli: |
349 | diff --git a/jujulint/cloud.py b/jujulint/cloud.py | |||
350 | index b3cde24..3382e3b 100644 | |||
351 | --- a/jujulint/cloud.py | |||
352 | +++ b/jujulint/cloud.py | |||
353 | @@ -33,13 +33,15 @@ Todo: | |||
354 | 33 | * Add function to run command on a unit, via fabric and jump host if configured | 33 | * Add function to run command on a unit, via fabric and jump host if configured |
355 | 34 | 34 | ||
356 | 35 | """ | 35 | """ |
357 | 36 | from fabric2 import Connection, Config | ||
358 | 37 | from paramiko.ssh_exception import SSHException | ||
359 | 38 | from jujulint.logging import Logger | ||
360 | 39 | from jujulint.lint import Linter | ||
361 | 40 | from subprocess import check_output | ||
362 | 41 | import socket | 36 | import socket |
363 | 37 | from subprocess import check_output | ||
364 | 38 | |||
365 | 42 | import yaml | 39 | import yaml |
366 | 40 | from fabric2 import Config, Connection | ||
367 | 41 | from paramiko.ssh_exception import SSHException | ||
368 | 42 | |||
369 | 43 | from jujulint.lint import Linter | ||
370 | 44 | from jujulint.logging import Logger | ||
371 | 43 | 45 | ||
372 | 44 | 46 | ||
373 | 45 | class Cloud: | 47 | class Cloud: |
374 | diff --git a/jujulint/config.py b/jujulint/config.py | |||
375 | index 1173187..d87677a 100644 | |||
376 | --- a/jujulint/config.py | |||
377 | +++ b/jujulint/config.py | |||
378 | @@ -18,9 +18,10 @@ | |||
379 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
380 | 19 | """Config handling routines.""" | 19 | """Config handling routines.""" |
381 | 20 | 20 | ||
382 | 21 | from confuse import Configuration | ||
383 | 22 | from argparse import ArgumentParser | 21 | from argparse import ArgumentParser |
384 | 23 | 22 | ||
385 | 23 | from confuse import Configuration | ||
386 | 24 | |||
387 | 24 | 25 | ||
388 | 25 | class Config(Configuration): | 26 | class Config(Configuration): |
389 | 26 | """Helper class for holding parsed config, extending confuse's BaseConfiguraion class.""" | 27 | """Helper class for holding parsed config, extending confuse's BaseConfiguraion class.""" |
390 | diff --git a/jujulint/lint.py b/jujulint/lint.py | |||
391 | index 404e8b8..e908b7a 100755 | |||
392 | --- a/jujulint/lint.py | |||
393 | +++ b/jujulint/lint.py | |||
394 | @@ -19,24 +19,23 @@ | |||
395 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
396 | 20 | """Lint operations and rule processing engine.""" | 20 | """Lint operations and rule processing engine.""" |
397 | 21 | import collections | 21 | import collections |
398 | 22 | from datetime import datetime, timezone | ||
399 | 23 | import json | 22 | import json |
400 | 24 | import logging | 23 | import logging |
401 | 25 | import os.path | 24 | import os.path |
402 | 26 | import pprint | 25 | import pprint |
403 | 27 | import re | 26 | import re |
404 | 28 | import traceback | 27 | import traceback |
405 | 28 | from datetime import datetime, timezone | ||
406 | 29 | 29 | ||
407 | 30 | import yaml | ||
408 | 31 | |||
409 | 32 | from attr import attrs, attrib | ||
410 | 33 | import attr | 30 | import attr |
411 | 34 | import dateutil.parser | 31 | import dateutil.parser |
412 | 32 | import yaml | ||
413 | 33 | from attr import attrib, attrs | ||
414 | 35 | from dateutil import relativedelta | 34 | from dateutil import relativedelta |
415 | 36 | 35 | ||
416 | 37 | import jujulint.util as utils | 36 | import jujulint.util as utils |
417 | 37 | from jujulint.check_spaces import Relation, find_space_mismatches | ||
418 | 38 | from jujulint.logging import Logger | 38 | from jujulint.logging import Logger |
419 | 39 | from jujulint.check_spaces import find_space_mismatches, Relation | ||
420 | 40 | 39 | ||
421 | 41 | VALID_CONFIG_CHECKS = ("isset", "eq", "neq", "gte") | 40 | VALID_CONFIG_CHECKS = ("isset", "eq", "neq", "gte") |
422 | 42 | 41 | ||
423 | @@ -195,7 +194,7 @@ class Linter: | |||
424 | 195 | if val[-1].lower() == val[-1]: | 194 | if val[-1].lower() == val[-1]: |
425 | 196 | quotient = 1000 | 195 | quotient = 1000 |
426 | 197 | 196 | ||
428 | 198 | conv = {"g": quotient ** 3, "m": quotient ** 2, "k": quotient} | 197 | conv = {"g": quotient**3, "m": quotient**2, "k": quotient} |
429 | 199 | 198 | ||
430 | 200 | return _int * conv[val[-1].lower()] | 199 | return _int * conv[val[-1].lower()] |
431 | 201 | 200 | ||
432 | @@ -684,21 +683,39 @@ class Linter: | |||
433 | 684 | enforce_endpoints = space_checks.get("enforce endpoints", []) | 683 | enforce_endpoints = space_checks.get("enforce endpoints", []) |
434 | 685 | enforce_relations = [ | 684 | enforce_relations = [ |
435 | 686 | Relation(*relation) | 685 | Relation(*relation) |
437 | 687 | for relation in space_checks.get("enforce relations", [])] | 686 | for relation in space_checks.get("enforce relations", []) |
438 | 687 | ] | ||
439 | 688 | ignore_endpoints = space_checks.get("ignore endpoints", []) | 688 | ignore_endpoints = space_checks.get("ignore endpoints", []) |
440 | 689 | ignore_relations = [ | 689 | ignore_relations = [ |
443 | 690 | Relation(*relation) | 690 | Relation(*relation) for relation in space_checks.get("ignore relations", []) |
444 | 691 | for relation in space_checks.get("ignore relations", [])] | 691 | ] |
445 | 692 | 692 | ||
446 | 693 | mismatches = find_space_mismatches(parsed_yaml) | 693 | mismatches = find_space_mismatches(parsed_yaml) |
447 | 694 | for mismatch in mismatches: | 694 | for mismatch in mismatches: |
448 | 695 | try: | 695 | try: |
450 | 696 | self._handle_space_mismatch(mismatch, enforce_endpoints, enforce_relations, ignore_endpoints, ignore_relations) | 696 | self._handle_space_mismatch( |
451 | 697 | mismatch, | ||
452 | 698 | enforce_endpoints, | ||
453 | 699 | enforce_relations, | ||
454 | 700 | ignore_endpoints, | ||
455 | 701 | ignore_relations, | ||
456 | 702 | ) | ||
457 | 697 | except Exception: | 703 | except Exception: |
458 | 698 | # FOR NOW: super quick and dirty | 704 | # FOR NOW: super quick and dirty |
460 | 699 | print('Exception caught during space check; please check space by hand. {}'.format(traceback.format_exc())) | 705 | print( |
461 | 706 | "Exception caught during space check; please check space by hand. {}".format( | ||
462 | 707 | traceback.format_exc() | ||
463 | 708 | ) | ||
464 | 709 | ) | ||
465 | 700 | 710 | ||
467 | 701 | def _handle_space_mismatch(self, mismatch, enforce_endpoints, enforce_relations, ignore_endpoints, ignore_relations): | 711 | def _handle_space_mismatch( |
468 | 712 | self, | ||
469 | 713 | mismatch, | ||
470 | 714 | enforce_endpoints, | ||
471 | 715 | enforce_relations, | ||
472 | 716 | ignore_endpoints, | ||
473 | 717 | ignore_relations, | ||
474 | 718 | ): | ||
475 | 702 | # By default: treat mismatches as warnings. | 719 | # By default: treat mismatches as warnings. |
476 | 703 | # If we have a matching enforcement rule, treat as an error. | 720 | # If we have a matching enforcement rule, treat as an error. |
477 | 704 | # If we have a matching ignore rule, do not warn. | 721 | # If we have a matching ignore rule, do not warn. |
478 | @@ -722,12 +739,14 @@ class Linter: | |||
479 | 722 | 739 | ||
480 | 723 | message = "Space binding mismatch: {}".format(mismatch) | 740 | message = "Space binding mismatch: {}".format(mismatch) |
481 | 724 | if error: | 741 | if error: |
488 | 725 | self.handle_error({ | 742 | self.handle_error( |
489 | 726 | "id": "space-binding-mismatch", | 743 | { |
490 | 727 | "tags": ["mismatch", "space", "binding"], | 744 | "id": "space-binding-mismatch", |
491 | 728 | "description": "Unhandled space binding mismatch", | 745 | "tags": ["mismatch", "space", "binding"], |
492 | 729 | "message": message, | 746 | "description": "Unhandled space binding mismatch", |
493 | 730 | }) | 747 | "message": message, |
494 | 748 | } | ||
495 | 749 | ) | ||
496 | 731 | elif warning: | 750 | elif warning: |
497 | 732 | # DEFAULT: not a critical error, so just warn | 751 | # DEFAULT: not a critical error, so just warn |
498 | 733 | self._log_with_header(message, level=logging.WARN) | 752 | self._log_with_header(message, level=logging.WARN) |
499 | @@ -1041,7 +1060,7 @@ class Linter: | |||
500 | 1041 | offer_overlay = True | 1060 | offer_overlay = True |
501 | 1042 | if parsed_yaml is None or not offer_overlay: | 1061 | if parsed_yaml is None or not offer_overlay: |
502 | 1043 | parsed_yaml = doc | 1062 | parsed_yaml = doc |
504 | 1044 | return(parsed_yaml) | 1063 | return parsed_yaml |
505 | 1045 | 1064 | ||
506 | 1046 | def lint_yaml_string(self, yaml_string): | 1065 | def lint_yaml_string(self, yaml_string): |
507 | 1047 | """Lint provided YAML string.""" | 1066 | """Lint provided YAML string.""" |
508 | @@ -1086,15 +1105,10 @@ class Linter: | |||
509 | 1086 | 1105 | ||
510 | 1087 | if "relations" in parsed_yaml: | 1106 | if "relations" in parsed_yaml: |
511 | 1088 | # "bindings" *should* be in exported bundles, *unless* no custom bindings exist, | 1107 | # "bindings" *should* be in exported bundles, *unless* no custom bindings exist, |
513 | 1089 | # in which case "juju export-bundle" omits them. | 1108 | # in which case "juju export-bundle" omits them. See LP#1949883. |
514 | 1090 | if "bindings" in list(parsed_yaml[applications].values())[0]: | 1109 | if "bindings" in list(parsed_yaml[applications].values())[0]: |
522 | 1091 | #try: | 1110 | self.check_spaces(parsed_yaml) |
523 | 1092 | self.check_spaces(parsed_yaml) | 1111 | |
517 | 1093 | #except Exception as e: | ||
518 | 1094 | # self._log_with_header( | ||
519 | 1095 | # "Encountered error while checking spaces: {}".format(e), | ||
520 | 1096 | # level=logging.WARN | ||
521 | 1097 | # ) | ||
524 | 1098 | else: | 1112 | else: |
525 | 1099 | self._log_with_header( | 1113 | self._log_with_header( |
526 | 1100 | "Relations detected but custom bindings not found; " | 1114 | "Relations detected but custom bindings not found; " |
527 | diff --git a/jujulint/logging.py b/jujulint/logging.py | |||
528 | index e3c42f8..1d1586d 100644 | |||
529 | --- a/jujulint/logging.py | |||
530 | +++ b/jujulint/logging.py | |||
531 | @@ -17,10 +17,11 @@ | |||
532 | 17 | # You should have received a copy of the GNU General Public License | 17 | # You should have received a copy of the GNU General Public License |
533 | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
534 | 19 | """Logging helper functions.""" | 19 | """Logging helper functions.""" |
535 | 20 | import colorlog | ||
536 | 21 | import logging | 20 | import logging |
537 | 22 | import sys | 21 | import sys |
538 | 23 | 22 | ||
539 | 23 | import colorlog | ||
540 | 24 | |||
541 | 24 | 25 | ||
542 | 25 | class Logger: | 26 | class Logger: |
543 | 26 | """Helper class for logging.""" | 27 | """Helper class for logging.""" |
544 | diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml | |||
545 | index 9f84064..555e9af 100644 | |||
546 | --- a/snap/snapcraft.yaml | |||
547 | +++ b/snap/snapcraft.yaml | |||
548 | @@ -1,6 +1,6 @@ | |||
549 | 1 | --- | 1 | --- |
550 | 2 | name: juju-lint | 2 | name: juju-lint |
552 | 3 | base: core18 | 3 | base: core20 |
553 | 4 | summary: Linter for Juju models to compare deployments with configurable policy | 4 | summary: Linter for Juju models to compare deployments with configurable policy |
554 | 5 | adopt-info: juju-lint | 5 | adopt-info: juju-lint |
555 | 6 | description: | | 6 | description: | |
556 | @@ -13,8 +13,8 @@ apps: | |||
557 | 13 | juju-lint: | 13 | juju-lint: |
558 | 14 | command: bin/juju-lint | 14 | command: bin/juju-lint |
559 | 15 | environment: | 15 | environment: |
562 | 16 | PATH: "/snap/juju-lint/current/bin:/snap/juju-lint/current/usr/bin:/bin:/usr/bin:" | 16 | PATH: "/snap/juju-lint/current/bin:/snap/juju-lint/current/usr/bin:/bin:/usr/bin:/snap/bin" |
563 | 17 | PYTHONPATH: $SNAP/usr/lib/python3.6/site-packages:$SNAP/usr/lib/python3.6/dist-packages:$PYTHONPATH | 17 | PYTHONPATH: $SNAP/usr/lib/python3.8/site-packages:$SNAP/usr/lib/python3.8/dist-packages:$PYTHONPATH |
564 | 18 | parts: | 18 | parts: |
565 | 19 | juju-lint: | 19 | juju-lint: |
566 | 20 | plugin: python | 20 | plugin: python |
567 | diff --git a/tests/conftest.py b/tests/conftest.py | |||
568 | index fc7093e..55ac1ac 100644 | |||
569 | --- a/tests/conftest.py | |||
570 | +++ b/tests/conftest.py | |||
571 | @@ -8,11 +8,12 @@ | |||
572 | 8 | 8 | ||
573 | 9 | """Test fixtures for juju-lint tool.""" | 9 | """Test fixtures for juju-lint tool.""" |
574 | 10 | 10 | ||
575 | 11 | import mock | ||
576 | 12 | import os | 11 | import os |
577 | 13 | import pytest | ||
578 | 14 | import sys | 12 | import sys |
579 | 15 | 13 | ||
580 | 14 | import mock | ||
581 | 15 | import pytest | ||
582 | 16 | |||
583 | 16 | # bring in top level library to path | 17 | # bring in top level library to path |
584 | 17 | test_path = os.path.dirname(os.path.abspath(__file__)) | 18 | test_path = os.path.dirname(os.path.abspath(__file__)) |
585 | 18 | sys.path.insert(0, test_path + "/../") | 19 | sys.path.insert(0, test_path + "/../") |
586 | diff --git a/tests/requirements.txt b/tests/requirements.txt | |||
587 | index b40fd2a..2575eb3 100644 | |||
588 | --- a/tests/requirements.txt | |||
589 | +++ b/tests/requirements.txt | |||
590 | @@ -1,8 +1,10 @@ | |||
591 | 1 | # Module requirements | 1 | # Module requirements |
592 | 2 | black | ||
593 | 2 | flake8 | 3 | flake8 |
594 | 3 | flake8-colors | 4 | flake8-colors |
595 | 4 | flake8-docstrings | 5 | flake8-docstrings |
596 | 5 | flake8-html | 6 | flake8-html |
597 | 7 | isort | ||
598 | 6 | mock | 8 | mock |
599 | 7 | pep8-naming | 9 | pep8-naming |
600 | 8 | pycodestyle | 10 | pycodestyle |
601 | @@ -10,4 +12,4 @@ pyflakes | |||
602 | 10 | pytest | 12 | pytest |
603 | 11 | pytest-cov | 13 | pytest-cov |
604 | 12 | pytest-html | 14 | pytest-html |
605 | 13 | pytest-mock | ||
606 | 14 | \ No newline at end of file | 15 | \ No newline at end of file |
607 | 16 | pytest-mock | ||
608 | diff --git a/tests/test_jujulint.py b/tests/test_jujulint.py | |||
609 | index c4c51f3..cff1d6b 100644 | |||
610 | --- a/tests/test_jujulint.py | |||
611 | +++ b/tests/test_jujulint.py | |||
612 | @@ -1,8 +1,8 @@ | |||
613 | 1 | #!/usr/bin/python3 | 1 | #!/usr/bin/python3 |
614 | 2 | """Tests for jujulint.""" | 2 | """Tests for jujulint.""" |
615 | 3 | import logging | ||
616 | 3 | from datetime import datetime, timezone | 4 | from datetime import datetime, timezone |
617 | 4 | from unittest import mock | 5 | from unittest import mock |
618 | 5 | import logging | ||
619 | 6 | 6 | ||
620 | 7 | import pytest | 7 | import pytest |
621 | 8 | 8 | ||
622 | @@ -106,7 +106,7 @@ class TestLinter: | |||
623 | 106 | "test-app-4": {"charm": "local:SERIES/TEST-CHARM12"}, | 106 | "test-app-4": {"charm": "local:SERIES/TEST-CHARM12"}, |
624 | 107 | "test-app-5": {"charm": "local:TEST-CHARM12"}, | 107 | "test-app-5": {"charm": "local:TEST-CHARM12"}, |
625 | 108 | "test-app-6": {"charm": "cs:~TEST-CHARMERS/TEST-CHARM12-123"}, | 108 | "test-app-6": {"charm": "cs:~TEST-CHARMERS/TEST-CHARM12-123"}, |
627 | 109 | "test-app-7": {"charm": "ch:amd64/bionic/TEST-CHARM12-123"} | 109 | "test-app-7": {"charm": "ch:amd64/bionic/TEST-CHARM12-123"}, |
628 | 110 | } | 110 | } |
629 | 111 | linter.map_charms(applications) | 111 | linter.map_charms(applications) |
630 | 112 | for charm in linter.model.charms: | 112 | for charm in linter.model.charms: |
631 | @@ -524,9 +524,7 @@ applications: | |||
632 | 524 | 524 | ||
633 | 525 | def test_config_eq_no_suffix_check_all(self, linter, juju_status): | 525 | def test_config_eq_no_suffix_check_all(self, linter, juju_status): |
634 | 526 | """Test the config condition 'eq'. when no suffix all should be checked.""" | 526 | """Test the config condition 'eq'. when no suffix all should be checked.""" |
638 | 527 | linter.lint_rules["config"] = { | 527 | linter.lint_rules["config"] = {"ubuntu": {"fake-opt": {"eq": False}}} |
636 | 528 | "ubuntu": {"fake-opt": {"eq": False}} | ||
637 | 529 | } | ||
639 | 530 | juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": True} | 528 | juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": True} |
640 | 531 | juju_status["applications"]["ubuntu-host"] = juju_status["applications"].pop( | 529 | juju_status["applications"]["ubuntu-host"] = juju_status["applications"].pop( |
641 | 532 | "ubuntu" | 530 | "ubuntu" |
642 | @@ -720,21 +718,21 @@ applications: | |||
643 | 720 | } | 718 | } |
644 | 721 | 719 | ||
645 | 722 | def test_check_spaces_detect_mismatches(self, linter, mocker): | 720 | def test_check_spaces_detect_mismatches(self, linter, mocker): |
647 | 723 | mock_log: mock.MagicMock = mocker.patch('jujulint.lint.Linter._log_with_header') | 721 | mock_log: mock.MagicMock = mocker.patch("jujulint.lint.Linter._log_with_header") |
648 | 724 | linter.model.app_to_charm = self.check_spaces_example_app_charm_map | 722 | linter.model.app_to_charm = self.check_spaces_example_app_charm_map |
649 | 725 | 723 | ||
650 | 726 | # Run the space check. | 724 | # Run the space check. |
651 | 727 | # Based on the above bundle, we should have exactly one mismatch. | 725 | # Based on the above bundle, we should have exactly one mismatch. |
652 | 728 | linter.check_spaces(self.check_spaces_example_bundle) | 726 | linter.check_spaces(self.check_spaces_example_bundle) |
654 | 729 | 727 | ||
655 | 730 | # By default the mismatch should only trigger a warning, not an error. | 728 | # By default the mismatch should only trigger a warning, not an error. |
656 | 731 | errors = linter.output_collector["errors"] | 729 | errors = linter.output_collector["errors"] |
657 | 732 | assert len(errors) == 0 | 730 | assert len(errors) == 0 |
658 | 733 | assert mock_log.call_count == 1 | 731 | assert mock_log.call_count == 1 |
660 | 734 | assert mock_log.mock_calls[0].kwargs['level'] == logging.WARN | 732 | assert mock_log.mock_calls[0].kwargs["level"] == logging.WARN |
661 | 735 | assert mock_log.mock_calls[0].args[0] == ( | 733 | assert mock_log.mock_calls[0].args[0] == ( |
664 | 736 | 'Space binding mismatch: SpaceMismatch(telegraf-app:prometheus-client ' | 734 | "Space binding mismatch: SpaceMismatch(prometheus-app:target " |
665 | 737 | '(space external-space) != prometheus-app:target (space internal-space))' | 735 | "(space internal-space) != telegraf-app:prometheus-client (space external-space))" |
666 | 738 | ) | 736 | ) |
667 | 739 | 737 | ||
668 | 740 | def test_check_spaces_enforce_endpoints(self, linter): | 738 | def test_check_spaces_enforce_endpoints(self, linter): |
669 | @@ -749,7 +747,9 @@ applications: | |||
670 | 749 | 747 | ||
671 | 750 | # Enforce the opposite end of the relation. | 748 | # Enforce the opposite end of the relation. |
672 | 751 | # This should also generate an error. | 749 | # This should also generate an error. |
674 | 752 | linter.lint_rules["space checks"] = {"enforce endpoints": ["telegraf:prometheus-client"]} | 750 | linter.lint_rules["space checks"] = { |
675 | 751 | "enforce endpoints": ["telegraf:prometheus-client"] | ||
676 | 752 | } | ||
677 | 753 | linter.check_spaces(self.check_spaces_example_bundle) | 753 | linter.check_spaces(self.check_spaces_example_bundle) |
678 | 754 | errors = linter.output_collector["errors"] | 754 | errors = linter.output_collector["errors"] |
679 | 755 | assert len(errors) == 2 | 755 | assert len(errors) == 2 |
680 | @@ -759,20 +759,24 @@ applications: | |||
681 | 759 | 759 | ||
682 | 760 | # Run the space check with prometheus:target endpoint enforced. | 760 | # Run the space check with prometheus:target endpoint enforced. |
683 | 761 | # This should generate an error. | 761 | # This should generate an error. |
685 | 762 | linter.lint_rules["space checks"] = {"enforce relations": [["prometheus:target", "telegraf:prometheus-client"]]} | 762 | linter.lint_rules["space checks"] = { |
686 | 763 | "enforce relations": [["prometheus:target", "telegraf:prometheus-client"]] | ||
687 | 764 | } | ||
688 | 763 | linter.check_spaces(self.check_spaces_example_bundle) | 765 | linter.check_spaces(self.check_spaces_example_bundle) |
689 | 764 | errors = linter.output_collector["errors"] | 766 | errors = linter.output_collector["errors"] |
690 | 765 | assert len(errors) == 1 | 767 | assert len(errors) == 1 |
691 | 766 | 768 | ||
692 | 767 | # Reverse the relation's definition order. | 769 | # Reverse the relation's definition order. |
693 | 768 | # This should work the same way and also generate an error. | 770 | # This should work the same way and also generate an error. |
695 | 769 | linter.lint_rules["space checks"] = {"enforce relations": [["telegraf:prometheus-client", "prometheus:target"]]} | 771 | linter.lint_rules["space checks"] = { |
696 | 772 | "enforce relations": [["telegraf:prometheus-client", "prometheus:target"]] | ||
697 | 773 | } | ||
698 | 770 | linter.check_spaces(self.check_spaces_example_bundle) | 774 | linter.check_spaces(self.check_spaces_example_bundle) |
699 | 771 | errors = linter.output_collector["errors"] | 775 | errors = linter.output_collector["errors"] |
700 | 772 | assert len(errors) == 2 | 776 | assert len(errors) == 2 |
701 | 773 | 777 | ||
702 | 774 | def test_check_spaces_ignore_endpoints(self, linter, mocker): | 778 | def test_check_spaces_ignore_endpoints(self, linter, mocker): |
704 | 775 | mock_log: mock.MagicMock = mocker.patch('jujulint.lint.Linter._log_with_header') | 779 | mock_log: mock.MagicMock = mocker.patch("jujulint.lint.Linter._log_with_header") |
705 | 776 | linter.model.app_to_charm = self.check_spaces_example_app_charm_map | 780 | linter.model.app_to_charm = self.check_spaces_example_app_charm_map |
706 | 777 | 781 | ||
707 | 778 | # Run the space check with prometheus:target endpoint ignored. | 782 | # Run the space check with prometheus:target endpoint ignored. |
708 | @@ -785,19 +789,23 @@ applications: | |||
709 | 785 | 789 | ||
710 | 786 | # Enforce the opposite end of the relation. | 790 | # Enforce the opposite end of the relation. |
711 | 787 | # This should also generate an error. | 791 | # This should also generate an error. |
713 | 788 | linter.lint_rules["space checks"] = {"ignore endpoints": ["telegraf:prometheus-client"]} | 792 | linter.lint_rules["space checks"] = { |
714 | 793 | "ignore endpoints": ["telegraf:prometheus-client"] | ||
715 | 794 | } | ||
716 | 789 | linter.check_spaces(self.check_spaces_example_bundle) | 795 | linter.check_spaces(self.check_spaces_example_bundle) |
717 | 790 | errors = linter.output_collector["errors"] | 796 | errors = linter.output_collector["errors"] |
718 | 791 | assert len(errors) == 0 | 797 | assert len(errors) == 0 |
719 | 792 | assert mock_log.call_count == 0 | 798 | assert mock_log.call_count == 0 |
720 | 793 | 799 | ||
721 | 794 | def test_check_spaces_ignore_relations(self, linter, mocker): | 800 | def test_check_spaces_ignore_relations(self, linter, mocker): |
723 | 795 | mock_log: mock.MagicMock = mocker.patch('jujulint.lint.Linter._log_with_header') | 801 | mock_log: mock.MagicMock = mocker.patch("jujulint.lint.Linter._log_with_header") |
724 | 796 | linter.model.app_to_charm = self.check_spaces_example_app_charm_map | 802 | linter.model.app_to_charm = self.check_spaces_example_app_charm_map |
725 | 797 | 803 | ||
726 | 798 | # Run the space check with prometheus:target endpoint ignored. | 804 | # Run the space check with prometheus:target endpoint ignored. |
727 | 799 | # This should neither generate an error nor a warning. | 805 | # This should neither generate an error nor a warning. |
729 | 800 | linter.lint_rules["space checks"] = {"ignore relations": [["prometheus:target", "telegraf:prometheus-client"]]} | 806 | linter.lint_rules["space checks"] = { |
730 | 807 | "ignore relations": [["prometheus:target", "telegraf:prometheus-client"]] | ||
731 | 808 | } | ||
732 | 801 | linter.check_spaces(self.check_spaces_example_bundle) | 809 | linter.check_spaces(self.check_spaces_example_bundle) |
733 | 802 | errors = linter.output_collector["errors"] | 810 | errors = linter.output_collector["errors"] |
734 | 803 | assert len(errors) == 0 | 811 | assert len(errors) == 0 |
735 | @@ -805,7 +813,9 @@ applications: | |||
736 | 805 | 813 | ||
737 | 806 | # Reverse the relation's definition order. | 814 | # Reverse the relation's definition order. |
738 | 807 | # This should work the same way and also not generate errors/warnings. | 815 | # This should work the same way and also not generate errors/warnings. |
740 | 808 | linter.lint_rules["space checks"] = {"ignore relations": [["telegraf:prometheus-client", "prometheus:target"]]} | 816 | linter.lint_rules["space checks"] = { |
741 | 817 | "ignore relations": [["telegraf:prometheus-client", "prometheus:target"]] | ||
742 | 818 | } | ||
743 | 809 | linter.check_spaces(self.check_spaces_example_bundle) | 819 | linter.check_spaces(self.check_spaces_example_bundle) |
744 | 810 | errors = linter.output_collector["errors"] | 820 | errors = linter.output_collector["errors"] |
745 | 811 | assert len(errors) == 0 | 821 | assert len(errors) == 0 |
746 | diff --git a/tox.ini b/tox.ini | |||
747 | index 3e9dbdf..843a442 100644 | |||
748 | --- a/tox.ini | |||
749 | +++ b/tox.ini | |||
750 | @@ -15,7 +15,7 @@ envlist = lintverbose, unit | |||
751 | 15 | skip_missing_interpreters = True | 15 | skip_missing_interpreters = True |
752 | 16 | 16 | ||
753 | 17 | [testenv] | 17 | [testenv] |
755 | 18 | basepython = python3 | 18 | basepython = python3.8 |
756 | 19 | deps = | 19 | deps = |
757 | 20 | -r{toxinidir}/tests/requirements.txt | 20 | -r{toxinidir}/tests/requirements.txt |
758 | 21 | -r{toxinidir}/requirements.txt | 21 | -r{toxinidir}/requirements.txt |
759 | @@ -24,20 +24,34 @@ commands = | |||
760 | 24 | test: python3 -m unittest discover tests | 24 | test: python3 -m unittest discover tests |
761 | 25 | 25 | ||
762 | 26 | [testenv:unit] | 26 | [testenv:unit] |
770 | 27 | commands = pytest -v \ | 27 | commands = |
771 | 28 | --cov=jujulint \ | 28 | pytest -v \ |
772 | 29 | --cov-report=term \ | 29 | --cov=jujulint \ |
773 | 30 | --cov-report=annotate:tests/report/coverage-annotated \ | 30 | --new-first \ |
774 | 31 | --cov-report=html:tests/report/coverage-html \ | 31 | --last-failed \ |
775 | 32 | --html=tests/report/index.html \ | 32 | --last-failed-no-failures all \ |
776 | 33 | --junitxml=tests/report/junit.xml | 33 | --cov-report=term-missing \ |
777 | 34 | --cov-report=annotate:tests/report/coverage-annotated \ | ||
778 | 35 | --cov-report=html:tests/report/coverage-html \ | ||
779 | 36 | --html=tests/report/index.html \ | ||
780 | 37 | --junitxml=tests/report/junit.xml | ||
781 | 34 | setenv = PYTHONPATH={toxinidir}/lib | 38 | setenv = PYTHONPATH={toxinidir}/lib |
782 | 35 | 39 | ||
783 | 36 | [testenv:lint] | 40 | [testenv:lint] |
784 | 37 | commands = flake8 jujulint --format=html --htmldir=tests/report/lint/ --tee | 41 | commands = flake8 jujulint --format=html --htmldir=tests/report/lint/ --tee |
785 | 38 | 42 | ||
786 | 39 | [testenv:lintverbose] | 43 | [testenv:lintverbose] |
788 | 40 | commands = flake8 jujulint | 44 | commands = |
789 | 45 | flake8 {toxinidir}/jujulint {toxinidir}/tests | ||
790 | 46 | black --check {toxinidir}/jujulint {toxinidir}/tests | ||
791 | 47 | isort --check {toxinidir}/jujulint {toxinidir}/tests | ||
792 | 48 | |||
793 | 49 | |||
794 | 50 | [testenv:format-code] | ||
795 | 51 | commands = | ||
796 | 52 | black {toxinidir}/jujulint {toxinidir}/tests | ||
797 | 53 | isort {toxinidir}/jujulint {toxinidir}/tests | ||
798 | 54 | |||
799 | 41 | 55 | ||
800 | 42 | [testenv:lintjunit] | 56 | [testenv:lintjunit] |
801 | 43 | commands = flake8 jujulint --format junit-xml --output-file=report/lint/junit.xml | 57 | commands = flake8 jujulint --format junit-xml --output-file=report/lint/junit.xml |
802 | @@ -45,3 +59,6 @@ commands = flake8 jujulint --format junit-xml --output-file=report/lint/junit.xm | |||
803 | 45 | [pytest] | 59 | [pytest] |
804 | 46 | filterwarnings = | 60 | filterwarnings = |
805 | 47 | ignore::DeprecationWarning | 61 | ignore::DeprecationWarning |
806 | 62 | |||
807 | 63 | [isort] | ||
808 | 64 | profile = black |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.