Skip to content

Commit 78e27e0

Browse files
authored
Merge pull request #29 from nginxinc/zaowang/add-azure-pipeline-repo
added azure_pipeline in this repo
2 parents 41c7cbd + 18c1090 commit 78e27e0

29 files changed

+750
-0
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Build and Release for Azure Pipeline
2+
on:
3+
push:
4+
branches:
5+
- main
6+
7+
jobs:
8+
build:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- uses: actions/checkout@v2
13+
14+
- name: Set up Node.js
15+
uses: actions/setup-node@v2
16+
with:
17+
node-version: '14'
18+
19+
- name: Install dependencies and build
20+
run: |
21+
cd ./azure-pipeline/src
22+
npm install
23+
cd ..
24+
25+
- name: Install tfx-cli
26+
run: |
27+
npm install -g tfx-cli
28+
29+
- name: Create extension
30+
run: |
31+
tfx extension create --manifest-globs vss-extension.json
32+
33+
- name: Upload VSIX file
34+
uses: actions/upload-artifact@v2
35+
with:
36+
name: VSIX file
37+
path: ./azure-pipeline/*.vsix

azure-pipeline/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
dist
3+
package-lock.json
4+
coverage

azure-pipeline/example.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
trigger:
2+
- main
3+
4+
pool:
5+
vmImage: ubuntu-latest
6+
7+
steps:
8+
- task: nginx-config-push@0
9+
inputs:
10+
serviceConnectionName: '(Enter the name of the service connection to Azure)'
11+
resourceGroupName: '(Enter the name of the Azure resource group of the deployment)'
12+
subscriptionId: '(Enter the Azure subscription ID of the deployment)'
13+
deploymentName: '(Enter the name for this deployment)'
14+
configDirectoryInRepo: '(Enter the relative path to the Nginx configuration directory in the repository)'
15+
configDirectoryInDeployment: '(Enter the target path for the Nginx configuration directory in the deployment environment, e.g., /etc/nginx)'
16+
rootConfigFileName: '(Enter the name of the root configuration file and make sure it is in the config directory. e.g., nginx.conf)'
5.63 KB
Loading
37 KB
Loading
82.7 KB
Loading
28.5 KB
Loading
30.7 KB
Loading
71 KB
Loading
43.6 KB
Loading
Loading
Loading
117 KB
Loading

azure-pipeline/jest.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {
2+
roots: ['<rootDir>/src'],
3+
transform: {
4+
'^.+\\.tsx?$': 'ts-jest',
5+
},
6+
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
7+
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
8+
};

azure-pipeline/package.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "nginx-for-azure-config-sync",
3+
"version": "2.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"start": "tsc && node src/dist/index.js",
8+
"test": "jest src/test --verbose --coverage"
9+
},
10+
"keywords": [],
11+
"author": "",
12+
"license": "ISC",
13+
"dependencies": {
14+
"@azure/identity": "^2.1.0",
15+
"@types/tar": "^6.1.1",
16+
"axios": "^0.27.2",
17+
"azure-pipelines-task-lib": "^4.0.0-preview",
18+
"fs-extra": "^10.1.0",
19+
"gunzip-maybe": "^1.4.2",
20+
"tar": "^6.1.11",
21+
"tar-fs": "^2.1.1"
22+
},
23+
"devDependencies": {
24+
"@types/gunzip-maybe": "^1.4.0",
25+
"@types/jest": "^29.5.3",
26+
"@types/mocha": "^9.1.1",
27+
"@types/node": "^18.0.4",
28+
"@types/q": "^1.5.5",
29+
"jest": "^29.6.2",
30+
"sync-request": "^6.1.0",
31+
"ts-jest": "^29.1.1"
32+
}
33+
}

azure-pipeline/readme.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Azure Pipeline task: NGINX for Azure Configuration Push
2+
3+
## Overview
4+
5+
This is a customed Azure pipeline task that automates the process of synchronizing NGINX configuration files. With this pipeline, the configuration files that are stored and managed in Azure DevOps or GitHub repositories can now be automatically packaged and uploaded to NGINX for Azure deployments.
6+
7+
When creating and updating the configuration of a NGINX for Azure deployment, an automated pipeline task would be very helpful. There are three main advantages of a pipeline task compared to a manual updation:
8+
9+
##### 1. Automation.
10+
11+
A pipeline task can be triggered immediately if designated NGINX coniguration files have changes. This would increase effectiveness when there are frequent modifications and updates.
12+
13+
##### 2. Version control.
14+
15+
NGINX configuration files could be stored in GitHub or Azure DevOps repositories. In this way, all configure changes would be able to be traced and rolled back if there is a mistake.
16+
17+
##### 3. Security.
18+
Pipelines are running in secured and reliable agents, where data is transmitted within Azure services, hence reduced unnecessary data distribution and delivery.
19+
20+
NGINX for Azure currently supports both **GitHub Action** pipeline and **Azure DevOps** pipeline task. While there are some authentication and repository supporting difference, the purpose and functionality of the two pipelines have no essential distinction. For details on GitHub Action pipeline, please check out NGINX for Azure Deployment Action for more details. For Azure DevOps pipeline task, please follow the instructions below.
21+
22+
### Pipeline Task Set-up Guideline
23+
24+
Basically, all that the pipeline task needed is a .yml task written as follows,
25+
26+
```yaml
27+
# example.yml
28+
29+
trigger:
30+
- main
31+
32+
pool:
33+
vmImage: ubuntu-latest
34+
35+
steps:
36+
- task: nginx-config-push@0
37+
inputs:
38+
serviceConnectionName: '(Enter the name of the service connection to Azure)'
39+
resourceGroupName: '(Enter the name of the Azure resource group of the deployment)'
40+
subscriptionId: '(Enter the Azure subscription ID of the deployment)'
41+
deploymentName: '(Enter the name for this deployment)'
42+
configDirectoryInRepo: '(Enter the relative path to the Nginx configuration directory in the repository)'
43+
configDirectoryInDeployment: '(Enter the target path for the Nginx configuration directory in the deployment environment, e.g., /etc/nginx/)'
44+
rootConfigFileName: '(Enter the name of the root configuration file and make sure it is in the config directory. e.g., nginx.conf)'
45+
46+
```
47+
48+
What is needed right now is to get values of the needed variables with following steps:
49+
50+
51+
##### 1. Get your NGINX configuration file ready in the repository.
52+
53+
Upload your NGINX configuration files onto any folder of your Azure DevOps repository. Then update the ‘configDirectoryInRepo’ to the path of the configuration folder in your repository.
54+
55+
![Image](images/readme-guidline-01.png)
56+
57+
58+
##### 2. Make sure your NGINX for Azure deployment is working properly.
59+
60+
Deploy a NGINX for Azure resource. See Deploy NGINX for Azure | NGINX for Azure Docs. Once your deployment is ready, you will be able to fill in the “resourceGroupName”, “subscriptionId” and the “deploymentName” variable from the portal overview board.
61+
62+
![Image](images/readme-guidline-02.png)
63+
64+
65+
66+
##### 3. Create a service connetion and grant permission for the pipeline.
67+
68+
This part would require a little bit more work to set up.
69+
A service connection gives you access to resources in your Azure subscription from your Azure DevOps project.
70+
1. In Azure DevOps, go to the project that contains your target pipeline. In the lower-left corner, select Project settings.
71+
2. Under Pipelines, select Service connections. In the upper-right corner, select New service connection.
72+
3. In New service connection, select Azure Resource Manager.
73+
74+
![Image](images/readme-guidline-03.png)
75+
76+
4. In the Authentication method dialog, select Service principal (automatic) to create a new service principal or select Service principal (manual) to use an existing service principal.
77+
5. Enter your subscription and resource and a name for your service connection.
78+
79+
Also, you may need to assign a proper role assignment to the service connection so that our pipeline can access NGINX for azure deployments. You can add Contributor as your role assignment to this service connection.
80+
81+
![Image](images/readme-guidline-04.png)
82+
83+
84+
Go back to the Azure DevOps project settings and click into the “service connection” tab. Remember the name displayed in the service connections list. This will be the value of ‘serciveConnectionName’ in the .yml file.
85+
86+
![Image](images/readme-guidline-05.png)
87+
88+
89+
##### 4. Install the pipeline task and fill all the needed variables into the .yml file on Azure DevOps.
90+
91+
Go to marketplace and search our pipeline task. Once you have the extension installed on your Azure DevOps organization, you will be able to create a pipeline task with the stated .yml file template and use our customized task conveniently. You can also use text editor of the pipeline to utilize a simple UI of this pipeline task.
92+
93+
94+
That's it! Once you have all the above settings done and a pipeline created on the Azure DevOps, your NGINX configuration will be automatically updated to your designated NGINX for Azure deployment resource once you have any changes to the files in the repository and the pipeline is triggered.

azure-pipeline/src/index.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import taskLib = require('azure-pipelines-task-lib/task');
2+
import { AxiosRequestConfig } from "axios";
3+
import { updateUserConfig } from './utils/api';
4+
import {
5+
API_VERSION,
6+
SCOPE,
7+
OUTPUT_PATH,
8+
INPUT,
9+
} from './utils/constants';
10+
import {
11+
FileHandler,
12+
} from './utils/file-handler';
13+
import {
14+
validatedEnvVar,
15+
validatedAuthParams,
16+
} from './utils/validation';
17+
import {
18+
ClientSecretCredential,
19+
AuthenticationError,
20+
} from '@azure/identity';
21+
import { info } from 'console';
22+
23+
/**
24+
* Class ConfigUpdater is responsible for updating the Nginx configuration.
25+
* It provides methods to authenticate, compress, and upload user-specific configurations
26+
* using Azure identity and other utilities.
27+
*/
28+
class ConfigUpdater {
29+
30+
private f = new FileHandler;
31+
32+
/**
33+
* Retrieves the authorization token for later authentication by using Azure identity.
34+
* @returns {Promise<any>} The authorization token.
35+
* @throws {Error} If the authorization parameters validation fails.
36+
* @throws {AuthenticationError} If the access token fetch fails.
37+
*/
38+
private getAuthorizationTokenFromKey = async (
39+
tenantID: string,
40+
clientID: string,
41+
servicePrincipalKey: string
42+
) => {
43+
try {
44+
const clientSecretCredential: ClientSecretCredential = new ClientSecretCredential(
45+
tenantID, clientID, servicePrincipalKey
46+
);
47+
const accessToken = await clientSecretCredential.getToken(SCOPE);
48+
if (!accessToken) {
49+
throw new AuthenticationError(0, {
50+
error: 'Access token fetch failed'
51+
});
52+
}
53+
return accessToken;
54+
} catch (e) {
55+
throw new Error("Authorization parameters validation failed");
56+
}
57+
}
58+
59+
/**
60+
* Compresses the configuration file folder and prepares it for sending to the backend.
61+
* @returns {Promise<Object>} An object containing properties related to the compressed file.
62+
*/
63+
private getConvertedFileObject = async () => {
64+
const file = await this.f.compressFile(
65+
validatedEnvVar(INPUT.source),
66+
validatedEnvVar(INPUT.target),
67+
OUTPUT_PATH,
68+
);
69+
return {
70+
properties: {
71+
rootFile: `${validatedEnvVar(INPUT.target)}/${validatedEnvVar(INPUT.rootFile)}`.replace(/\/\/+/g, '/'),
72+
package: {
73+
data: this.f.convertFileToBase64String(file),
74+
}
75+
}
76+
};
77+
}
78+
79+
/**
80+
* Constructs the request configuration, including the bearer token from the user's input.
81+
* @returns {Promise<AxiosRequestConfig>} The request configuration.
82+
*/
83+
private getRequestConfig = async () => {
84+
const token = await this.getAuthorizationTokenFromKey(
85+
validatedAuthParams('tenantid'),
86+
validatedAuthParams('servicePrincipalId'),
87+
validatedAuthParams('servicePrincipalKey'),
88+
);
89+
const config: AxiosRequestConfig = { headers: {
90+
Authorization: `Bearer ${token.token}`,
91+
}}
92+
return config;
93+
}
94+
95+
/**
96+
* Constructs the required parameters for the uploading API request.
97+
* @returns {Object} An object containing the required parameters for the request,
98+
* including subscription ID, resource group name, deployment name, and API version.
99+
*/
100+
private getRequestResource = () => {
101+
return {
102+
subscriptionId: validatedEnvVar(INPUT.subscription),
103+
resourceGroupName: validatedEnvVar(INPUT.resource),
104+
deploymentName: validatedEnvVar(INPUT.deployment),
105+
apiVersion: API_VERSION,
106+
}
107+
}
108+
109+
/**
110+
* Main function to compress the file, retrieve authentication info, and call the API to update the Nginx configuration.
111+
* @throws {Error} If the Nginx configuration uploading fails.
112+
*/
113+
updateNginxConfig = async () => {
114+
console.log('updateNginxConfig...')
115+
try {
116+
const res = await updateUserConfig(
117+
this.getRequestResource(),
118+
await this.getConvertedFileObject(),
119+
await this.getRequestConfig(),
120+
);
121+
console.log('Nginx config successfully uploaded!');
122+
} catch (error) {
123+
taskLib.setResult(taskLib.TaskResult.Failed, error as any);
124+
throw new Error("Nginx config uploading failed!");
125+
}
126+
};
127+
}
128+
129+
const updater = new ConfigUpdater();
130+
updater.updateNginxConfig();

0 commit comments

Comments
 (0)