Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Proxy Endpoint Unifier #687

Merged
merged 21 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d9489f2
feat: Added proxy-endpoint-unifier source
anaik91 Aug 14, 2023
a7c8a03
feat: updated Licenses in the proxy-endpoint-unifier wrapper
anaik91 Aug 14, 2023
b39a38f
feat: removed comments & added newlines in the proxy-endpoint-unifier…
anaik91 Aug 14, 2023
18badee
feat: addressed flake8 for main proxy-endpoint-unifier wrapper
anaik91 Aug 14, 2023
7ccef07
feat: addressed flake8 for xorhybrid proxy-endpoint-unifier wrapper
anaik91 Aug 14, 2023
b812452
feat: addressed flake8 for utils proxy-endpoint-unifier wrapper
anaik91 Aug 14, 2023
740316e
feat: added test scripts for proxy-endpoint-unifier
anaik91 Aug 14, 2023
cf6d1ff
feat: added missing licenses
anaik91 Aug 14, 2023
f3ff9a0
feat: fixed shell lint and updated README.md
anaik91 Aug 14, 2023
655e5af
feat: updated CODEOWNERS
anaik91 Aug 14, 2023
cbae732
fix: added execute permissions on pipeline.sh
anaik91 Aug 15, 2023
06fd5fa
feat: added pip package install to pipeline-runner
anaik91 Aug 15, 2023
b245415
fix: changes to proxy endpoint unifier readme
OmidTahouri Aug 15, 2023
795f140
Merge pull request #1 from OmidTahouri/feature/split-proxy-endpoints
anaik91 Aug 15, 2023
5f92893
fix: removing manifests , fixing Cache Policy & target endpoint
anaik91 Aug 15, 2023
f821756
fix: added support for FS read when proxy root xml is empty
anaik91 Aug 16, 2023
d2db718
fix: addressed PR comments
anaik91 Aug 16, 2023
d80c5b4
feat: added support for PostClientFlow
anaik91 Aug 16, 2023
fd3ba09
feat: modified pipeline.sh & README
anaik91 Aug 16, 2023
868e6a0
Merge branch 'main' into main
OmidTahouri Aug 16, 2023
af3e94e
feat: added zip packaged to pipeline runner
anaik91 Aug 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@
/tools/oas-configurable-proxy @danistrebel
/tools/pipeline-linter @seymen @danistrebel
/tools/pipeline-runner @seymen @danistrebel
/tools/sf-dependency-list @yuriylesyuk
/tools/sf-dependency-list @yuriylesyuk
/tools/proxy-endpoint-unifier @anaik91
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ Apigee products.
A tool to generate topologically sorted Shared Flow dependencies.
- [Apigee Envoy Quickstart Toolkit](tools/apigee-envoy-quickstart) -
A tool to set up the sample deployments of Apigee Envoy.
- [Apigee API Proxy Endpoint Unifier](tools/proxy-endpoint-unifier) -
A tool to unify/split proxy endpoints based on API basepath.

## Labs

Expand Down
3 changes: 2 additions & 1 deletion tools/pipeline-runner/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ RUN apk add --no-cache \
freetype-dev \
harfbuzz \
ca-certificates \
ttf-freefont
ttf-freefont \
py-pip

# Reduce nighly log (note: -ntp requires maven 3.6.1+)
RUN mv /usr/bin/mvn /usr/bin/_mvn &&\
Expand Down
46 changes: 46 additions & 0 deletions tools/proxy-endpoint-unifier/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Apigee API Proxy Endpoint Unifier

Apigee X and hybrid have a limitation of hosting up to 5 Proxy Endpoints per API Proxy. Apigee Edge has no such limitation.
The objective of this tool is to take a proxy bundle and intelligently convert its proxy endpoints into logically
grouped conditional flows, in order to stay within the Proxy Endpoint limit.

## Disclaimer
This is not an officially supported Google product.

## Prerequisites
* `python3`
* Please install the required Python dependencies
```
python3 -m pip install -r requirements.txt
```
* Please fill in `input.properties`
```
[common]
OmidTahouri marked this conversation as resolved.
Show resolved Hide resolved
input_apis=apis # Folder Containing Extracted Proxy Bundles
processed_apis=transformed # Folder to export transformed Proxies to
proxy_bundle_directory=transformed_bundles # Folder to export transformed Proxies Bundles (zip) to
proxy_endpoint_count=4 # Number of Proxy Endpoints to retain while transforming (1-5)
debug=false # Flag to export debug logs

[validate]
enabled=true # Flag to enable proxy validation
gcp_project_id=xxx-xxx-xxx # Apigee Project for proxy validation
```

