├── test
├── boilerplate
│ ├── boilerplate.js.preamble
│ ├── boilerplate.py.preamble
│ ├── boilerplate.sh.preamble
│ ├── boilerplate.xml.preamble
│ ├── boilerplate.go.preamble
│ ├── boilerplate.html.preamble
│ ├── boilerplate.css.txt
│ ├── boilerplate.go.txt
│ ├── boilerplate.tf.txt
│ ├── boilerplate.bzl.txt
│ ├── boilerplate.py.txt
│ ├── boilerplate.sh.txt
│ ├── boilerplate.yaml.txt
│ ├── boilerplate.BUILD.txt
│ ├── boilerplate.Makefile.txt
│ ├── boilerplate.WORKSPACE.txt
│ ├── boilerplate.bazel.txt
│ ├── boilerplate.xml.txt
│ ├── boilerplate.Dockerfile.txt
│ ├── boilerplate.html.txt
│ ├── boilerplate.java.txt
│ ├── boilerplate.js.txt
│ ├── boilerplate.scss.txt
│ └── boilerplate.ts.txt
├── make.sh
└── verify_boilerplate.py
├── public
├── favicon.ico
├── manifest.json
└── index.html
├── .gitignore
├── .github
└── workflows
│ └── main.yml
├── src
├── gke-ipam.js
├── App.test.js
├── index.css
├── App.css
├── index.js
├── App.js
├── rules
│ ├── output
│ │ ├── ServiceOutpuRules.js
│ │ ├── NodeOutputRules.test.js
│ │ ├── PodOutputRules.js
│ │ └── NodeOutputRules.js
│ └── input
│ │ ├── PodNetmaskRules.js
│ │ ├── AvailableNetmaskRules.js
│ │ ├── BaseNetmaskRules.test.js
│ │ ├── BaseNetmaskRules.js
│ │ ├── node
│ │ └── NodeRules.js
│ │ ├── cluster
│ │ └── ClusterRules.js
│ │ ├── service
│ │ └── ServiceRules.js
│ │ └── InputRules.js
├── ui
│ ├── output
│ │ ├── LogicOutputFreeNetworkRow.js
│ │ ├── Results.js
│ │ ├── LogicOutputNetworkRow.js
│ │ ├── LogicOutputDropdown.js
│ │ └── LogicOutput.js
│ └── input
│ │ └── Parameters.js
├── calculate.js
├── logic
│ ├── IPUtils.test.js
│ ├── Packer.js
│ ├── IPUtils.js
│ ├── Packer.test.js
│ └── Logic.js
├── components
│ ├── TextDropdownInput.js
│ ├── NetmaskInput.js
│ ├── TextDropdown.js
│ ├── NetmaskDropdown.js
│ └── StateButtons.js
├── logo.svg
├── Calculator.js
└── serviceWorker.js
├── package.json
├── CONTRIBUTING.md
├── .eslintrc.js
├── Jenkinsfile
├── Makefile
├── README.md
└── LICENSE
/test/boilerplate/boilerplate.js.preamble:
--------------------------------------------------------------------------------
1 | #!
2 |
--------------------------------------------------------------------------------
/test/boilerplate/boilerplate.py.preamble:
--------------------------------------------------------------------------------
1 | #!
2 |
--------------------------------------------------------------------------------
/test/boilerplate/boilerplate.sh.preamble:
--------------------------------------------------------------------------------
1 | #!
2 |
--------------------------------------------------------------------------------
/test/boilerplate/boilerplate.xml.preamble:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/test/boilerplate/boilerplate.Dockerfile.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2019 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
--------------------------------------------------------------------------------
/test/boilerplate/boilerplate.html.txt:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/test/boilerplate/boilerplate.java.txt:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
--------------------------------------------------------------------------------
/test/boilerplate/boilerplate.js.txt:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
--------------------------------------------------------------------------------
/test/boilerplate/boilerplate.scss.txt:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
--------------------------------------------------------------------------------
/test/boilerplate/boilerplate.ts.txt:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - master # Trigger the workflow on push to master
7 |
8 | jobs:
9 | build:
10 |
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - uses: actions/checkout@v1
15 | - name: Create the production build
16 | run: make basefiles npm-install npm-test npm-build
17 | env:
18 | IPAM_HOST_URL: https://googlecloudplatform.github.io/gke-ip-address-management/
19 | - name: Push changes
20 | uses: peaceiris/actions-gh-pages@v2
21 | env:
22 | ACTIONS_DEPLOY_KEY: ${{ secrets.ACTIONS_DEPLOY_KEY }}
23 | PUBLISH_BRANCH: gh-pages
24 | PUBLISH_DIR: ./build
25 |
--------------------------------------------------------------------------------
/src/gke-ipam.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | // Copyright 2019 Google LLC
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // https://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 |
16 | // Transpile all code following this line with babel and use 'env' (aka ES6) preset.
17 | require('babel-register')({
18 | presets: ['env'],
19 | });
20 |
21 | // Import the rest of our application.
22 | module.exports = require('./calculate.js');
23 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import React from 'react';
16 | import ReactDOM from 'react-dom';
17 | import App from './App';
18 |
19 | // This test will execute a lot of code, so it will skew the coverage report.
20 | it('renders without crashing', () => {
21 | const div = document.createElement('div');
22 | ReactDOM.render(, div);
23 | ReactDOM.unmountComponentAtNode(div);
24 | });
25 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019 Google LLC
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | https://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | body {
18 | margin: 0;
19 | padding: 0;
20 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
21 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
22 | sans-serif;
23 | -webkit-font-smoothing: antialiased;
24 | -moz-osx-font-smoothing: grayscale;
25 | }
26 |
27 | code {
28 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
29 | monospace;
30 | }
31 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gke-net",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "babel-preset-env": "^1.7.0",
7 | "babel-register": "^6.26.0",
8 | "bootstrap": "^4.3.1",
9 | "minimist": "^1.2.3",
10 | "react": "^16.6.0",
11 | "react-dom": "^16.6.0",
12 | "react-scripts": "^4.0.0",
13 | "reactstrap": "^6.5.0"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test",
19 | "eject": "react-scripts eject",
20 | "lint": "eslint src",
21 | "calculate": "src/calculate.js"
22 | },
23 | "eslintConfig": {
24 | "extends": "react-app"
25 | },
26 | "browserslist": [
27 | ">0.2%",
28 | "not dead",
29 | "not ie <= 11",
30 | "not op_mini all"
31 | ],
32 | "devDependencies": {
33 | "eslint-config-airbnb": "^17.1.1",
34 | "eslint-config-google": "^0.13.0",
35 | "eslint-plugin-import": "^2.18.2",
36 | "eslint-plugin-jsx-a11y": "^6.2.3",
37 | "eslint-plugin-react": "^7.14.3"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019 Google LLC
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | https://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | .App {
18 | text-align: center;
19 | }
20 |
21 | .App-logo {
22 | animation: App-logo-spin infinite 20s linear;
23 | height: 40vmin;
24 | }
25 |
26 |
27 | .App-header {
28 | background-color: #222;
29 | height: 115px;
30 | padding: 10px;
31 | color: white;
32 | }
33 |
34 |
35 | .App-link {
36 | color: #61dafb;
37 | }
38 |
39 | @keyframes App-logo-spin {
40 | from {
41 | transform: rotate(0deg);
42 | }
43 | to {
44 | transform: rotate(360deg);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 | Contributions to this project must be accompanied by a Contributor License
8 | Agreement. You (or your employer) retain the copyright to your contribution;
9 | this simply gives us permission to use and redistribute your contributions as
10 | part of the project. Head over to https://cla.developers.google.com/ to see your
11 | current agreements on file or to sign a new one.
12 |
13 | You generally only need to submit a CLA once, so if you've already submitted one
14 | (even if it was for a different project), you probably don't need to do it again.
15 |
16 | ## Code reviews
17 | All submissions, including submissions by project members, require review. We
18 | use GitHub pull requests for this purpose. Consult GitHub Help for more
19 | information on using pull requests.
20 |
21 | ## Community Guidelines
22 | This project follows [Google's Open Source Community
23 | Guidelines](https://opensource.google.com/conduct/).
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import React from 'react';
16 | import ReactDOM from 'react-dom';
17 | import './index.css';
18 | import App from './App';
19 | import * as serviceWorker from './serviceWorker';
20 | import 'bootstrap/dist/css/bootstrap.min.css';
21 |
22 | ReactDOM.render( < App / >, document.getElementById('root'));
23 |
24 | // If you want your app to work offline and load faster, you can change
25 | // unregister() to register() below. Note this comes with some pitfalls.
26 | // Learn more about service workers: http://bit.ly/CRA-PWA
27 | serviceWorker.unregister();
28 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import React, {
16 | Component,
17 | } from 'react';
18 | import './App.css';
19 | import Calculator from './Calculator';
20 |
21 | /**
22 | * Main application class.
23 | */
24 | class App extends Component {
25 | /**
26 | * Render the main application component.
27 | *
28 | * @return {Object} the main
for the application.
29 | */
30 | render() {
31 | return (
32 |
33 |
GKE IP Address Management
34 |
35 |
36 |
37 |
38 |
39 | );
40 | }
41 | }
42 |
43 | export default App;
44 |
--------------------------------------------------------------------------------
/src/rules/output/ServiceOutpuRules.js:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import IPUtils from '../../logic/IPUtils';
16 |
17 | /**
18 | * Rules for the Service subnet range
19 | */
20 | class ServiceOutputRules {
21 | /**
22 | * Constructor.
23 | *
24 | * @param {Input} input
25 | */
26 | constructor(input) {
27 | this.input = input;
28 | }
29 |
30 | /**
31 | * Returns the maximum number of usable IPs.
32 | *
33 | * @return {number} maximum number of usable IPs.
34 | */
35 | maximum() {
36 | const serviceIps = IPUtils.netmaskToUsableIps(this.input.serviceNetmask);
37 | return serviceIps;
38 | }
39 | }
40 |
41 | export default ServiceOutputRules;
42 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | module.exports = {
16 | env: {
17 | browser: true,
18 | node: true,
19 | es6: true,
20 | jest: true
21 | },
22 | parser: "babel-eslint",
23 | extends: ["eslint:recommended", "plugin:react/recommended", "google"],
24 | globals: {
25 | Atomics: "readonly",
26 | SharedArrayBuffer: "readonly"
27 | },
28 | parserOptions: {
29 | ecmaFeatures: {
30 | jsx: true
31 | },
32 | ecmaVersion: 2018,
33 | sourceType: "module"
34 | },
35 | plugins: ["react"],
36 | rules: {
37 | "max-len": ["error", { code: 120 }],
38 | "no-invalid-this": ["off"]
39 | },
40 | settings: {
41 | react: {
42 | version: "detect"
43 | }
44 | }
45 | };
46 |
--------------------------------------------------------------------------------
/src/rules/output/NodeOutputRules.test.js:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import {NodeNetworkRules, ClusterNetworkRules} from './NodeOutputRules';
16 |
17 | it('NodeNetworkRules calculates the right value', () => {
18 | const r1 = new NodeNetworkRules({nodeNetmask: 24});
19 | expect(r1.maximum()).toEqual(252);
20 | const r2 = new NodeNetworkRules({nodeNetmask: 16});
21 | expect(r2.maximum()).toEqual(65532);
22 | });
23 |
24 | it('ClusterNetworkRules calculates the right value', () => {
25 | const r1 = new ClusterNetworkRules({clusterNetmask: 22, nodePodNetmask: 24});
26 | expect(r1.maximum()).toEqual(4);
27 | const r2 = new ClusterNetworkRules({clusterNetmask: 20, nodePodNetmask: 24});
28 | expect(r2.maximum()).toEqual(16);
29 | });
30 |
--------------------------------------------------------------------------------
/src/rules/output/PodOutputRules.js:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import {nodePodNetmaskOptions} from '../../ui/input/Parameters';
16 |
17 | /**
18 | * Rules for the Pod subnet range
19 | */
20 | class PodOutputRules {
21 | /**
22 | * Constructor.
23 | *
24 | * @param {Input} input
25 | */
26 | constructor(input) {
27 | this.input = input;
28 | }
29 |
30 | /**
31 | * Returns the maximum number of usable IPs.
32 | *
33 | * @return {number} maximum number of usable IPs.
34 | */
35 | maximum() {
36 | let i;
37 | for (i = 0; i < nodePodNetmaskOptions.length; ++i) {
38 | if (nodePodNetmaskOptions[i].value === this.input.nodePodNetmask) {
39 | return nodePodNetmaskOptions[i].max;
40 | }
41 | }
42 | return NaN;
43 | }
44 | }
45 |
46 | export default PodOutputRules;
47 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env groovy
2 | /*
3 | Copyright 2019 Google LLC
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 |
9 | https://www.apache.org/licenses/LICENSE-2.0
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
16 |
17 | */
18 |
19 | def label = "gke-ipam-${UUID.randomUUID().toString()}"
20 | podTemplate(label: label, containers: [
21 | containerTemplate(name: 'node', image: 'node:12.7.0-stretch', ttyEnabled: true, command: 'cat'),
22 | containerTemplate(name: 'python', image: 'python:3.7.4-buster', ttyEnabled: true, command: 'cat'),
23 | ]) {
24 | node(label) {
25 | container('python') {
26 | stage('Checkout') {
27 | checkout scm
28 | }
29 | stage('Verify Headers') {
30 | sh 'make verify-header'
31 | }
32 | }
33 | container('node') {
34 | stage('Install dependencies') {
35 | sh 'make npm-install'
36 | }
37 | stage('Run tests') {
38 | sh 'make npm-test'
39 | }
40 | stage('Run lint') {
41 | sh 'make lint'
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/rules/input/PodNetmaskRules.js:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import BaseNetmaskRules from './BaseNetmaskRules';
16 |
17 | /**
18 | * Hardcoded minimum limit for pod ranges
19 | */
20 | class HardcodedMin {
21 | /**
22 | * Return the minimum for the range
23 | *
24 | * @return {number} range minimum
25 | */
26 | minimum() {
27 | return 28;
28 | }
29 | }
30 |
31 | /**
32 | * Hardcoded max limit for pod ranges
33 | */
34 | class HardcodedMax {
35 | /**
36 | * Return the max for the range
37 | *
38 | * @return {number} range max
39 | */
40 | maximum() {
41 | return 24;
42 | }
43 | }
44 |
45 |
46 | /**
47 | * Rules for available range of pod netmask options.
48 | */
49 | class PodNetmastRules extends BaseNetmaskRules {
50 | /**
51 | * Constructor.
52 | */
53 | constructor() {
54 | super([new HardcodedMin()], [new HardcodedMax()]);
55 | }
56 | }
57 |
58 | export default PodNetmastRules;
59 |
--------------------------------------------------------------------------------
/src/rules/input/AvailableNetmaskRules.js:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import BaseNetmaskRules from './BaseNetmaskRules';
16 |
17 | /**
18 | * Rule that define the minimum available netmask
19 | */
20 | class HardcodedMin {
21 | /**
22 | * Calculate the minimum netmask possible.
23 | *
24 | * @return {number} minimum netmask possible.
25 | */
26 | minimum() {
27 | return 29;
28 | }
29 | }
30 |
31 | /**
32 | * Rule that define the maximum available netmask
33 | */
34 | class HardcodedMax {
35 | /**
36 | * Calculate the maximum netmask possible.
37 | *
38 | * @return {number} maximum netmask possible.
39 | */
40 | maximum() {
41 | return 8;
42 | }
43 | }
44 |
45 | /**
46 | * Rules that define the available netmasks to show
47 | */
48 | class AvailableNetmaskRules extends BaseNetmaskRules {
49 | /**
50 | *
51 | * Constructor.
52 | */
53 | constructor() {
54 | super([new HardcodedMin()], [new HardcodedMax()]);
55 | }
56 | }
57 |
58 | export default AvailableNetmaskRules;
59 |
--------------------------------------------------------------------------------
/src/ui/output/LogicOutputFreeNetworkRow.js:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import React, {Component} from 'react';
16 | import '../../App.css';
17 | import IPUtils from '../../logic/IPUtils';
18 | import PropTypes from 'prop-types';
19 |
20 | /**
21 | * A row for an available or free subnet.
22 | */
23 | class LogicOutputFreeNetworkRow extends Component {
24 | /**
25 | * Render the row.
26 | *
27 | * @return {Object} the table row for the network.
28 | */
29 | render() {
30 | return (
31 |
);
151 | }
152 |
153 | /**
154 | * Handle the selection of a different combination to be displayed.
155 | *
156 | * @param {number} combinationIndex The index of the combinations to be displayed.
157 | */
158 | handleChangeCombination(combinationIndex) {
159 | this.handleUpdateOutput(
160 | Object.assign({}, this.state.output, {
161 | combinationIndex: combinationIndex,
162 | }),
163 | );
164 | }
165 |
166 | /**
167 | * Update the output.
168 | *
169 | * @param {object} output The updated output.
170 | */
171 | handleUpdateOutput(output) {
172 | this.setState({
173 | output: output,
174 | });
175 | }
176 |
177 | /**
178 | * Render the main output component.
179 | *
180 | * @return {Object} the main
for the output.
181 | */
182 | render() {
183 | const nodeOutputRules = new NodeOutputRules(this.props.input);
184 | const serviceOutputRules = new ServiceOutputRules(this.props.input);
185 | const podOutputRules = new PodOutputRules(this.props.input);
186 |
187 | return (
188 |
189 |
190 | Summary
191 | With the current configuration, up to{' '}
192 | {this.props.logic.getCombinations().length} isolated clusters can be
193 | created.
194 | Each cluster will suppport:
195 | Up to {nodeOutputRules.maximum()} node(s) per cluster.
196 | Up to {serviceOutputRules.maximum()} service(s) per cluster.
197 | Up to {podOutputRules.maximum()} pods per node.
198 |
210 | );
211 | }
212 | }
213 |
214 |
215 | LogicOutput.propTypes = {
216 | logic: PropTypes.instanceOf(Logic),
217 | input: PropTypes.object,
218 | };
219 |
220 | export default LogicOutput;
221 |
--------------------------------------------------------------------------------
/src/ui/input/Parameters.js:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // https://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | import React, {Component} from 'react';
16 | import {Form, FormGroup, Input, Label, Col} from 'reactstrap';
17 | import TextDropdown from '../../components/TextDropdown';
18 | import NetmaskInput from '../../components/NetmaskInput';
19 | import TextDropdownInput from '../../components/TextDropdownInput';
20 | import InputRules from '../../rules/input/InputRules';
21 | import PropTypes from 'prop-types';
22 |
23 | const locationTypeOptions = [
24 | {value: 'ZONAL', text: 'Zonal'},
25 | {value: 'MULTI_ZONAL', text: 'Multi - zonal'},
26 | {value: 'REGIONAL', text: 'Regional'},
27 | ];
28 |
29 | const extraZoneOptions = [
30 | {value: '1', text: '1'},
31 | {value: '2', text: '2'},
32 | {value: '3', text: '3'},
33 | ];
34 |
35 | const masterBlockOptions = [
36 | {value: 'PUBLIC', text: 'Public Master'},
37 | {value: 'SHARE', text: 'Share one Master CIDR across all clusters'},
38 | {value: 'UNIQUE', text: 'Create a Unique Master CIDR for each cluster'},
39 | {value: 'DEFAULT', text: 'Use default values for the Master CIDR block'},
40 | ];
41 |
42 | const nodePodNetmaskOptions = [
43 | {value: '28', text: '8 pods (/28)', max: 8},
44 | {value: '27', text: '9 to 16 pods (/27)', max: 16},
45 | {value: '26', text: '17 to 32 pods (/26)', max: 32},
46 | {value: '25', text: '33 to 64 pods (/25)', max: 64},
47 | {value: '24', text: '65 to 110 pods (/24)', max: 110},
48 | ];
49 |
50 | /**
51 | * User inputted parameters
52 | */
53 | class Parameters extends Component {
54 | /**
55 | * Constructor.
56 | *
57 | * @param {Object} props
58 | */
59 | constructor(props) {
60 | super(props);
61 |
62 | this.handleChangeNetwork = this.handleChangeNetwork.bind(this);
63 |
64 | this.handleLocationTypeChange = this.handleLocationTypeChange.bind(this);
65 | this.handleExtraZonesChange = this.handleExtraZonesChange.bind(this);
66 | this.handlePropChange = this.handlePropChange.bind(this);
67 | }
68 |
69 |
70 | /**
71 | * Handle selecting a different location type.
72 | *
73 | * @param {Object} newValue the new value.
74 | */
75 | handleLocationTypeChange(newValue) {
76 | this.props.handleUpdateInput(
77 | Object.assign({}, this.props.input, {
78 | locationType: newValue,
79 | }),
80 | );
81 | }
82 |
83 |
84 | /**
85 | * Handle selecting a different number of extra zones (used for zonal clusters only).
86 | *
87 | * @param {Object} newValue the new value.
88 | */
89 | handleExtraZonesChange(newValue) {
90 | this.props.handleUpdateInput(
91 | Object.assign({}, this.props.input, {
92 | extraZones: newValue,
93 | }),
94 | );
95 | }
96 |
97 |
98 | /**
99 | * Handle changing the network.
100 | *
101 | * @param {Object} e the change event.
102 | */
103 | handleChangeNetwork(e) {
104 | this.props.handleUpdateInput(
105 | Object.assign({}, this.props.input, {
106 | network: e.target.value,
107 | }),
108 | );
109 | }
110 |
111 | /**
112 | * Handle changing an arbitrary property.
113 | *
114 | * @param {string} propName the name of the property to update.
115 | * @param {Object} newValue the new value.
116 | */
117 | handlePropChange(propName, newValue) {
118 | this.props.handleUpdateInput(
119 | Object.assign({}, this.props.input, {
120 | [propName]: newValue,
121 | }),
122 | );
123 | }
124 |
125 | /**
126 | * Render the widgets for the parameters.
127 | *
128 | * @return {Object} the Form element that contains the parameters.
129 | */
130 | render() {
131 | let extraZonesLabel;
132 | let extraZonesInput;
133 |
134 | if (this.props.input.locationType === 'MULTI_ZONAL') {
135 | extraZonesLabel = (
136 |
139 | );
140 | extraZonesInput = (
141 |
142 |
149 |
150 | );
151 | } else {
152 | extraZonesLabel = ' ';
153 | extraZonesInput = ' ';
154 | }
155 |
156 | return (
157 |
246 | );
247 | }
248 | }
249 |
250 | Parameters.propTypes = {
251 | input: PropTypes.object,
252 | inputRules: PropTypes.instanceOf(InputRules),
253 | handleUpdateInput: PropTypes.func,
254 | };
255 |
256 | export default Parameters;
257 |
258 | export {nodePodNetmaskOptions};
259 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
5 | 1. Definitions.
6 | "License" shall mean the terms and conditions for use, reproduction,
7 | and distribution as defined by Sections 1 through 9 of this document.
8 | "Licensor" shall mean the copyright owner or entity authorized by
9 | the copyright owner that is granting the License.
10 | "Legal Entity" shall mean the union of the acting entity and all
11 | other entities that control, are controlled by, or are under common
12 | control with that entity. For the purposes of this definition,
13 | "control" means (i) the power, direct or indirect, to cause the
14 | direction or management of such entity, whether by contract or
15 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
16 | outstanding shares, or (iii) beneficial ownership of such entity.
17 | "You" (or "Your") shall mean an individual or Legal Entity
18 | exercising permissions granted by this License.
19 | "Source" form shall mean the preferred form for making modifications,
20 | including but not limited to software source code, documentation
21 | source, and configuration files.
22 | "Object" form shall mean any form resulting from mechanical
23 | transformation or translation of a Source form, including but
24 | not limited to compiled object code, generated documentation,
25 | and conversions to other media types.
26 | "Work" shall mean the work of authorship, whether in Source or
27 | Object form, made available under the License, as indicated by a
28 | copyright notice that is included in or attached to the work
29 | (an example is provided in the Appendix below).
30 | "Derivative Works" shall mean any work, whether in Source or Object
31 | form, that is based on (or derived from) the Work and for which the
32 | editorial revisions, annotations, elaborations, or other modifications
33 | represent, as a whole, an original work of authorship. For the purposes
34 | of this License, Derivative Works shall not include works that remain
35 | separable from, or merely link (or bind by name) to the interfaces of,
36 | the Work and Derivative Works thereof.
37 | "Contribution" shall mean any work of authorship, including
38 | the original version of the Work and any modifications or additions
39 | to that Work or Derivative Works thereof, that is intentionally
40 | submitted to Licensor for inclusion in the Work by the copyright owner
41 | or by an individual or Legal Entity authorized to submit on behalf of
42 | the copyright owner. For the purposes of this definition, "submitted"
43 | means any form of electronic, verbal, or written communication sent
44 | to the Licensor or its representatives, including but not limited to
45 | communication on electronic mailing lists, source code control systems,
46 | and issue tracking systems that are managed by, or on behalf of, the
47 | Licensor for the purpose of discussing and improving the Work, but
48 | excluding communication that is conspicuously marked or otherwise
49 | designated in writing by the copyright owner as "Not a Contribution."
50 | "Contributor" shall mean Licensor and any individual or Legal Entity
51 | on behalf of whom a Contribution has been received by Licensor and
52 | subsequently incorporated within the Work.
53 | 2. Grant of Copyright License. Subject to the terms and conditions of
54 | this License, each Contributor hereby grants to You a perpetual,
55 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
56 | copyright license to reproduce, prepare Derivative Works of,
57 | publicly display, publicly perform, sublicense, and distribute the
58 | Work and such Derivative Works in Source or Object form.
59 | 3. Grant of Patent License. Subject to the terms and conditions of
60 | this License, each Contributor hereby grants to You a perpetual,
61 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
62 | (except as stated in this section) patent license to make, have made,
63 | use, offer to sell, sell, import, and otherwise transfer the Work,
64 | where such license applies only to those patent claims licensable
65 | by such Contributor that are necessarily infringed by their
66 | Contribution(s) alone or by combination of their Contribution(s)
67 | with the Work to which such Contribution(s) was submitted. If You
68 | institute patent litigation against any entity (including a
69 | cross-claim or counterclaim in a lawsuit) alleging that the Work
70 | or a Contribution incorporated within the Work constitutes direct
71 | or contributory patent infringement, then any patent licenses
72 | granted to You under this License for that Work shall terminate
73 | as of the date such litigation is filed.
74 | 4. Redistribution. You may reproduce and distribute copies of the
75 | Work or Derivative Works thereof in any medium, with or without
76 | modifications, and in Source or Object form, provided that You
77 | meet the following conditions:
78 | (a) You must give any other recipients of the Work or
79 | Derivative Works a copy of this License; and
80 | (b) You must cause any modified files to carry prominent notices
81 | stating that You changed the files; and
82 | (c) You must retain, in the Source form of any Derivative Works
83 | that You distribute, all copyright, patent, trademark, and
84 | attribution notices from the Source form of the Work,
85 | excluding those notices that do not pertain to any part of
86 | the Derivative Works; and
87 | (d) If the Work includes a "NOTICE" text file as part of its
88 | distribution, then any Derivative Works that You distribute must
89 | include a readable copy of the attribution notices contained
90 | within such NOTICE file, excluding those notices that do not
91 | pertain to any part of the Derivative Works, in at least one
92 | of the following places: within a NOTICE text file distributed
93 | as part of the Derivative Works; within the Source form or
94 | documentation, if provided along with the Derivative Works; or,
95 | within a display generated by the Derivative Works, if and
96 | wherever such third-party notices normally appear. The contents
97 | of the NOTICE file are for informational purposes only and
98 | do not modify the License. You may add Your own attribution
99 | notices within Derivative Works that You distribute, alongside
100 | or as an addendum to the NOTICE text from the Work, provided
101 | that such additional attribution notices cannot be construed
102 | as modifying the License.
103 | You may add Your own copyright statement to Your modifications and
104 | may provide additional or different license terms and conditions
105 | for use, reproduction, or distribution of Your modifications, or
106 | for any such Derivative Works as a whole, provided Your use,
107 | reproduction, and distribution of the Work otherwise complies with
108 | the conditions stated in this License.
109 | 5. Submission of Contributions. Unless You explicitly state otherwise,
110 | any Contribution intentionally submitted for inclusion in the Work
111 | by You to the Licensor shall be under the terms and conditions of
112 | this License, without any additional terms or conditions.
113 | Notwithstanding the above, nothing herein shall supersede or modify
114 | the terms of any separate license agreement you may have executed
115 | with Licensor regarding such Contributions.
116 | 6. Trademarks. This License does not grant permission to use the trade
117 | names, trademarks, service marks, or product names of the Licensor,
118 | except as required for reasonable and customary use in describing the
119 | origin of the Work and reproducing the content of the NOTICE file.
120 | 7. Disclaimer of Warranty. Unless required by applicable law or
121 | agreed to in writing, Licensor provides the Work (and each
122 | Contributor provides its Contributions) on an "AS IS" BASIS,
123 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
124 | implied, including, without limitation, any warranties or conditions
125 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
126 | PARTICULAR PURPOSE. You are solely responsible for determining the
127 | appropriateness of using or redistributing the Work and assume any
128 | risks associated with Your exercise of permissions under this License.
129 | 8. Limitation of Liability. In no event and under no legal theory,
130 | whether in tort (including negligence), contract, or otherwise,
131 | unless required by applicable law (such as deliberate and grossly
132 | negligent acts) or agreed to in writing, shall any Contributor be
133 | liable to You for damages, including any direct, indirect, special,
134 | incidental, or consequential damages of any character arising as a
135 | result of this License or out of the use or inability to use the
136 | Work (including but not limited to damages for loss of goodwill,
137 | work stoppage, computer failure or malfunction, or any and all
138 | other commercial damages or losses), even if such Contributor
139 | has been advised of the possibility of such damages.
140 | 9. Accepting Warranty or Additional Liability. While redistributing
141 | the Work or Derivative Works thereof, You may choose to offer,
142 | and charge a fee for, acceptance of support, warranty, indemnity,
143 | or other liability obligations and/or rights consistent with this
144 | License. However, in accepting such obligations, You may act only
145 | on Your own behalf and on Your sole responsibility, not on behalf
146 | of any other Contributor, and only if You agree to indemnify,
147 | defend, and hold each Contributor harmless for any liability
148 | incurred by, or claims asserted against, such Contributor by reason
149 | of your accepting any such warranty or additional liability.
150 | END OF TERMS AND CONDITIONS
151 | APPENDIX: How to apply the Apache License to your work.
152 | To apply the Apache License to your work, attach the following
153 | boilerplate notice, with the fields enclosed by brackets "{}"
154 | replaced with your own identifying information. (Don't include
155 | the brackets!) The text should be enclosed in the appropriate
156 | comment syntax for the file format. We also recommend that a
157 | file or class name and description of purpose be included on the
158 | same "printed page" as the copyright notice for easier
159 | identification within third-party archives.
160 | Copyright 2019 Google Inc.
161 | Licensed under the Apache License, Version 2.0 (the "License");
162 | you may not use this file except in compliance with the License.
163 | You may obtain a copy of the License at
164 | http://www.apache.org/licenses/LICENSE-2.0
165 | Unless required by applicable law or agreed to in writing, software
166 | distributed under the License is distributed on an "AS IS" BASIS,
167 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
168 | See the License for the specific language governing permissions and
169 | limitations under the License.
--------------------------------------------------------------------------------
/test/verify_boilerplate.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3.7
2 | # Copyright 2019 Google LLC
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # https://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | # Verifies that all source files contain the necessary copyright boilerplate
16 | # snippet.
17 |
18 | # This code is based on existing work
19 | # https://partner-code.googlesource.com/helmsman-cardinal/+/refs/heads/master/helmsman-template-project/test/verify_boilerplate.py
20 |
21 |
22 | """
23 | A runnable module to test the presence of boilerplate
24 | text in files within a repo.
25 | """
26 |
27 | from __future__ import print_function
28 | from subprocess import run, CalledProcessError
29 | import argparse
30 | import glob
31 | import os
32 | import re
33 | import sys
34 |
35 |
36 | # These directories will be omitted from header checks
37 | SKIPPED_PATHS = [
38 | 'Godeps', 'third_party', '_gopath', '_output',
39 | '.git', 'vendor', '__init__.py', 'node_modules',
40 | 'bazel-out', 'external', '3rdparty'
41 | ]
42 |
43 | # A map of regular expressions used in boilerplate validation.
44 | # The date regex is used in validating the date referenced
45 | # is the boilerplate, by ensuring it is an acceptable year.
46 | REGEXES = {
47 | # beware the Y2100 problem
48 | "date": re.compile(r'(20\d\d)')
49 | }
50 |
51 |
52 | def get_args():
53 | """Parses command line arguments.
54 | Configures and runs argparse.ArgumentParser to extract command line
55 | arguments.
56 | Returns:
57 | An argparse.Namespace containing the arguments parsed from the
58 | command line
59 | """
60 | parser = argparse.ArgumentParser()
61 |
62 | parser.add_argument("filenames",
63 | help="""A list of files to check, all in repo are
64 | checked if this is unspecified.""",
65 | nargs='*')
66 |
67 | parser.add_argument("-f", "--force-extension",
68 | default="",
69 | help="""Force an extension to compare against. Useful
70 | for files without extensions, such as runnable shell
71 | scripts .""")
72 |
73 | parser.add_argument(
74 | "-r", "--rootdir",
75 | default=None,
76 | help="""Root directory of repository. If not specified, the script will
77 | attempt to draw this value from git.""")
78 |
79 | parser.add_argument("-b", "--boilerplate-dir",
80 | default=None,
81 | help="""Directory with boilerplate files. Defaults to
82 | [root]/test/boilerplate.""")
83 |
84 | args = parser.parse_args()
85 |
86 | if not args.rootdir:
87 | ask_git = run(
88 | ["git", "rev-parse", "--show-toplevel"],
89 | capture_output=True, text=True)
90 | try:
91 | ask_git.check_returncode()
92 | except CalledProcessError:
93 | print("""No root specfied and directory does not seem to be a git
94 | repository, or git is not installed.""", file=sys.stderr)
95 | sys.exit(1)
96 | args.rootdir = ask_git.stdout.strip()
97 |
98 | if not args.boilerplate_dir:
99 | args.boilerplate_dir = os.path.join(args.rootdir, "test/boilerplate")
100 |
101 | return args
102 |
103 |
104 | def get_references(args):
105 | """Reads each reference boilerplate file's contents into an array, then
106 | adds that array to a dictionary keyed by the file extension.
107 |
108 | Returns:
109 | A dictionary of boilerplate lines, keyed by file extension.
110 | For example, boilerplate.py.txt would result in the
111 | k,v pair {".py": py_lines} where py_lines is an array
112 | containing each line of the file.
113 | """
114 | references = {}
115 |
116 | # Find all paths for boilerplate references
117 | boilerplate_paths = glob.glob(
118 | os.path.join(args.boilerplate_dir, "boilerplate.*.txt"))
119 |
120 | # Read all boilerplate references into dictionary
121 | for path in boilerplate_paths:
122 | with open(path, 'r') as ref_file:
123 | extension = os.path.basename(path).split(".")[1]
124 | ref = ref_file.read().splitlines()
125 | references[extension] = ref
126 |
127 | return references
128 |
129 |
130 | # Improvement: combine this function with `get_references`
131 | def get_preambles(args):
132 | """Reads each preamble boilerplate file's contents into an array, then
133 | adds that array to a dictionary keyed by the file extension.
134 |
135 | Returns:
136 | A dictionary of boilerplate lines, keyed by file extension.
137 | For example, boilerplate.py.preamble would result
138 | in the k,v pair {".py": py_lines} where py_lines is
139 | an array containing each line of the file
140 | (ex: "#!/usr/bin/env python3.7")
141 | """
142 | preambles = {}
143 |
144 | # Find all paths for boilerplate preambles
145 | boilerplate_paths = glob.glob(
146 | os.path.join(args.boilerplate_dir, "boilerplate.*.preamble"))
147 |
148 | # Read all boilerplate preambles into dictionary
149 | for path in boilerplate_paths:
150 | with open(path, 'r') as ref_file:
151 | extension = os.path.basename(path).split(".")[1]
152 | ref = ref_file.read().splitlines()
153 | preambles[extension] = ref
154 |
155 | return preambles
156 |
157 |
158 | def has_valid_header(filename, references, preambles, regexs, args):
159 | """Test whether a file has the correct boilerplate header.
160 | Tests each file against the boilerplate stored in refs for that file type
161 | (based on extension), or by the entire filename (eg Dockerfile, Makefile).
162 | Some heuristics are applied to remove build tags and shebangs, but little
163 | variance in header formatting is tolerated.
164 | Args:
165 | filename: A string containing the name of the file to test
166 | references: A map of reference boilerplate text,
167 | keyed by file extension
168 | preambles: A map of preamble boilerplate text, keyed by file extension
169 | regexs: a map of compiled regex objects used in verifying boilerplate
170 | Returns:
171 | True if the file has the correct boilerplate header, otherwise returns
172 | False.
173 | """
174 | # Read the entire file.
175 | with open(filename, 'r') as test_file:
176 | data = test_file.read()
177 |
178 | # Select the appropriate reference based on the extension,
179 | # or if none, the file name.
180 | basename, extension = get_file_parts(filename)
181 | if args.force_extension:
182 | extension = args.force_extension
183 | elif extension:
184 | extension = extension
185 | else:
186 | extension = basename
187 | ref = references[extension]
188 | print("Verifying boilerplate in file: %s as %s" % (
189 | os.path.relpath(filename, args.rootdir),
190 | extension))
191 |
192 | preamble = preambles.get(extension)
193 | if preamble:
194 | preamble = re.escape("\n".join(preamble))
195 | regflags = re.MULTILINE | re.IGNORECASE
196 | regex = re.compile(r"^(%s.*\n)\n*" % preamble, regflags)
197 | (data, _) = regex.subn("", data, 1)
198 |
199 | data = data.splitlines()
200 |
201 | # if our test file is smaller than the reference it surely fails!
202 | if len(ref) > len(data):
203 | return False
204 | # truncate our file to the same number of lines as the reference file
205 | data = data[:len(ref)]
206 |
207 | # if we don't match the reference at this point, fail
208 | if ref != data:
209 | return False
210 |
211 | return True
212 |
213 |
214 | def get_file_parts(filename):
215 | """Extracts the basename and extension parts of a filename.
216 | Identifies the extension as everything after the last period in filename.
217 | Args:
218 | filename: string containing the filename
219 | Returns:
220 | A tuple of:
221 | A string containing the basename
222 | A string containing the extension in lowercase
223 | """
224 | extension = os.path.splitext(filename)[1].split(".")[-1].lower()
225 | basename = os.path.basename(filename)
226 | return basename, extension
227 |
228 |
229 | def normalize_files(files, args):
230 | """Extracts the files that require boilerplate checking from the files
231 | argument.
232 | A new list will be built. Each path from the original files argument will
233 | be added unless it is within one of SKIPPED_DIRS. All relative paths will
234 | be converted to absolute paths by prepending the root_dir path parsed from
235 | the command line, or its default value.
236 | Args:
237 | files: a list of file path strings
238 | Returns:
239 | A modified copy of the files list where any any path in a skipped
240 | directory is removed, and all paths have been made absolute.
241 | """
242 | newfiles = [f for f in files if not any(s in f for s in SKIPPED_PATHS)]
243 |
244 | for idx, pathname in enumerate(newfiles):
245 | if not os.path.isabs(pathname):
246 | newfiles[idx] = os.path.join(args.rootdir, pathname)
247 | return newfiles
248 |
249 |
250 | def get_files(extensions, args):
251 | """Generates a list of paths whose boilerplate should be verified.
252 | If a list of file names has been provided on the command line, it will be
253 | treated as the initial set to search. Otherwise, all paths within rootdir
254 | will be discovered and used as the initial set.
255 | Once the initial set of files is identified, it is normalized via
256 | normalize_files() and further stripped of any file name whose extension is
257 | not in extensions.
258 | Args:
259 | extensions: a list of file extensions indicating which file types
260 | should have their boilerplate verified
261 | Returns:
262 | A list of absolute file paths
263 | """
264 | files = []
265 | if args.filenames:
266 | files = args.filenames
267 | else:
268 | for root, dirs, walkfiles in os.walk(args.rootdir):
269 | # don't visit certain dirs. This is just a performance improvement
270 | # as we would prune these later in normalize_files(). But doing it
271 | # cuts down the amount of filesystem walking we do and cuts down
272 | # the size of the file list
273 | for dpath in SKIPPED_PATHS:
274 | if dpath in dirs:
275 | dirs.remove(dpath)
276 | for name in walkfiles:
277 | pathname = os.path.join(root, name)
278 | files.append(pathname)
279 | files = normalize_files(files, args)
280 | outfiles = []
281 | for pathname in files:
282 | basename, extension = get_file_parts(pathname)
283 | extension_present = extension in extensions or basename in extensions
284 | if args.force_extension or extension_present:
285 | outfiles.append(pathname)
286 | return outfiles
287 |
288 |
289 | def main(args):
290 | """Identifies and verifies files that should have the desired boilerplate.
291 | Retrieves the lists of files to be validated and tests each one in turn.
292 | If all files contain correct boilerplate, this function terminates
293 | normally. Otherwise it prints the name of each non-conforming file and
294 | exists with a non-zero status code.
295 | """
296 | refs = get_references(args)
297 | preambles = get_preambles(args)
298 | filenames = get_files(refs.keys(), args)
299 | nonconforming_files = []
300 | for filename in filenames:
301 | if not has_valid_header(filename, refs, preambles, REGEXES, args):
302 | nonconforming_files.append(filename)
303 | if nonconforming_files:
304 | print('%d files have incorrect boilerplate headers:' % len(
305 | nonconforming_files))
306 | for filename in sorted(nonconforming_files):
307 | print(os.path.relpath(filename, args.rootdir))
308 | sys.exit(1)
309 | else:
310 | print('All files examined have correct boilerplate.')
311 |
312 |
313 | if __name__ == "__main__":
314 | ARGS = get_args()
315 | main(ARGS)
316 |
--------------------------------------------------------------------------------