* If enabling validation, please run the following command to authenticate against Apigee APIs:

```
export APIGEE_ACCESS_TOKEN=$(gcloud auth print-access-token)
```


## Running
Run the script as below
```
python3 main.py
```


## Copyright
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we remove this as the solution inherits the license from the repo level?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1


Copyright 2023 Google LLC. This software is provided as-is, without warranty or representation for any use or purpose. Your use of it is subject to your agreement with Google.
24 changes: 24 additions & 0 deletions tools/proxy-endpoint-unifier/input.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

[common]
input_apis=test/api_bundles
processed_apis=test/transformed
proxy_bundle_directory=test/transformed_bundles
proxy_endpoint_count=4
debug=false

[validate]
enabled=true
gcp_project_id=xxx-xxx-xxx
132 changes: 132 additions & 0 deletions tools/proxy-endpoint-unifier/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#!/usr/bin/python

# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import sys
from xorhybrid import ApigeeXorHybrid
import utils


def main():
cfg = utils.parse_config('input.properties')
proxy_dir = cfg['common']['input_apis']
proxy_dest_dir = cfg['common']['processed_apis']
proxy_bundle_directory = cfg['common']['proxy_bundle_directory']
export_debug_file = cfg.getboolean('common', 'debug')
validation_enabled = cfg.getboolean('validate', 'enabled')
utils.delete_folder(proxy_dest_dir)
utils.delete_folder(proxy_bundle_directory)
utils.create_dir(proxy_bundle_directory)
proxy_endpoint_count = utils.get_proxy_endpoint_count(cfg)
proxies = utils.list_dir(proxy_dir)

final_dict = {}
processed_dict = {}

for each_dir in proxies:
each_proxy_dict = utils.read_proxy_artifacts(
f"{proxy_dir}/{each_dir}",
utils.parse_proxy_root(f"{proxy_dir}/{each_dir}")
)
if len(each_proxy_dict) > 0:
each_proxy_rel = utils.get_proxy_objects_relationships(
each_proxy_dict)
final_dict[each_dir] = each_proxy_dict
processed_dict[each_dir] = each_proxy_rel

processing_final_dict = final_dict.copy()

path_group_map = {}
for each_api, each_api_info in processed_dict.items():
path_group_map[each_api] = utils.get_api_path_groups(each_api_info)

grouped_apis = {}
for each_api, base_path_info in path_group_map.items():
grouped_apis[each_api] = utils.group_paths_by_path(
base_path_info, proxy_endpoint_count)

bundled_group = {}
for each_api, grouped_api in grouped_apis.items():
bundled_group[each_api] = utils.bundle_path(grouped_api)

merged_pes = {}
merged_objects = {}
for each_api, grouped_api in bundled_group.items():
print(f'Processing API => {each_api} with {len(grouped_api)} groups')
for index, each_group in enumerate(grouped_api):
merged_objects[f"{each_api}_{index}"] = {
'Policies': [],
'TargetEndpoints': [],
'ProxyEndpoints': []
}
for each_path, pes in each_group.items():
each_pe = '-'.join(pes)
merged_pes[each_pe] = utils.merge_proxy_endpoints(
processing_final_dict[each_api],
each_path,
pes
)
merged_objects[f"{each_api}_{index}"]['Name'] = f"{final_dict[each_api]['proxyName']}_{index}" # noqa
OmidTahouri marked this conversation as resolved.
Show resolved Hide resolved
merged_objects[f"{each_api}_{index}"]['Policies'].extend( # noqa
[ item for pe in pes for item in processed_dict[each_api][pe]['Policies']]) # noqa
merged_objects[f"{each_api}_{index}"]['TargetEndpoints'].extend( # noqa
[ item for pe in pes for item in processed_dict[each_api][pe]['TargetEndpoints']]) # noqa
merged_objects[f"{each_api}_{index}"]['Policies'] = list(set(merged_objects[f"{each_api}_{index}"]['Policies'])) # noqa
merged_objects[f"{each_api}_{index}"]['TargetEndpoints'] = list(set(merged_objects[f"{each_api}_{index}"]['TargetEndpoints'])) # noqa
merged_objects[f"{each_api}_{index}"]['ProxyEndpoints'].append(each_pe) # noqa

for each_api, grouped_api in bundled_group.items():
for index, each_group in enumerate(grouped_api):
utils.clone_proxies(
f"{proxy_dir}/{each_api}",
f"{proxy_dest_dir}/{each_api}_{index}",
merged_objects[f"{each_api}_{index}"],
merged_pes,
proxy_bundle_directory
)

files = {
'final_dict': final_dict,
'processed_dict': processed_dict,
'path_group_map': path_group_map,
'grouped_apis': grouped_apis,
'bundled_group': bundled_group,
'merged_pes': merged_pes,
'merged_objects': merged_objects,
}
if export_debug_file:
utils.export_debug_log(files)

if validation_enabled:
errors = {}
gcp_project_id = cfg['validate']['gcp_project_id']
x = ApigeeXorHybrid(gcp_project_id)
anaik91 marked this conversation as resolved.
Show resolved Hide resolved
x.set_auth_header(os.getenv('APIGEE_ACCESS_TOKEN'))
result = {}
bundled_proxies = utils.list_dir(proxy_bundle_directory)
for each_bundle in bundled_proxies:
validation = x.validate_api('apis',f"{proxy_bundle_directory}/{each_bundle}") # noqa
if not validation:
errors[each_bundle] = validation
result[each_bundle] = validation
print(f"{each_bundle} ==> Validation : {validation}")
if len(errors) > 0:
print('ERROR: Some Validations have failed')
sys.exit(1)


if __name__ == '__main__':
main()
48 changes: 48 additions & 0 deletions tools/proxy-endpoint-unifier/pipeline.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/sh

# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -e

SCRIPTPATH="$( cd "$(dirname "$0")" || exit >/dev/null 2>&1 ; pwd -P )"

# Clean up previously generated files
rm -rf "$SCRIPTPATH/input.properties"
rm -rf "$SCRIPTPATH/transformed"
rm -rf "$SCRIPTPATH/transformed_bundles"

# Generate input file
cat > "$SCRIPTPATH/input.properties" << EOF
[common]
input_apis=$SCRIPTPATH/test/api_bundles
processed_apis=$SCRIPTPATH/transformed
proxy_bundle_directory=$SCRIPTPATH/transformed_bundles
anaik91 marked this conversation as resolved.
Show resolved Hide resolved
proxy_endpoint_count=4
debug=true

[validate]
enabled=true
gcp_project_id=$APIGEE_ORG
anaik91 marked this conversation as resolved.
Show resolved Hide resolved
EOF

# Install Dependencies
python3 -m pip install -r "$SCRIPTPATH/requirements.txt"

# Generate Gcloud Acccess Token
APIGEE_ACCESS_TOKEN=$(gcloud auth print-access-token)
export APIGEE_ACCESS_TOKEN

# Execute Utility
python3 "$SCRIPTPATH/main.py"
OmidTahouri marked this conversation as resolved.
Show resolved Hide resolved
16 changes: 16 additions & 0 deletions tools/proxy-endpoint-unifier/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

xmltodict==0.13.0
requests==2.28.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
Copyright 2023 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ExtractVariables name="ExtractVariables-3">
<Source>response</Source>
<JSONPayload>
<Variable name="latitude" type="float">
<JSONPath>$.results[0].geometry.location.lat</JSONPath>
</Variable>
<Variable name="longitude" type="float">
<JSONPath>$.results[0].geometry.location.lng</JSONPath>
</Variable>
</JSONPayload>
<VariablePrefix>geocoderesponse</VariablePrefix>
</ExtractVariables>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
Copyright 2023 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PopulateCache async="false" continueOnError="false" enabled="true" name="Populate-Cache-1">
<DisplayName>Populate Cache-1</DisplayName>
<Properties/>
<CacheKey>
<Prefix>my</Prefix>
<KeyFragment>test</KeyFragment>
</CacheKey>
<CacheResource>test-cache</CacheResource>
<Scope>Exclusive</Scope>
<ExpirySettings>
<TimeoutInSec>3600</TimeoutInSec>
</ExpirySettings>
<Source>request.content</Source>
</PopulateCache>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
Copyright 2023 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<Quota async="false" continueOnError="false" enabled="true" name="Quota-1" type="calendar">
<DisplayName>Quota-1</DisplayName>
<Properties/>
<Allow count="2000" countRef="request.header.allowed_quota"/>
<Interval ref="request.header.quota_count">1</Interval>
<Distributed>false</Distributed>
<Synchronous>false</Synchronous>
<TimeUnit ref="request.header.quota_timeout">month</TimeUnit>
<StartTime>2023-8-7 12:00:00</StartTime>
<AsynchronousConfiguration>
<SyncIntervalInSeconds>20</SyncIntervalInSeconds>
<SyncMessageCount>5</SyncMessageCount>
</AsynchronousConfiguration>
</Quota>
Loading
Loading