Initial open source commit
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
# Default execution environment for workers
|
||||
ORG_ID=Shuffle
|
||||
ENVIRONMENT_NAME=Shuffle
|
||||
|
||||
# Other configs
|
||||
BACKEND_HOSTNAME=shuffle-backend
|
||||
BACKEND_PORT=5010
|
||||
OUTER_HOSTNAME=192.168.3.6
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
*node_modules/
|
||||
*build/
|
||||
*.lock
|
||||
*.swo
|
||||
*.swp
|
||||
*.swn
|
||||
*__pycache__*
|
||||
|
||||
Shuffle-*.json
|
||||
|
||||
functions/generated_apps
|
||||
*.zip
|
||||
|
||||
*.png
|
||||
*.jpeg
|
||||
*.jpg
|
||||
|
||||
backend/onprem/app_sdk/apps
|
||||
*test.py
|
||||
@@ -0,0 +1,120 @@
|
||||
# Shuffle
|
||||
[Shuffler](https://shuffler.io) is an automation platform for your security stack. It leverages docker for scaling and OpenAPI for integrations. It has the possibility to run across multiple isolated environments, and gives you powerful tools to track progress.
|
||||
|
||||
## Try it
|
||||
Check out the [installation guide](https://github.com/frikky/shuffle/blob/master/install-guide.md)
|
||||
|
||||
## Documentation
|
||||
Documentation can be found on https://shuffler.io/docs/about or in your own instance. Currently lacking:
|
||||
* API documentation
|
||||
* Updates after migrating from SaaS to open source
|
||||
|
||||
## Features
|
||||
* Premade workflows for Email, TheHive, C rtex MISP
|
||||
* Premade apps for a number of security tools
|
||||
* Simple workflow editor
|
||||
* App creator for [OpenAPI](https://github.com/frikky/OpenAPI-security-definitions)
|
||||
* Easy to learn Python library for custom apps
|
||||
|
||||
## License
|
||||
Shuffle is an open source and free software released under the AGPL (Affero General Public License).
|
||||
|
||||
### Setup - Local
|
||||
Frontend - requires [npm](https://nodejs.org/en/download/)/[yarn](https://yarnpkg.com/lang/en/docs/install/#debian-stable)/your preferred manager. Runs independently from backend - edit frontend/src/App.yaml to change from localhost to prod setting.
|
||||
```bash
|
||||
cd frontend
|
||||
npm i
|
||||
npm start
|
||||
```
|
||||
|
||||
Backend - API calls - requires [>=go1.13](https://golang.org/dl/) and [gcloud](https://cloud.google.com/sdk/install)
|
||||
```bash
|
||||
cd backend/go-app
|
||||
go build
|
||||
sudo apt -y update && sudo apt -y upgrade && sudo apt install -y google-cloud-sdk-app-engine-python google-cloud-sdk-app-engine-python google-cloud-sdk-datastore-emulator google-cloud-sdk-app-engine-go
|
||||
go run *.go
|
||||
```
|
||||
|
||||
### Project overview
|
||||
Below is the folder structure with a short explanation
|
||||
```bash
|
||||
├── README.md # :)
|
||||
├── deploy.sh # Simple oneliner script to build and deploy the code to gcloud
|
||||
├── backend # Contains directly backend related code. Go with sh tests
|
||||
├── frontend # Contains frontend code. ReactJS and cytoscape. Horrible code :)
|
||||
├── app_gen # Contains code generation projects for OpenAPI or PythonLib -> Shuffler app
|
||||
├── functions # Contains google cloud function code mainly.
|
||||
│ ├── apps # Some of the existing apps, manually made mostly
|
||||
│ ├── generated_apps # Some of the autogenerated apps
|
||||
│ ├── newworker # The worker that handles a workflow as a google cloud function
|
||||
│ ├── static_baseline.py # Static code used by stitcher.go to generate code
|
||||
│ ├── stitcher.go # Attempts to stitch together an app and deploy it to cloud functions and (TBD: Docker hub)
|
||||
│ └── triggers # Custom triggers used in https://shuffler.io/workflows
|
||||
│ ├── onprem # All code for onprem solutions (https://shuffler.io/docs/hybrid for short doc) _mostly_ reflects google cloud. Should be deprecated somehow and use the same code.
|
||||
├── openintegrationhub # Here to remind me that openintegrationhub is a thing
|
||||
├── legacy # Legacy README. Contains A LOT of useful information about what I found with WALKOFF
|
||||
└── tmp # Some legacy code, not yet ready to be removed
|
||||
```
|
||||
|
||||
# Architecture
|
||||
A basic image of how everything fits together, including legacy (left side)
|
||||

|
||||
|
||||
# Technology
|
||||
GCP was chosen because why not use the best thingies. "Serverless" \o/
|
||||
```bash
|
||||
├── languages
|
||||
│ ├── Go # I like go, which is why go.
|
||||
│ ├── Python3.7 # 3.7 specifically because of f-strings and 2.7 deprecation in 2020
|
||||
│ ├── Javascript # Frontend stuff. Uses ReactJS + Cytoscape for visualization
|
||||
│ ├── sh/Bash # Basic testing and some deployment stuff
|
||||
├── gcloud
|
||||
│ ├── appengine # Hosting frontend and backend. Currently on a free plan which is nice :)
|
||||
│ ├── cloud functions # Runs the "apps", "triggers" and other things
|
||||
│ ├── datastore # Database - TODO before live: Move to firebase
|
||||
│ ├── storage # Save datablobs and information before deployment
|
||||
│ ├── pubsub # Used to instantly run cloud functions
|
||||
│ ├── scheduler # Schedules can be triggers
|
||||
├── onprem
|
||||
│ ├── Docker # Runs the same cloud functions. I didn't like the thought of proxies
|
||||
```
|
||||
|
||||
# Current focus(es) AKA todo
|
||||
1. Make workflows work 99%+ of the time. This is a challenge with onprem + cloud stuff. Cloud sometimes breaks currently because of workers
|
||||
2. Add user run statistics (e.g. how many runs of each workflow, how many failures etc.)
|
||||
3. X - Fix OpenAPI app generator
|
||||
4. Fix error overview in workflows
|
||||
5. X - Better GUI (improved, but not good)
|
||||
6. Have default workflows
|
||||
|
||||
# How to Add a trigger / custom thing
|
||||
1. Add it to TriggersView in AngularWorkflow.js
|
||||
2. Add it to RightSideBar for trigger
|
||||
|
||||
# Migration
|
||||
There will be a major overhaul to the backend specifically. I'm currently moving and updating the following:
|
||||
- Create dockerfiles and a single runscript
|
||||
- * App creator - (Cloud function -> Docker)
|
||||
- * Workflows - Run workflows locally
|
||||
- * App list - IMPORT EXISTING APPS
|
||||
- * Dockerfiles - Load the ones that are in workflows with a new version
|
||||
- * Docker-compose- Frontend, backend, db & orborus
|
||||
- * Configuration - Write setup documentation - Did for docker
|
||||
- Workflows - IMPORT DEFAULT WORKFLOWS - Create some towards e.g. TheHive & MISP.
|
||||
- Documentation - General documentation /docs rewrite
|
||||
- API doc - 1. In Shuffle. 2. In e.g. python
|
||||
- Remove orborus? Can deploy straight, but that would be weird.
|
||||
- Add secret to orborus
|
||||
- Change workflow name
|
||||
- Remove registration and add user screen
|
||||
- Add external and internal hostname for orborus & worker
|
||||
|
||||
```
|
||||
# 1. export DATASTORE_EMULATOR_HOST=0.0.0.0:8000
|
||||
# 2. docker run -p 8000:8000 google/cloud-sdk gcloud beta emulators datastore start --project=shuffle --host-port 0.0.0.0:8000 --no-store-on-disk
|
||||
```
|
||||
* Mail: Use appengine and connect to sendmail
|
||||
|
||||
### Migration issues:
|
||||
* Some workflows where items have multiple parents don't work.
|
||||
* Fix dummy.json (GCP config) - bypass this somehow.
|
||||
@@ -0,0 +1,235 @@
|
||||
components:
|
||||
schemas:
|
||||
tmp0:
|
||||
properties:
|
||||
cb_version:
|
||||
type: string
|
||||
company_name:
|
||||
type: string
|
||||
copied_mod_len:
|
||||
type: string
|
||||
digsig_issuer:
|
||||
type: string
|
||||
digsig_prog_name:
|
||||
type: string
|
||||
digsig_publisher:
|
||||
type: string
|
||||
digsig_result:
|
||||
type: string
|
||||
digsig_result_code:
|
||||
type: string
|
||||
digsig_sign_time:
|
||||
type: string
|
||||
digsig_subject:
|
||||
type: string
|
||||
endpoint:
|
||||
type: string
|
||||
event_partition_id:
|
||||
type: string
|
||||
facet_id:
|
||||
type: string
|
||||
file_desc:
|
||||
type: string
|
||||
file_version:
|
||||
type: string
|
||||
group:
|
||||
type: string
|
||||
host_count:
|
||||
type: string
|
||||
internal_name:
|
||||
type: string
|
||||
is_64bit:
|
||||
type: string
|
||||
is_executable_image:
|
||||
type: string
|
||||
last_seen:
|
||||
type: string
|
||||
legal_copyright:
|
||||
type: string
|
||||
md5:
|
||||
type: string
|
||||
observed_filename:
|
||||
type: string
|
||||
orig_mod_len:
|
||||
type: string
|
||||
original_filename:
|
||||
type: string
|
||||
os_type:
|
||||
type: string
|
||||
product_name:
|
||||
type: string
|
||||
product_version:
|
||||
type: string
|
||||
server_added_timestamp:
|
||||
type: string
|
||||
signed:
|
||||
type: string
|
||||
timestamp:
|
||||
type: string
|
||||
watchlists:
|
||||
type: string
|
||||
type: object
|
||||
tmp1:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
type: object
|
||||
tmp2:
|
||||
properties:
|
||||
childproc_count:
|
||||
type: string
|
||||
cmdline:
|
||||
type: string
|
||||
comms_ip:
|
||||
type: string
|
||||
crossproc_count:
|
||||
type: string
|
||||
emet_config:
|
||||
type: string
|
||||
emet_count:
|
||||
type: string
|
||||
filemod_count:
|
||||
type: string
|
||||
filtering_known_dlls:
|
||||
type: string
|
||||
group:
|
||||
type: string
|
||||
host_type:
|
||||
type: string
|
||||
hostname:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
interface_ip:
|
||||
type: string
|
||||
last_server_update:
|
||||
type: string
|
||||
last_update:
|
||||
type: string
|
||||
modload_count:
|
||||
type: string
|
||||
netconn_count:
|
||||
type: string
|
||||
os_type:
|
||||
type: string
|
||||
parent_id:
|
||||
type: string
|
||||
parent_name:
|
||||
type: string
|
||||
parent_pid:
|
||||
type: string
|
||||
parent_unique_id:
|
||||
type: string
|
||||
path:
|
||||
type: string
|
||||
process_md5:
|
||||
type: string
|
||||
process_name:
|
||||
type: string
|
||||
process_pid:
|
||||
type: string
|
||||
processblock_count:
|
||||
type: string
|
||||
regmod_count:
|
||||
type: string
|
||||
segment_id:
|
||||
type: string
|
||||
sensor_id:
|
||||
type: string
|
||||
start:
|
||||
type: string
|
||||
terminated:
|
||||
type: string
|
||||
unique_id:
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
type: object
|
||||
tmp3:
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
type: object
|
||||
securitySchemes: {}
|
||||
info:
|
||||
contact:
|
||||
email: frikky@shuffler.io
|
||||
name: '@frikkylikeme'
|
||||
url: https://twitter.com/frikkylikeme
|
||||
description: Automated generation of Carbon Black Response
|
||||
title: Carbon Black Response
|
||||
version: 1.0.0
|
||||
openapi: 3.0.2
|
||||
paths:
|
||||
tmp0:
|
||||
post:
|
||||
description: Carbon Black Response Binary Search
|
||||
parameters:
|
||||
- description: Query
|
||||
in: query
|
||||
name: Query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/tmp0'
|
||||
description: Successful request
|
||||
summary: Carbon Black Response Binary Search
|
||||
tmp1:
|
||||
post:
|
||||
description: Carbon Black Response Isolate Sensor
|
||||
parameters:
|
||||
- description: Hostname of a sensor to isolate.
|
||||
in: query
|
||||
name: Hostname
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/tmp1'
|
||||
description: Successful request
|
||||
summary: Carbon Black Response Isolate Sensor
|
||||
tmp2:
|
||||
post:
|
||||
description: Carbon Black Response Process Search
|
||||
parameters:
|
||||
- description: Query
|
||||
in: query
|
||||
name: Query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/tmp2'
|
||||
description: Successful request
|
||||
summary: Carbon Black Response Process Search
|
||||
tmp3:
|
||||
post:
|
||||
description: Carbon Black Response Unisolate Sensor
|
||||
parameters:
|
||||
- description: Hostname of a sensor to unisolate.
|
||||
in: query
|
||||
name: Hostname
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/tmp3'
|
||||
description: Successful request
|
||||
summary: Carbon Black Response Unisolate Sensor
|
||||
@@ -0,0 +1,559 @@
|
||||
components:
|
||||
schemas:
|
||||
tmp0:
|
||||
properties:
|
||||
outcome:
|
||||
type: string
|
||||
type: object
|
||||
tmp1:
|
||||
properties:
|
||||
allRansomwareProcessesSuspended:
|
||||
type: string
|
||||
closeTime:
|
||||
type: string
|
||||
closerName:
|
||||
type: string
|
||||
creationTime:
|
||||
type: string
|
||||
customClassification:
|
||||
type: string
|
||||
decisionFeature:
|
||||
type: string
|
||||
detectionType:
|
||||
type: string
|
||||
elementDisplayName:
|
||||
type: string
|
||||
hasRansomwareSuspendedProcesses:
|
||||
type: string
|
||||
isBlocked:
|
||||
type: string
|
||||
malopActivityTypes:
|
||||
type: string
|
||||
malopLastUpdateTime:
|
||||
type: string
|
||||
malopStartTime:
|
||||
type: string
|
||||
managementStatus:
|
||||
type: string
|
||||
rootCauseElementNames:
|
||||
type: string
|
||||
rootCauseElementTypes:
|
||||
type: string
|
||||
type: object
|
||||
tmp2:
|
||||
properties:
|
||||
blocking:
|
||||
type: string
|
||||
direction:
|
||||
type: string
|
||||
domain:
|
||||
type: string
|
||||
ipAddress:
|
||||
type: string
|
||||
ipAddressString:
|
||||
type: string
|
||||
lastUpdated:
|
||||
type: string
|
||||
port:
|
||||
type: string
|
||||
ruleId:
|
||||
type: string
|
||||
type: object
|
||||
tmp3:
|
||||
properties:
|
||||
blacklistClassification:
|
||||
type: string
|
||||
classificationLink:
|
||||
type: string
|
||||
containsTorDomain:
|
||||
type: string
|
||||
domainClassificationSuspicion:
|
||||
type: string
|
||||
domainNameUniqueId:
|
||||
type: string
|
||||
elementDisplayName:
|
||||
type: string
|
||||
everResolvedDomain:
|
||||
type: string
|
||||
everResolvedSecondLevelDomain:
|
||||
type: string
|
||||
extendedDisplayId:
|
||||
type: string
|
||||
federationSegments:
|
||||
type: string
|
||||
getIpDiscoveryDomainList:
|
||||
type: string
|
||||
hasMalops:
|
||||
type: string
|
||||
hasResolvedClassificationEvidence:
|
||||
type: string
|
||||
hasSuspicions:
|
||||
type: string
|
||||
indifferentClassification:
|
||||
type: string
|
||||
isDomainMalicious:
|
||||
type: string
|
||||
isInIpDiscoveryDomainList:
|
||||
type: string
|
||||
isInternalDomain:
|
||||
type: string
|
||||
isInternalDomainByName:
|
||||
type: string
|
||||
isReverseLookup:
|
||||
type: string
|
||||
isTorrentDomain:
|
||||
type: string
|
||||
lookupDomainClassification:
|
||||
type: string
|
||||
maliciousClassification:
|
||||
type: string
|
||||
maliciousClassificationString:
|
||||
type: string
|
||||
maliciousClassificationType:
|
||||
type: string
|
||||
malwareClassification:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
relatedToMalop:
|
||||
type: string
|
||||
rootCauseKey:
|
||||
type: string
|
||||
secondLevelDomain:
|
||||
type: string
|
||||
sinkholedClassification:
|
||||
type: string
|
||||
sinkholedClassificationEvidence:
|
||||
type: string
|
||||
topLevelDomain:
|
||||
type: string
|
||||
unknownClassification:
|
||||
type: string
|
||||
unresolvedClassification:
|
||||
type: string
|
||||
unwantedClassification:
|
||||
type: string
|
||||
whitelistClassification:
|
||||
type: string
|
||||
type: object
|
||||
tmp4:
|
||||
properties:
|
||||
attemptExecutionProcessSuspicion:
|
||||
type: string
|
||||
attributes:
|
||||
type: string
|
||||
blackListClassification:
|
||||
type: string
|
||||
canonizedPath:
|
||||
type: string
|
||||
classificationLink:
|
||||
type: string
|
||||
correctedPath:
|
||||
type: string
|
||||
createdTime:
|
||||
type: string
|
||||
detectionEventData:
|
||||
type: string
|
||||
dualExtensionName:
|
||||
type: string
|
||||
elementDisplayName:
|
||||
type: string
|
||||
extendedDisplayId:
|
||||
type: string
|
||||
extension:
|
||||
type: string
|
||||
extensionRecord:
|
||||
type: string
|
||||
extensionType:
|
||||
type: string
|
||||
externalProductClassification:
|
||||
type: string
|
||||
externalProductClassificationIsSigned:
|
||||
type: string
|
||||
externalProductClassificationSignatureVerificationStatus:
|
||||
type: string
|
||||
externalProductClassificationSignatureVerified:
|
||||
type: string
|
||||
externalProductClassificationSignatureVerifiedByVerificationStatus:
|
||||
type: string
|
||||
externalProductClassificationType:
|
||||
type: string
|
||||
federationSegments:
|
||||
type: string
|
||||
fileHasSystem32SubDirectories:
|
||||
type: string
|
||||
fileHasSystemSubDirectories:
|
||||
type: string
|
||||
fileHashUniqueId:
|
||||
type: string
|
||||
fileInSystemPath:
|
||||
type: string
|
||||
fileReputationSuspicion:
|
||||
type: string
|
||||
fileReputationSuspicionDecision:
|
||||
type: string
|
||||
fileVersionSuspicionDecision:
|
||||
type: string
|
||||
getNetworkScannersNames:
|
||||
type: string
|
||||
getToolsForUnusualNetworkEvidence:
|
||||
type: string
|
||||
hackingToolClassification:
|
||||
type: string
|
||||
hasAutorun:
|
||||
type: string
|
||||
hasClassification:
|
||||
type: string
|
||||
hasInternalName:
|
||||
type: string
|
||||
hasLegitClassification:
|
||||
type: string
|
||||
hasMalops:
|
||||
type: string
|
||||
hasNonLegitClassification:
|
||||
type: string
|
||||
hasNonLegitClassificationEvidence:
|
||||
type: string
|
||||
hasProductClassification:
|
||||
type: string
|
||||
hasRansomwareClassificationSubType:
|
||||
type: string
|
||||
hasSuspicions:
|
||||
type: string
|
||||
identifiedProduct:
|
||||
type: string
|
||||
indifferentClassification:
|
||||
type: string
|
||||
isDocument:
|
||||
type: string
|
||||
isExecutable:
|
||||
type: string
|
||||
isFromRemovableDevice:
|
||||
type: string
|
||||
isFromTemp:
|
||||
type: string
|
||||
isNoTypeFoundClassification:
|
||||
type: string
|
||||
isPEFile:
|
||||
type: string
|
||||
isProcessImageFile:
|
||||
type: string
|
||||
isScreenSaver:
|
||||
type: string
|
||||
isSigned:
|
||||
type: string
|
||||
isSuspicious:
|
||||
type: string
|
||||
lastDetectionEventData:
|
||||
type: string
|
||||
maliciousClassification:
|
||||
type: string
|
||||
maliciousClassificationString:
|
||||
type: string
|
||||
maliciousClassificationType:
|
||||
type: string
|
||||
maliciousToolClassification:
|
||||
type: string
|
||||
malwareClassification:
|
||||
type: string
|
||||
malwareClassificationEvidence:
|
||||
type: string
|
||||
malwareType:
|
||||
type: string
|
||||
md5:
|
||||
type: string
|
||||
md5String:
|
||||
type: string
|
||||
missingInterperterSectionValue:
|
||||
type: string
|
||||
modifiedTime:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
nameWithoutExtension:
|
||||
type: string
|
||||
path:
|
||||
type: string
|
||||
peSignedAndVerified:
|
||||
type: string
|
||||
productClassificationType:
|
||||
type: string
|
||||
productType:
|
||||
type: string
|
||||
profileId:
|
||||
type: string
|
||||
ransomwareClassification:
|
||||
type: string
|
||||
reasonSignatureVerificationStatus:
|
||||
type: string
|
||||
relatedToMalop:
|
||||
type: string
|
||||
reportedByAntiMalwareEvidence:
|
||||
type: string
|
||||
reportedByAntiMalwareSuspicion:
|
||||
type: string
|
||||
rootCauseKey:
|
||||
type: string
|
||||
sha1:
|
||||
type: string
|
||||
sha1String:
|
||||
type: string
|
||||
signatureVerified:
|
||||
type: string
|
||||
signatureVerifiedByVerificationStatus:
|
||||
type: string
|
||||
signatureVerifiedInternalOrExternal:
|
||||
type: string
|
||||
signedByApple:
|
||||
type: string
|
||||
signedByLinux:
|
||||
type: string
|
||||
signedByMicrosoft:
|
||||
type: string
|
||||
signedByOperatingSystem:
|
||||
type: string
|
||||
signedInternalOrExternal:
|
||||
type: string
|
||||
size:
|
||||
type: string
|
||||
suspiciousClassification:
|
||||
type: string
|
||||
suspiciousScreenSaverCondition:
|
||||
type: string
|
||||
unknownClassification:
|
||||
type: string
|
||||
unsignedPeFileEvidence:
|
||||
type: string
|
||||
unwantedClassification:
|
||||
type: string
|
||||
whitelistClassification:
|
||||
type: string
|
||||
type: object
|
||||
tmp5:
|
||||
properties:
|
||||
address:
|
||||
type: string
|
||||
addressInternalExternalLocal:
|
||||
type: string
|
||||
addressString:
|
||||
type: string
|
||||
blackListClassification:
|
||||
type: string
|
||||
countryCode:
|
||||
type: string
|
||||
countryName:
|
||||
type: string
|
||||
countryNameOrNotExternalType:
|
||||
type: string
|
||||
elementDisplayName:
|
||||
type: string
|
||||
extendedDisplayId:
|
||||
type: string
|
||||
federationSegments:
|
||||
type: string
|
||||
geolocationLookup:
|
||||
type: string
|
||||
hasMalops:
|
||||
type: string
|
||||
hasSuspicions:
|
||||
type: string
|
||||
isDynamicConfiguration:
|
||||
type: string
|
||||
isExternalAddress:
|
||||
type: string
|
||||
isInternalAddress:
|
||||
type: string
|
||||
isLocalAddress:
|
||||
type: string
|
||||
latitude:
|
||||
type: string
|
||||
longitude:
|
||||
type: string
|
||||
lookupIpClassification:
|
||||
type: string
|
||||
maliciousClassification:
|
||||
type: string
|
||||
maliciousClassificationType:
|
||||
type: string
|
||||
rootCauseKey:
|
||||
type: string
|
||||
uniqueIpAddressHash:
|
||||
type: string
|
||||
version:
|
||||
type: string
|
||||
whiteListClassification:
|
||||
type: string
|
||||
type: object
|
||||
securitySchemes: {}
|
||||
info:
|
||||
contact:
|
||||
email: frikky@shuffler.io
|
||||
name: '@frikkylikeme'
|
||||
url: https://twitter.com/frikkylikeme
|
||||
description: Automated generation of Cyberreason
|
||||
title: Cyberreason
|
||||
version: 1.0.0
|
||||
openapi: 3.0.2
|
||||
paths:
|
||||
tmp0:
|
||||
post:
|
||||
description: CyberReason Block or Unblock an Item
|
||||
parameters:
|
||||
- description: Either 'blacklist' or 'whitelist'
|
||||
in: query
|
||||
name: Action
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: IP, Domain, or Hash CSV values to blacklist or whitelist
|
||||
in: query
|
||||
name: Values
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/tmp0'
|
||||
description: Successful request
|
||||
summary: CyberReason Block Item
|
||||
tmp1:
|
||||
post:
|
||||
description: CyberReason get alerts from MalOps
|
||||
parameters: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/tmp1'
|
||||
description: Successful request
|
||||
summary: CyberReason Get MalOps Alerts
|
||||
tmp2:
|
||||
post:
|
||||
description: CyberReason isolate a host by port
|
||||
parameters:
|
||||
- description: IP to block
|
||||
in: query
|
||||
name: IP
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Direction to block traffic, one of 'ALL', 'INCOMING', 'OUTGOING'
|
||||
in: query
|
||||
name: Direction to block
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Port to block
|
||||
in: query
|
||||
name: Port
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: If true will isolate, false will remove from isolation
|
||||
in: query
|
||||
name: Block
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/tmp2'
|
||||
description: Successful request
|
||||
summary: CyberReason Isolate Host
|
||||
tmp3:
|
||||
post:
|
||||
description: Check CyberReason for a domain
|
||||
parameters:
|
||||
- description: Limit of results to return
|
||||
in: body
|
||||
name: Limit
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- description: Timeout
|
||||
in: body
|
||||
name: Timeout (ms)
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- description: Domain to search for
|
||||
in: query
|
||||
name: Domain
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/tmp3'
|
||||
description: Successful request
|
||||
summary: CyberReason Query for Domain
|
||||
tmp4:
|
||||
post:
|
||||
description: Check CyberReason for a hash
|
||||
parameters:
|
||||
- description: Limit of results to return
|
||||
in: body
|
||||
name: Limit
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- description: Timeout
|
||||
in: body
|
||||
name: Timeout (ms)
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- description: Hash to search for
|
||||
in: query
|
||||
name: Hash
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/tmp4'
|
||||
description: Successful request
|
||||
summary: CyberReason Query for Hash
|
||||
tmp5:
|
||||
post:
|
||||
description: Check CyberReason for a IP
|
||||
parameters:
|
||||
- description: Limit of results to return
|
||||
in: body
|
||||
name: Limit
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- description: Timeout
|
||||
in: body
|
||||
name: Timeout (ms)
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- description: IP to search for
|
||||
in: query
|
||||
name: IP
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/tmp5'
|
||||
description: Successful request
|
||||
summary: CyberReason Query for IP
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,166 @@
|
||||
components:
|
||||
schemas:
|
||||
tmp0:
|
||||
properties:
|
||||
bid:
|
||||
type: string
|
||||
cve:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
msb:
|
||||
type: string
|
||||
osvdb:
|
||||
type: string
|
||||
source:
|
||||
type: string
|
||||
type: object
|
||||
tmp1:
|
||||
properties:
|
||||
data:
|
||||
type: string
|
||||
domains:
|
||||
type: string
|
||||
hostnames:
|
||||
type: string
|
||||
ip:
|
||||
type: string
|
||||
location.area_code:
|
||||
type: string
|
||||
location.city:
|
||||
type: string
|
||||
location.country_code:
|
||||
type: string
|
||||
location.country_name:
|
||||
type: string
|
||||
location.dma_code:
|
||||
type: string
|
||||
location.latitude:
|
||||
type: string
|
||||
location.longitude:
|
||||
type: string
|
||||
location.postal_code:
|
||||
type: string
|
||||
location.region_code:
|
||||
type: string
|
||||
org:
|
||||
type: string
|
||||
os:
|
||||
type: string
|
||||
port:
|
||||
type: string
|
||||
transport:
|
||||
type: string
|
||||
type: object
|
||||
tmp2:
|
||||
properties:
|
||||
devicetype:
|
||||
type: string
|
||||
domains:
|
||||
type: string
|
||||
hostnames:
|
||||
type: string
|
||||
ip_str:
|
||||
type: string
|
||||
isp:
|
||||
type: string
|
||||
location.area_code:
|
||||
type: string
|
||||
location.city:
|
||||
type: string
|
||||
location.country_code:
|
||||
type: string
|
||||
location.country_name:
|
||||
type: string
|
||||
location.dma_code:
|
||||
type: string
|
||||
location.latitude:
|
||||
type: string
|
||||
location.longitude:
|
||||
type: string
|
||||
location.postal_code:
|
||||
type: string
|
||||
org:
|
||||
type: string
|
||||
os:
|
||||
type: string
|
||||
port:
|
||||
type: string
|
||||
product:
|
||||
type: string
|
||||
timestamp:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
type: object
|
||||
securitySchemes: {}
|
||||
info:
|
||||
contact:
|
||||
email: frikky@shuffler.io
|
||||
name: '@frikkylikeme'
|
||||
url: https://twitter.com/frikkylikeme
|
||||
description: Automated generation of Shodan
|
||||
title: Shodan
|
||||
version: 1.0.0
|
||||
openapi: 3.0.2
|
||||
paths:
|
||||
tmp0:
|
||||
post:
|
||||
description: Search across a variety of data sources for exploits
|
||||
parameters:
|
||||
- description: Search query used to search the database of known exploits
|
||||
in: query
|
||||
name: Query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/tmp0'
|
||||
description: Successful request
|
||||
summary: Shodan Exploit Search
|
||||
tmp1:
|
||||
post:
|
||||
description: Search all services that have been found on the given host IP
|
||||
parameters:
|
||||
- description: Host IP address
|
||||
in: query
|
||||
name: IP
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/tmp1'
|
||||
description: Successful request
|
||||
summary: Shodan Host
|
||||
tmp2:
|
||||
post:
|
||||
description: Search the SHODAN database
|
||||
parameters:
|
||||
- description: Keyword to search Shodan for
|
||||
in: query
|
||||
name: Query
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: 'Max number of results to return. Default: ''Infinite'''
|
||||
in: body
|
||||
name: Limit
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/tmp2'
|
||||
description: Successful request
|
||||
summary: Shodan Search
|
||||
@@ -0,0 +1,149 @@
|
||||
components:
|
||||
schemas:
|
||||
tmp0:
|
||||
properties:
|
||||
bios_uuid:
|
||||
type: string
|
||||
fqdn:
|
||||
type: string
|
||||
hostname:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
ipv4:
|
||||
type: string
|
||||
ipv6:
|
||||
type: string
|
||||
mac_address:
|
||||
type: string
|
||||
netbios_name:
|
||||
type: string
|
||||
operating_system:
|
||||
type: string
|
||||
ssh_fingerprint:
|
||||
type: string
|
||||
system_type:
|
||||
type: string
|
||||
type: object
|
||||
tmp1:
|
||||
properties:
|
||||
agent_name:
|
||||
type: string
|
||||
fqdn:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
ipv4:
|
||||
type: string
|
||||
ipv6:
|
||||
type: string
|
||||
last_seen:
|
||||
type: string
|
||||
mac_address:
|
||||
type: string
|
||||
operating_system:
|
||||
type: string
|
||||
type: object
|
||||
tmp2:
|
||||
properties:
|
||||
hostcount:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
owner:
|
||||
type: string
|
||||
policy:
|
||||
type: string
|
||||
scan_end:
|
||||
type: string
|
||||
scan_start:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
targets:
|
||||
type: string
|
||||
uuid:
|
||||
type: string
|
||||
type: object
|
||||
tmp4:
|
||||
properties:
|
||||
count:
|
||||
type: string
|
||||
plugin_family:
|
||||
type: string
|
||||
plugin_name:
|
||||
type: string
|
||||
severity:
|
||||
type: string
|
||||
vulnerability_state:
|
||||
type: string
|
||||
type: object
|
||||
securitySchemes: {}
|
||||
info:
|
||||
contact:
|
||||
email: frikky@shuffler.io
|
||||
name: '@frikkylikeme'
|
||||
url: https://twitter.com/frikkylikeme
|
||||
description: Automated generation of Tenable Tenable.io
|
||||
title: Tenable Tenable.io
|
||||
version: 1.0.0
|
||||
openapi: 3.0.2
|
||||
paths:
|
||||
tmp0:
|
||||
post:
|
||||
description: Returns information about the specified asset.
|
||||
parameters:
|
||||
- description: The UUID of the asset.
|
||||
in: query
|
||||
name: Asset UUID
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful request
|
||||
summary: Tenable.io Asset Info
|
||||
tmp1:
|
||||
post:
|
||||
description: Returns a list of up to 5000 assets.
|
||||
parameters: []
|
||||
responses:
|
||||
'200':
|
||||
description: Successful request
|
||||
summary: Tenable.io List Assets
|
||||
tmp2:
|
||||
post:
|
||||
description: Returns details for the given scan.
|
||||
parameters:
|
||||
- description: The ID of the scan.
|
||||
in: query
|
||||
name: Scan ID
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful request
|
||||
summary: Tenable.io Scan Details
|
||||
tmp3:
|
||||
post:
|
||||
description: Launches a scan.
|
||||
parameters:
|
||||
- description: The ID of the scan.
|
||||
in: body
|
||||
name: Scan ID
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful request
|
||||
summary: Tenable.io Scan Launch
|
||||
tmp4:
|
||||
post:
|
||||
description: Retrieves a list of recorded vulnerabilities.
|
||||
parameters: []
|
||||
responses:
|
||||
'200':
|
||||
description: Successful request
|
||||
summary: Tenable.io Vulnerabilities
|
||||
@@ -0,0 +1,109 @@
|
||||
import json
|
||||
import yaml
|
||||
|
||||
items = []
|
||||
|
||||
openapi = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {
|
||||
"title": "MISP",
|
||||
"description": "MISP API generated from the misp book: https://github.com/MISP/misp-book/blob/master/automation/README.md",
|
||||
"version": "1.0.0",
|
||||
"contact": {
|
||||
"name": "@frikkylikeme",
|
||||
"url": "https://twitter.com/frikkylikeme",
|
||||
"email": "frikky@shuffler.io"
|
||||
}
|
||||
},
|
||||
"paths": {},
|
||||
"components": {
|
||||
"schemas": {},
|
||||
"securitySchemes": {
|
||||
"ApiKeyAuth": {
|
||||
"type": "apikey",
|
||||
"in": "header",
|
||||
"name": "Authorization",
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
with open("misp.txt", "r") as tmp:
|
||||
newitem = {}
|
||||
recorditem = False
|
||||
|
||||
counter = 0
|
||||
itemsplit = tmp.read().split("\n")
|
||||
for item in itemsplit:
|
||||
counter += 1
|
||||
if item.startswith("### ") and "/" in item:
|
||||
try:
|
||||
path = item.split(" ")[2]
|
||||
method = item.split(" ")[1].lower()
|
||||
newitem = {
|
||||
"path": path,
|
||||
"method": method,
|
||||
}
|
||||
|
||||
try:
|
||||
openapi["paths"][path][method] = {}
|
||||
except KeyError:
|
||||
openapi["paths"][path] = {}
|
||||
openapi["paths"][path][method] = {}
|
||||
|
||||
except IndexError:
|
||||
newitem = {}
|
||||
continue
|
||||
|
||||
recorditem = True
|
||||
#print(newitem)
|
||||
|
||||
if not recorditem:
|
||||
continue
|
||||
|
||||
if "Description" in item:
|
||||
openapi["paths"][newitem["path"]][newitem["method"]]["description"] = itemsplit[counter+1]
|
||||
elif "URL Arguments" in item:
|
||||
parameters = []
|
||||
innercnt = 0
|
||||
|
||||
openapi["paths"][newitem["path"]][newitem["method"]]["parameters"] = []
|
||||
while True:
|
||||
curline = itemsplit[counter+1+innercnt]
|
||||
if "#" in curline:
|
||||
break
|
||||
|
||||
innercnt += 1
|
||||
if not curline:
|
||||
continue
|
||||
|
||||
print(curline)
|
||||
parameters.append({
|
||||
"description": curline.split(" ")[1],
|
||||
"in": "query",
|
||||
"name": curline.split(" ")[1],
|
||||
"required": True,
|
||||
"schema": {"type": "string"},
|
||||
})
|
||||
|
||||
openapi["paths"][newitem["path"]][newitem["method"]]["parameters"] = parameters
|
||||
elif "Output" in item:
|
||||
# FIXME
|
||||
innercnt = 0
|
||||
while True:
|
||||
curline = itemsplit[counter+1+innercnt]
|
||||
|
||||
if "#" in curline:
|
||||
break
|
||||
|
||||
innercnt += 1
|
||||
if "json" in curline:
|
||||
continue
|
||||
#print(curline)
|
||||
|
||||
print(json.dumps(openapi, indent=4))
|
||||
|
||||
|
||||
generatedfile = "generated/misp.yaml"
|
||||
with open(generatedfile, "w+") as tmp:
|
||||
tmp.write(yaml.dump(openapi))
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,131 @@
|
||||
import requests
|
||||
import yaml
|
||||
|
||||
def parse_data(data):
|
||||
openapi = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"version": "1.0.0",
|
||||
"contact": {
|
||||
"name": "@frikkylikeme",
|
||||
"url": "https://twitter.com/frikkylikeme",
|
||||
"email": "frikky@shuffler.io"
|
||||
}
|
||||
},
|
||||
"paths": {},
|
||||
"components": {
|
||||
"schemas": {},
|
||||
"securitySchemes": {},
|
||||
}
|
||||
}
|
||||
|
||||
category = data["category"]
|
||||
|
||||
filename = "%s.yaml" % data["title"].replace(" ", "_").lower()
|
||||
openapi["info"]["title"] = data["title"]
|
||||
openapi["info"]["description"] = "Automated generation of %s" % data["title"]
|
||||
# data["description"]
|
||||
|
||||
cnt = 0
|
||||
for task in data["tasks"]:
|
||||
method = "post"
|
||||
|
||||
openapi["paths"]["tmp%d" % cnt] = {}
|
||||
openapi["paths"]["tmp%d" % cnt][method] = {
|
||||
"summary": task["name"],
|
||||
"description": task["description"],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful request",
|
||||
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
#taskname = task["name"]
|
||||
#taskdescription = task["description"]
|
||||
taskcategory = task["family"]
|
||||
|
||||
# This doesn't really do much except build the return value structures
|
||||
for parameter in task["input_parameters"]:
|
||||
example = parameter["example"]
|
||||
|
||||
inVar = "query"
|
||||
|
||||
if parameter["type"] == 6:
|
||||
inVar = "body"
|
||||
|
||||
schema = "string"
|
||||
schemaset = False
|
||||
if parameter["type"] != 1:
|
||||
if (parameter["type"] == 7):
|
||||
schema = "boolean"
|
||||
schemaset = True
|
||||
|
||||
if schema == "string" and schemaset:
|
||||
print("Should change type: %d" % parameter["type"])
|
||||
print(task["name"])
|
||||
print(parameter["name"])
|
||||
print()
|
||||
|
||||
|
||||
if len(example) == 1:
|
||||
print("Change to number?")
|
||||
if example.startswith("{"):
|
||||
print("Change to json object?")
|
||||
if example.startswith("["):
|
||||
print("Change to array object?")
|
||||
|
||||
# Not sure how to tackle this
|
||||
openapi["paths"]["tmp%d" % cnt][method]["parameters"].append({
|
||||
"in": inVar,
|
||||
"name": parameter["name"],
|
||||
"required": parameter["required"],
|
||||
"description": parameter["description"],
|
||||
"schema": {"type": schema}
|
||||
})
|
||||
|
||||
if len(task["available_output_variables"]) > 0:
|
||||
openapi["paths"]["tmp%d" % cnt][method]["responses"]["200"]["content"]: {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/tmp%d" % cnt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
openapi["components"]["schemas"]["tmp%d" % cnt] = {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
}
|
||||
|
||||
for output in task["available_output_variables"]:
|
||||
openapi["components"]["schemas"]["tmp%d" % cnt]["properties"][output["key"]] = {"type": "string"}
|
||||
|
||||
|
||||
cnt += 1
|
||||
|
||||
#print(openapi)
|
||||
#print(filename)
|
||||
|
||||
return filename, openapi
|
||||
|
||||
def dump_data(filename, openapi):
|
||||
generatedfile = "generated/%s" % filename
|
||||
with open(generatedfile, "w+") as tmp:
|
||||
tmp.write(yaml.dump(openapi))
|
||||
|
||||
print("Generated %s" % generatedfile)
|
||||
|
||||
if __name__ == "__main__":
|
||||
url = "https://apphub.swimlane.com/api/v1/bundles/cjuspytpz00rh0hpjo5chqg10"
|
||||
url = "https://apphub.swimlane.com/api/v1/bundles/cjyoy62ch04920lr26id5sr0e"
|
||||
url = "https://apphub.swimlane.com/api/v1/bundles/cjqrdc2yr02rs0fli6jrosiqb"
|
||||
url = "https://apphub.swimlane.com/api/v1/bundles/cjqrdat0u01ux0flipb68a0a0"
|
||||
url = "https://apphub.swimlane.com/api/v1/bundles/cjqrdhbwp07nf0fli23lyb52h"
|
||||
data = requests.get(url).json()
|
||||
filename, openapi = parse_data(data)
|
||||
dump_data(filename, openapi)
|
||||
@@ -0,0 +1,7 @@
|
||||
# OpenAPI generator
|
||||
This contains test code that's been moved to shaffuru/backend/go-app/codegen.go
|
||||
|
||||
## Todo:
|
||||
1. Don't use filesystem, but rather store in GCP
|
||||
2. Add swagger 2.0 to 3.0 converter
|
||||
3. Fix body / data parsing
|
||||
@@ -0,0 +1,26 @@
|
||||
# Base our app image off of the WALKOFF App SDK image
|
||||
FROM frikky/shuffle:app_sdk as base
|
||||
|
||||
# We're going to stage away all of the bloat from the build tools so lets create a builder stage
|
||||
FROM base as builder
|
||||
|
||||
# Install all alpine build tools needed for our pip installs
|
||||
RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev
|
||||
|
||||
# Install all of our pip packages in a single directory that we can copy to our base image later
|
||||
RUN mkdir /install
|
||||
WORKDIR /install
|
||||
COPY requirements.txt /requirements.txt
|
||||
RUN pip install --prefix="/install" -r /requirements.txt
|
||||
|
||||
# Switch back to our base image and copy in all of our built packages and source code
|
||||
FROM base
|
||||
COPY --from=builder /install /usr/local
|
||||
COPY src /app
|
||||
|
||||
# Install any binary dependencies needed in our final image - this can be a lot of different stuff
|
||||
RUN apk --no-cache add --update libmagic
|
||||
|
||||
# Finally, lets run our app!
|
||||
WORKDIR /app
|
||||
CMD python app.py --log-level DEBUG
|
||||
@@ -0,0 +1,3 @@
|
||||
# No extra requirements needed
|
||||
requests
|
||||
urllib3
|
||||
@@ -0,0 +1,387 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"gopkg.in/yaml.v2"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type WorkflowApp struct {
|
||||
Name string `json:"name" yaml:"name" required:true datastore:"name"`
|
||||
IsValid bool `json:"is_valid" yaml:"is_valid" required:true datastore:"is_valid"`
|
||||
ID string `json:"id" yaml:"id,omitempty" required:false datastore:"id"`
|
||||
Link string `json:"link" yaml:"link" required:false datastore:"link,noindex"`
|
||||
AppVersion string `json:"app_version" yaml:"app_version" required:true datastore:"app_version"`
|
||||
Description string `json:"description" datastore:"description" required:false yaml:"description"`
|
||||
Environment string `json:"environment" datastore:"environment" required:true yaml:"environment"`
|
||||
SmallImage string `json:"small_image" datastore:"small_image,noindex" required:false yaml:"small_image"`
|
||||
LargeImage string `json:"large_image" datastore:"large_image,noindex" yaml:"large_image" required:false`
|
||||
ContactInfo struct {
|
||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||
Url string `json:"url" datastore:"url" yaml:"url"`
|
||||
} `json:"contact_info" datastore:"contact_info" yaml:"contact_info" required:false`
|
||||
Actions []WorkflowAppAction `json:"actions" yaml:"actions" required:true datastore:"actions"`
|
||||
Authentication Authentication `json:"authentication" yaml:"authentication" required:false datastore:"authentication"`
|
||||
}
|
||||
|
||||
type AuthenticationParams struct {
|
||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
||||
ID string `json:"id" datastore:"id" yaml:"id"`
|
||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||
Example string `json:"example" datastore:"example" yaml:"example"s`
|
||||
Value string `json:"value,omitempty" datastore:"value" yaml:"value"`
|
||||
Multiline bool `json:"multiline" datastore:"multiline" yaml:"multiline"`
|
||||
Required bool `json:"required" datastore:"required" yaml:"required"`
|
||||
}
|
||||
|
||||
type Authentication struct {
|
||||
Required bool `json:"required" datastore:"required" yaml:"required" `
|
||||
Parameters []AuthenticationParams `json:"parameters" datastore:"parameters" yaml:"parameters"`
|
||||
}
|
||||
|
||||
type AuthenticationStore struct {
|
||||
Key string `json:"key" datastore:"key"`
|
||||
Value string `json:"value" datastore:"value"`
|
||||
}
|
||||
|
||||
type WorkflowAppActionParameter struct {
|
||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
||||
ID string `json:"id" datastore:"id" yaml:"id,omitempty"`
|
||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||
Example string `json:"example" datastore:"example" yaml:"example"`
|
||||
Value string `json:"value" datastore:"value" yaml:"value,omitempty"`
|
||||
Multiline bool `json:"multiline" datastore:"multiline" yaml:"multiline"`
|
||||
ActionField string `json:"action_field" datastore:"action_field" yaml:"actionfield,omitempty"`
|
||||
Variant string `json:"variant" datastore:"variant" yaml:"variant,omitempty"`
|
||||
Required bool `json:"required" datastore:"required" yaml:"required"`
|
||||
Schema SchemaDefinition `json:"schema" datastore:"schema" yaml:"schema"`
|
||||
}
|
||||
|
||||
type SchemaDefinition struct {
|
||||
Type string `json:"type" datastore:"type"`
|
||||
}
|
||||
|
||||
type WorkflowAppAction struct {
|
||||
Description string `json:"description" datastore:"description"`
|
||||
ID string `json:"id" datastore:"id" yaml:"id,omitempty"`
|
||||
Name string `json:"name" datastore:"name"`
|
||||
NodeType string `json:"node_type" datastore:"node_type"`
|
||||
Environment string `json:"environment" datastore:"environment"`
|
||||
Authentication []AuthenticationStore `json:"authentication" datastore:"authentication" yaml:"authentication,omitempty"`
|
||||
Parameters []WorkflowAppActionParameter `json:"parameters" datastore: "parameters"`
|
||||
Returns struct {
|
||||
Description string `json:"description" datastore:"returns" yaml:"description,omitempty"`
|
||||
ID string `json:"id" datastore:"id" yaml:"id,omitempty"`
|
||||
Schema SchemaDefinition `json:"schema" datastore:"schema" yaml:"schema"`
|
||||
} `json:"returns" datastore:"returns"`
|
||||
}
|
||||
|
||||
func copyFile(fromfile, tofile string) error {
|
||||
from, err := os.Open(fromfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer from.Close()
|
||||
|
||||
to, err := os.OpenFile(tofile, os.O_RDWR|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer to.Close()
|
||||
|
||||
_, err = io.Copy(to, from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Builds the base structure for the app that we're making
|
||||
// Returns error if anything goes wrong. This has to work if
|
||||
// the python code is supposed to be generated
|
||||
func buildStructure(swagger *openapi3.Swagger, curHash string) (string, error) {
|
||||
//log.Printf("%#v", swagger)
|
||||
|
||||
// adding md5 based on input data to not overwrite earlier data.
|
||||
generatedPath := "generated"
|
||||
identifier := fmt.Sprintf("%s-%s", swagger.Info.Title, curHash)
|
||||
appPath := fmt.Sprintf("%s/%s", generatedPath, identifier)
|
||||
|
||||
os.MkdirAll(appPath, os.ModePerm)
|
||||
os.Mkdir(fmt.Sprintf("%s/src", appPath), os.ModePerm)
|
||||
|
||||
err := copyFile("baseline/Dockerfile", fmt.Sprintf("%s/%s", appPath, "Dockerfile"))
|
||||
if err != nil {
|
||||
log.Println("Failed to move Dockerfile")
|
||||
return appPath, err
|
||||
}
|
||||
|
||||
err = copyFile("baseline/requirements.txt", fmt.Sprintf("%s/%s", appPath, "requirements.txt"))
|
||||
if err != nil {
|
||||
log.Println("Failed to move requrements.txt")
|
||||
return appPath, err
|
||||
}
|
||||
|
||||
return appPath, nil
|
||||
}
|
||||
|
||||
func makePythoncode(name, url, method string, parameters, optionalQueries []string) string {
|
||||
method = strings.ToLower(method)
|
||||
queryString := ""
|
||||
queryData := ""
|
||||
|
||||
// FIXME - this might break - need to check if ? or & should be set as query
|
||||
parameterData := ""
|
||||
if len(optionalQueries) > 0 {
|
||||
queryString += ", "
|
||||
for _, query := range optionalQueries {
|
||||
queryString += fmt.Sprintf("%s=\"\"", query)
|
||||
queryData += fmt.Sprintf(`
|
||||
if %s:
|
||||
url += f"&%s={%s}"`, query, query, query)
|
||||
}
|
||||
}
|
||||
|
||||
if len(parameters) > 0 {
|
||||
parameterData = fmt.Sprintf(", %s", strings.Join(parameters, ", "))
|
||||
}
|
||||
|
||||
// FIXME - add checks for query data etc
|
||||
data := fmt.Sprintf(` async def %s_%s(self%s%s):
|
||||
url=f"%s"
|
||||
%s
|
||||
return requests.%s(url).text
|
||||
`, name, method, parameterData, queryString, url, queryData, method)
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func generateYaml(swagger *openapi3.Swagger) (WorkflowApp, []string, error) {
|
||||
api := WorkflowApp{}
|
||||
log.Printf("%#v", swagger.Info)
|
||||
|
||||
if len(swagger.Info.Title) == 0 {
|
||||
return WorkflowApp{}, []string{}, errors.New("Swagger.Info.Title can't be empty.")
|
||||
}
|
||||
|
||||
if len(swagger.Servers) == 0 {
|
||||
return WorkflowApp{}, []string{}, errors.New("Swagger.Servers can't be empty. Add 'servers':[{'url':'hostname.com'}'")
|
||||
}
|
||||
|
||||
api.Name = swagger.Info.Title
|
||||
api.Description = swagger.Info.Description
|
||||
api.IsValid = true
|
||||
api.Link = swagger.Servers[0].URL // host doesnt exist lol
|
||||
api.AppVersion = "1.0.0"
|
||||
api.Environment = "cloud"
|
||||
api.ID = ""
|
||||
api.SmallImage = ""
|
||||
api.LargeImage = ""
|
||||
|
||||
// This is the python code to be generated
|
||||
// Could just as well be go at this point lol
|
||||
pythonFunctions := []string{}
|
||||
|
||||
for actualPath, path := range swagger.Paths {
|
||||
//log.Printf("%#v", path)
|
||||
//log.Printf("%#v", actualPath)
|
||||
// Find the path name and add it to makeCode() param
|
||||
|
||||
firstQuery := true
|
||||
if path.Get != nil {
|
||||
// What to do with this, hmm
|
||||
functionName := strings.ReplaceAll(path.Get.Summary, " ", "_")
|
||||
functionName = strings.ToLower(functionName)
|
||||
|
||||
action := WorkflowAppAction{
|
||||
Description: path.Get.Description,
|
||||
Name: path.Get.Summary,
|
||||
NodeType: "action",
|
||||
Environment: api.Environment,
|
||||
Parameters: []WorkflowAppActionParameter{},
|
||||
}
|
||||
|
||||
action.Returns.Schema.Type = "string"
|
||||
baseUrl := fmt.Sprintf("%s%s", api.Link, actualPath)
|
||||
|
||||
//log.Println(path.Parameters)
|
||||
|
||||
// Parameters: []WorkflowAppActionParameter{},
|
||||
// FIXME - add data for POST stuff
|
||||
firstQuery = true
|
||||
optionalQueries := []string{}
|
||||
parameters := []string{}
|
||||
optionalParameters := []WorkflowAppActionParameter{}
|
||||
if len(path.Get.Parameters) > 0 {
|
||||
for _, param := range path.Get.Parameters {
|
||||
curParam := WorkflowAppActionParameter{
|
||||
Name: param.Value.Name,
|
||||
Description: param.Value.Description,
|
||||
Multiline: false,
|
||||
Required: param.Value.Required,
|
||||
Schema: SchemaDefinition{
|
||||
Type: param.Value.Schema.Value.Type,
|
||||
},
|
||||
}
|
||||
|
||||
if param.Value.Required {
|
||||
action.Parameters = append(action.Parameters, curParam)
|
||||
} else {
|
||||
optionalParameters = append(optionalParameters, curParam)
|
||||
}
|
||||
|
||||
if param.Value.In == "path" {
|
||||
log.Printf("PATH!: %s", param.Value.Name)
|
||||
parameters = append(parameters, param.Value.Name)
|
||||
//baseUrl = fmt.Sprintf("%s%s", baseUrl)
|
||||
} else if param.Value.In == "query" {
|
||||
log.Printf("QUERY!: %s", param.Value.Name)
|
||||
if !param.Value.Required {
|
||||
optionalQueries = append(optionalQueries, param.Value.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
parameters = append(parameters, param.Value.Name)
|
||||
|
||||
if firstQuery {
|
||||
baseUrl = fmt.Sprintf("%s?%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
} else {
|
||||
baseUrl = fmt.Sprintf("%s&%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ensuring that they end up last in the specification
|
||||
// (order is ish important for optional params) - they need to be last.
|
||||
for _, optionalParam := range optionalParameters {
|
||||
action.Parameters = append(action.Parameters, optionalParam)
|
||||
}
|
||||
|
||||
curCode := makePythoncode(functionName, baseUrl, "get", parameters, optionalQueries)
|
||||
pythonFunctions = append(pythonFunctions, curCode)
|
||||
|
||||
api.Actions = append(api.Actions, action)
|
||||
}
|
||||
}
|
||||
|
||||
return api, pythonFunctions, nil
|
||||
}
|
||||
|
||||
func verifyApi(api WorkflowApp) WorkflowApp {
|
||||
if api.AppVersion == "" {
|
||||
api.AppVersion = "1.0.0"
|
||||
}
|
||||
|
||||
return api
|
||||
}
|
||||
|
||||
func dumpPython(basePath, name, version string, pythonFunctions []string) error {
|
||||
//log.Printf("%#v", api)
|
||||
log.Printf(strings.Join(pythonFunctions, "\n"))
|
||||
|
||||
parsedCode := fmt.Sprintf(`import requests
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from walkoff_app_sdk.app_base import AppBase
|
||||
|
||||
class %s(AppBase):
|
||||
"""
|
||||
Autogenerated class by Shuffler
|
||||
"""
|
||||
|
||||
__version__ = "%s"
|
||||
app_name = "%s"
|
||||
|
||||
def __init__(self, redis, logger, console_logger=None):
|
||||
self.verify = False
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
super().__init__(redis, logger, console_logger)
|
||||
|
||||
%s
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(CarbonBlack.run(), debug=True)
|
||||
`, name, version, name, strings.Join(pythonFunctions, "\n"))
|
||||
|
||||
err := ioutil.WriteFile(fmt.Sprintf("%s/src/app.py", basePath), []byte(parsedCode), os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(parsedCode)
|
||||
|
||||
//log.Println(string(data))
|
||||
return nil
|
||||
}
|
||||
|
||||
func dumpApi(basePath string, api WorkflowApp) error {
|
||||
//log.Printf("%#v", api)
|
||||
data, err := yaml.Marshal(api)
|
||||
if err != nil {
|
||||
log.Printf("Error with yaml marshal: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(fmt.Sprintf("%s/api.yaml", basePath), []byte(data), os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//log.Println(string(data))
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
data := []byte(`{"swagger":"3.0","info":{"title":"hi","description":"you","version":"1.0"},"servers":[{"url":"https://shuffler.io/api/v1"}],"host":"shuffler.io","basePath":"/api/v1","schemes":["https:"],"paths":{"/workflows":{"get":{"responses":{"default":{"description":"default","schema":{}}},"summary":"Get workflows","description":"Get workflows","parameters":[]}},"/workflows/{id}":{"get":{"responses":{"default":{"description":"default","schema":{}}},"summary":"Get workflow","description":"Get workflow","parameters":[{"in":"query","name":"forgetme","description":"Generated by shuffler.io OpenAPI","required":true,"schema":{"type":"string"}},{"in":"query","name":"anotherone","description":"Generated by shuffler.io OpenAPI","required":false,"schema":{"type":"string"}},{"in":"query","name":"hi","description":"Generated by shuffler.io OpenAPI","required":true,"schema":{"type":"string"}},{"in":"path","name":"id","description":"Generated by shuffler.io OpenAPI","required":true,"schema":{"type":"string"}}]}}},"securityDefinitions":{}}`)
|
||||
|
||||
hasher := md5.New()
|
||||
hasher.Write(data)
|
||||
newmd5 := hex.EncodeToString(hasher.Sum(nil))
|
||||
|
||||
swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(data)
|
||||
if err != nil {
|
||||
log.Printf("Swagger validation error: %s", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
if strings.Contains(swagger.Info.Title, " ") {
|
||||
strings.ReplaceAll(swagger.Info.Title, " ", "")
|
||||
}
|
||||
|
||||
basePath, err := buildStructure(swagger, newmd5)
|
||||
if err != nil {
|
||||
log.Printf("Failed to build base structure: %s", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
api, pythonfunctions, err := generateYaml(swagger)
|
||||
if err != nil {
|
||||
log.Printf("Failed building and generating yaml: %s", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
err = dumpApi(basePath, api)
|
||||
if err != nil {
|
||||
log.Printf("Failed dumping yaml: %s", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
err = dumpPython(basePath, swagger.Info.Title, swagger.Info.Version, pythonfunctions)
|
||||
if err != nil {
|
||||
log.Printf("Failed dumping python: %s", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
Code used to generate apps from OpenAPI JSON data
|
||||
Any function ending with GCP doesn't use local fileIO, but rather
|
||||
google cloud storage, and also has a normal filesystem version of the
|
||||
same code (doesn't required client as first argument).
|
||||
|
||||
This code is used in the backend to generate apps on the fly for users.
|
||||
All new code is appended to backend/go-app/codegen.go
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"cloud.google.com/go/storage"
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var bucketName = "shuffler.appspot.com"
|
||||
|
||||
type WorkflowApp struct {
|
||||
Name string `json:"name" yaml:"name" required:true datastore:"name"`
|
||||
IsValid bool `json:"is_valid" yaml:"is_valid" required:true datastore:"is_valid"`
|
||||
ID string `json:"id" yaml:"id,omitempty" required:false datastore:"id"`
|
||||
Link string `json:"link" yaml:"link" required:false datastore:"link,noindex"`
|
||||
AppVersion string `json:"app_version" yaml:"app_version" required:true datastore:"app_version"`
|
||||
Description string `json:"description" datastore:"description" required:false yaml:"description"`
|
||||
Environment string `json:"environment" datastore:"environment" required:true yaml:"environment"`
|
||||
SmallImage string `json:"small_image" datastore:"small_image,noindex" required:false yaml:"small_image"`
|
||||
LargeImage string `json:"large_image" datastore:"large_image,noindex" yaml:"large_image" required:false`
|
||||
ContactInfo struct {
|
||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||
Url string `json:"url" datastore:"url" yaml:"url"`
|
||||
} `json:"contact_info" datastore:"contact_info" yaml:"contact_info" required:false`
|
||||
Actions []WorkflowAppAction `json:"actions" yaml:"actions" required:true datastore:"actions"`
|
||||
Authentication Authentication `json:"authentication" yaml:"authentication" required:false datastore:"authentication"`
|
||||
}
|
||||
|
||||
type AuthenticationParams struct {
|
||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
||||
ID string `json:"id" datastore:"id" yaml:"id"`
|
||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||
Example string `json:"example" datastore:"example" yaml:"example"s`
|
||||
Value string `json:"value,omitempty" datastore:"value" yaml:"value"`
|
||||
Multiline bool `json:"multiline" datastore:"multiline" yaml:"multiline"`
|
||||
Required bool `json:"required" datastore:"required" yaml:"required"`
|
||||
}
|
||||
|
||||
type Authentication struct {
|
||||
Required bool `json:"required" datastore:"required" yaml:"required" `
|
||||
Parameters []AuthenticationParams `json:"parameters" datastore:"parameters" yaml:"parameters"`
|
||||
}
|
||||
|
||||
type AuthenticationStore struct {
|
||||
Key string `json:"key" datastore:"key"`
|
||||
Value string `json:"value" datastore:"value"`
|
||||
}
|
||||
|
||||
type WorkflowAppActionParameter struct {
|
||||
Description string `json:"description" datastore:"description" yaml:"description"`
|
||||
ID string `json:"id" datastore:"id" yaml:"id,omitempty"`
|
||||
Name string `json:"name" datastore:"name" yaml:"name"`
|
||||
Example string `json:"example" datastore:"example" yaml:"example"`
|
||||
Value string `json:"value" datastore:"value" yaml:"value,omitempty"`
|
||||
Multiline bool `json:"multiline" datastore:"multiline" yaml:"multiline"`
|
||||
ActionField string `json:"action_field" datastore:"action_field" yaml:"actionfield,omitempty"`
|
||||
Variant string `json:"variant" datastore:"variant" yaml:"variant,omitempty"`
|
||||
Required bool `json:"required" datastore:"required" yaml:"required"`
|
||||
Schema SchemaDefinition `json:"schema" datastore:"schema" yaml:"schema"`
|
||||
}
|
||||
|
||||
type SchemaDefinition struct {
|
||||
Type string `json:"type" datastore:"type"`
|
||||
}
|
||||
|
||||
type WorkflowAppAction struct {
|
||||
Description string `json:"description" datastore:"description"`
|
||||
ID string `json:"id" datastore:"id" yaml:"id,omitempty"`
|
||||
Name string `json:"name" datastore:"name"`
|
||||
NodeType string `json:"node_type" datastore:"node_type"`
|
||||
Environment string `json:"environment" datastore:"environment"`
|
||||
Authentication []AuthenticationStore `json:"authentication" datastore:"authentication" yaml:"authentication,omitempty"`
|
||||
Parameters []WorkflowAppActionParameter `json:"parameters" datastore: "parameters"`
|
||||
Returns struct {
|
||||
Description string `json:"description" datastore:"returns" yaml:"description,omitempty"`
|
||||
ID string `json:"id" datastore:"id" yaml:"id,omitempty"`
|
||||
Schema SchemaDefinition `json:"schema" datastore:"schema" yaml:"schema"`
|
||||
} `json:"returns" datastore:"returns"`
|
||||
}
|
||||
|
||||
func copyFile(fromfile, tofile string) error {
|
||||
from, err := os.Open(fromfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer from.Close()
|
||||
|
||||
to, err := os.OpenFile(tofile, os.O_RDWR|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer to.Close()
|
||||
|
||||
_, err = io.Copy(to, from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildStructureGCP(client *storage.Client, swagger *openapi3.Swagger, curHash string) (string, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
// 1. Have baseline in bucket/generated_apps/baseline
|
||||
// 2. Copy the baseline to a new folder with identifier name
|
||||
|
||||
basePath := "generated_apps"
|
||||
identifier := fmt.Sprintf("%s-%s", swagger.Info.Title, curHash)
|
||||
appPath := fmt.Sprintf("%s/%s", basePath, identifier)
|
||||
fileNames := []string{"Dockerfile", "requirements.txt"}
|
||||
for _, file := range fileNames {
|
||||
src := client.Bucket(bucketName).Object(fmt.Sprintf("%s/baseline/%s", basePath, file))
|
||||
dst := client.Bucket(bucketName).Object(fmt.Sprintf("%s/%s", appPath, file))
|
||||
if _, err := dst.CopierFrom(src).Run(ctx); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return appPath, nil
|
||||
}
|
||||
|
||||
// Builds the base structure for the app that we're making
|
||||
// Returns error if anything goes wrong. This has to work if
|
||||
// the python code is supposed to be generated
|
||||
func buildStructure(swagger *openapi3.Swagger, curHash string) (string, error) {
|
||||
//log.Printf("%#v", swagger)
|
||||
|
||||
// adding md5 based on input data to not overwrite earlier data.
|
||||
generatedPath := "generated"
|
||||
identifier := fmt.Sprintf("%s-%s", swagger.Info.Title, curHash)
|
||||
appPath := fmt.Sprintf("%s/%s", generatedPath, identifier)
|
||||
|
||||
os.MkdirAll(appPath, os.ModePerm)
|
||||
os.Mkdir(fmt.Sprintf("%s/src", appPath), os.ModePerm)
|
||||
|
||||
err := copyFile("baseline/Dockerfile", fmt.Sprintf("%s/%s", appPath, "Dockerfile"))
|
||||
if err != nil {
|
||||
log.Println("Failed to move Dockerfile")
|
||||
return appPath, err
|
||||
}
|
||||
|
||||
err = copyFile("baseline/requirements.txt", fmt.Sprintf("%s/%s", appPath, "requirements.txt"))
|
||||
if err != nil {
|
||||
log.Println("Failed to move requrements.txt")
|
||||
return appPath, err
|
||||
}
|
||||
|
||||
return appPath, nil
|
||||
}
|
||||
|
||||
func makePythoncode(name, url, method string, parameters, optionalQueries []string) string {
|
||||
method = strings.ToLower(method)
|
||||
queryString := ""
|
||||
queryData := ""
|
||||
|
||||
// FIXME - this might break - need to check if ? or & should be set as query
|
||||
parameterData := ""
|
||||
if len(optionalQueries) > 0 {
|
||||
queryString += ", "
|
||||
for _, query := range optionalQueries {
|
||||
queryString += fmt.Sprintf("%s=\"\"", query)
|
||||
queryData += fmt.Sprintf(`
|
||||
if %s:
|
||||
url += f"&%s={%s}"`, query, query, query)
|
||||
}
|
||||
}
|
||||
|
||||
if len(parameters) > 0 {
|
||||
parameterData = fmt.Sprintf(", %s", strings.Join(parameters, ", "))
|
||||
}
|
||||
|
||||
// FIXME - add checks for query data etc
|
||||
data := fmt.Sprintf(` async def %s_%s(self%s%s):
|
||||
url=f"%s"
|
||||
%s
|
||||
return requests.%s(url).text
|
||||
`, name, method, parameterData, queryString, url, queryData, method)
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func generateYaml(swagger *openapi3.Swagger) (WorkflowApp, []string, error) {
|
||||
api := WorkflowApp{}
|
||||
log.Printf("%#v", swagger.Info)
|
||||
|
||||
if len(swagger.Info.Title) == 0 {
|
||||
return WorkflowApp{}, []string{}, errors.New("Swagger.Info.Title can't be empty.")
|
||||
}
|
||||
|
||||
if len(swagger.Servers) == 0 {
|
||||
return WorkflowApp{}, []string{}, errors.New("Swagger.Servers can't be empty. Add 'servers':[{'url':'hostname.com'}'")
|
||||
}
|
||||
|
||||
api.Name = swagger.Info.Title
|
||||
api.Description = swagger.Info.Description
|
||||
api.IsValid = true
|
||||
api.Link = swagger.Servers[0].URL // host doesnt exist lol
|
||||
api.AppVersion = "1.0.0"
|
||||
api.Environment = "cloud"
|
||||
api.ID = ""
|
||||
api.SmallImage = ""
|
||||
api.LargeImage = ""
|
||||
|
||||
// This is the python code to be generated
|
||||
// Could just as well be go at this point lol
|
||||
pythonFunctions := []string{}
|
||||
|
||||
for actualPath, path := range swagger.Paths {
|
||||
//log.Printf("%#v", path)
|
||||
//log.Printf("%#v", actualPath)
|
||||
// Find the path name and add it to makeCode() param
|
||||
|
||||
firstQuery := true
|
||||
if path.Get != nil {
|
||||
// What to do with this, hmm
|
||||
functionName := strings.ReplaceAll(path.Get.Summary, " ", "_")
|
||||
functionName = strings.ToLower(functionName)
|
||||
|
||||
action := WorkflowAppAction{
|
||||
Description: path.Get.Description,
|
||||
Name: path.Get.Summary,
|
||||
NodeType: "action",
|
||||
Environment: api.Environment,
|
||||
Parameters: []WorkflowAppActionParameter{},
|
||||
}
|
||||
|
||||
action.Returns.Schema.Type = "string"
|
||||
baseUrl := fmt.Sprintf("%s%s", api.Link, actualPath)
|
||||
|
||||
//log.Println(path.Parameters)
|
||||
|
||||
// Parameters: []WorkflowAppActionParameter{},
|
||||
// FIXME - add data for POST stuff
|
||||
firstQuery = true
|
||||
optionalQueries := []string{}
|
||||
parameters := []string{}
|
||||
optionalParameters := []WorkflowAppActionParameter{}
|
||||
if len(path.Get.Parameters) > 0 {
|
||||
for _, param := range path.Get.Parameters {
|
||||
curParam := WorkflowAppActionParameter{
|
||||
Name: param.Value.Name,
|
||||
Description: param.Value.Description,
|
||||
Multiline: false,
|
||||
Required: param.Value.Required,
|
||||
Schema: SchemaDefinition{
|
||||
Type: param.Value.Schema.Value.Type,
|
||||
},
|
||||
}
|
||||
|
||||
if param.Value.Required {
|
||||
action.Parameters = append(action.Parameters, curParam)
|
||||
} else {
|
||||
optionalParameters = append(optionalParameters, curParam)
|
||||
}
|
||||
|
||||
if param.Value.In == "path" {
|
||||
log.Printf("PATH!: %s", param.Value.Name)
|
||||
parameters = append(parameters, param.Value.Name)
|
||||
//baseUrl = fmt.Sprintf("%s%s", baseUrl)
|
||||
} else if param.Value.In == "query" {
|
||||
log.Printf("QUERY!: %s", param.Value.Name)
|
||||
if !param.Value.Required {
|
||||
optionalQueries = append(optionalQueries, param.Value.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
parameters = append(parameters, param.Value.Name)
|
||||
|
||||
if firstQuery {
|
||||
baseUrl = fmt.Sprintf("%s?%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
} else {
|
||||
baseUrl = fmt.Sprintf("%s&%s={%s}", baseUrl, param.Value.Name, param.Value.Name)
|
||||
firstQuery = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ensuring that they end up last in the specification
|
||||
// (order is ish important for optional params) - they need to be last.
|
||||
for _, optionalParam := range optionalParameters {
|
||||
action.Parameters = append(action.Parameters, optionalParam)
|
||||
}
|
||||
|
||||
curCode := makePythoncode(functionName, baseUrl, "get", parameters, optionalQueries)
|
||||
pythonFunctions = append(pythonFunctions, curCode)
|
||||
|
||||
api.Actions = append(api.Actions, action)
|
||||
}
|
||||
}
|
||||
|
||||
return api, pythonFunctions, nil
|
||||
}
|
||||
|
||||
func verifyApi(api WorkflowApp) WorkflowApp {
|
||||
if api.AppVersion == "" {
|
||||
api.AppVersion = "1.0.0"
|
||||
}
|
||||
|
||||
return api
|
||||
}
|
||||
|
||||
func dumpPythonGCP(client *storage.Client, basePath, name, version string, pythonFunctions []string) error {
|
||||
//log.Printf("%#v", api)
|
||||
log.Printf(strings.Join(pythonFunctions, "\n"))
|
||||
|
||||
parsedCode := fmt.Sprintf(`import requests
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from walkoff_app_sdk.app_base import AppBase
|
||||
|
||||
class %s(AppBase):
|
||||
"""
|
||||
Autogenerated class by Shuffler
|
||||
"""
|
||||
|
||||
__version__ = "%s"
|
||||
app_name = "%s"
|
||||
|
||||
def __init__(self, redis, logger, console_logger=None):
|
||||
self.verify = False
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
super().__init__(redis, logger, console_logger)
|
||||
|
||||
%s
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(CarbonBlack.run(), debug=True)
|
||||
`, name, version, name, strings.Join(pythonFunctions, "\n"))
|
||||
|
||||
// Create bucket handle
|
||||
ctx := context.Background()
|
||||
bucket := client.Bucket(bucketName)
|
||||
obj := bucket.Object(fmt.Sprintf("%s/src/app.py", basePath))
|
||||
w := obj.NewWriter(ctx)
|
||||
if _, err := fmt.Fprintf(w, parsedCode); err != nil {
|
||||
return err
|
||||
}
|
||||
// Close, just like writing a file.
|
||||
if err := w.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dumpPython(basePath, name, version string, pythonFunctions []string) error {
|
||||
//log.Printf("%#v", api)
|
||||
log.Printf(strings.Join(pythonFunctions, "\n"))
|
||||
|
||||
parsedCode := fmt.Sprintf(`import requests
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from walkoff_app_sdk.app_base import AppBase
|
||||
|
||||
class %s(AppBase):
|
||||
"""
|
||||
Autogenerated class by Shuffler
|
||||
"""
|
||||
|
||||
__version__ = "%s"
|
||||
app_name = "%s"
|
||||
|
||||
def __init__(self, redis, logger, console_logger=None):
|
||||
self.verify = False
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
super().__init__(redis, logger, console_logger)
|
||||
|
||||
%s
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(CarbonBlack.run(), debug=True)
|
||||
`, name, version, name, strings.Join(pythonFunctions, "\n"))
|
||||
|
||||
err := ioutil.WriteFile(fmt.Sprintf("%s/src/app.py", basePath), []byte(parsedCode), os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(parsedCode)
|
||||
|
||||
//log.Println(string(data))
|
||||
return nil
|
||||
}
|
||||
|
||||
func dumpApiGCP(client *storage.Client, basePath string, api WorkflowApp) error {
|
||||
//log.Printf("%#v", api)
|
||||
data, err := yaml.Marshal(api)
|
||||
if err != nil {
|
||||
log.Printf("Error with yaml marshal: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Create bucket handle
|
||||
ctx := context.Background()
|
||||
bucket := client.Bucket(bucketName)
|
||||
obj := bucket.Object(fmt.Sprintf("%s/app.yaml", basePath))
|
||||
w := obj.NewWriter(ctx)
|
||||
if _, err := fmt.Fprintf(w, string(data)); err != nil {
|
||||
return err
|
||||
}
|
||||
// Close, just like writing a file.
|
||||
if err := w.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//log.Println(string(data))
|
||||
return nil
|
||||
}
|
||||
|
||||
func dumpApi(basePath string, api WorkflowApp) error {
|
||||
//log.Printf("%#v", api)
|
||||
data, err := yaml.Marshal(api)
|
||||
if err != nil {
|
||||
log.Printf("Error with yaml marshal: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(fmt.Sprintf("%s/api.yaml", basePath), []byte(data), os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//log.Println(string(data))
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
data := []byte(`{"swagger":"3.0","info":{"title":"hi","description":"you","version":"1.0"},"servers":[{"url":"https://shuffler.io/api/v1"}],"host":"shuffler.io","basePath":"/api/v1","schemes":["https:"],"paths":{"/workflows":{"get":{"responses":{"default":{"description":"default","schema":{}}},"summary":"Get workflows","description":"Get workflows","parameters":[]}},"/workflows/{id}":{"get":{"responses":{"default":{"description":"default","schema":{}}},"summary":"Get workflow","description":"Get workflow","parameters":[{"in":"query","name":"forgetme","description":"Generated by shuffler.io OpenAPI","required":true,"schema":{"type":"string"}},{"in":"query","name":"anotherone","description":"Generated by shuffler.io OpenAPI","required":false,"schema":{"type":"string"}},{"in":"query","name":"hi","description":"Generated by shuffler.io OpenAPI","required":true,"schema":{"type":"string"}},{"in":"path","name":"id","description":"Generated by shuffler.io OpenAPI","required":true,"schema":{"type":"string"}}]}}},"securityDefinitions":{}}`)
|
||||
|
||||
ctx := context.Background()
|
||||
client, err := storage.NewClient(ctx)
|
||||
if err != nil {
|
||||
log.Printf("Failed to create client: %v", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
hasher := md5.New()
|
||||
hasher.Write(data)
|
||||
newmd5 := hex.EncodeToString(hasher.Sum(nil))
|
||||
swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(data)
|
||||
if err != nil {
|
||||
log.Printf("Swagger validation error: %s", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
if strings.Contains(swagger.Info.Title, " ") {
|
||||
strings.ReplaceAll(swagger.Info.Title, " ", "")
|
||||
}
|
||||
|
||||
basePath, err := buildStructureGCP(client, swagger, newmd5)
|
||||
if err != nil {
|
||||
log.Printf("Failed to build base structure: %s", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
api, pythonfunctions, err := generateYaml(swagger)
|
||||
if err != nil {
|
||||
log.Printf("Failed building and generating yaml: %s", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
err = dumpApiGCP(client, basePath, api)
|
||||
if err != nil {
|
||||
log.Printf("Failed dumping yaml: %s", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
err = dumpPythonGCP(client, basePath, swagger.Info.Title, swagger.Info.Version, pythonfunctions)
|
||||
if err != nil {
|
||||
log.Printf("Failed dumping python: %s", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# Generators
|
||||
This folder contains an attempt at creating apps & similar from python libraries
|
||||
|
||||
## Howto
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Base our app image off of the WALKOFF App SDK image
|
||||
FROM frikky/shuffle:app_sdk as base
|
||||
|
||||
# We're going to stage away all of the bloat from the build tools so lets create a builder stage
|
||||
FROM base as builder
|
||||
|
||||
# Install all alpine build tools needed for our pip installs
|
||||
RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev
|
||||
|
||||
# Install all of our pip packages in a single directory that we can copy to our base image later
|
||||
RUN mkdir /install
|
||||
WORKDIR /install
|
||||
COPY requirements.txt /requirements.txt
|
||||
RUN pip install --prefix="/install" -r /requirements.txt
|
||||
|
||||
# Switch back to our base image and copy in all of our built packages and source code
|
||||
FROM base
|
||||
COPY --from=builder /install /usr/local
|
||||
COPY src /app
|
||||
|
||||
# Install any binary dependencies needed in our final image - this can be a lot of different stuff
|
||||
RUN apk --no-cache add --update libmagic
|
||||
|
||||
# Finally, lets run our app!
|
||||
WORKDIR /app
|
||||
CMD python app.py --log-level DEBUG
|
||||
@@ -0,0 +1,14 @@
|
||||
version: '3.4'
|
||||
services:
|
||||
thehive4py:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
env_file:
|
||||
- env.txt
|
||||
restart: "no"
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 10
|
||||
restart_policy:
|
||||
condition: none
|
||||
@@ -0,0 +1,4 @@
|
||||
REDIS_URI=redis://redis
|
||||
REDIS_ACTION_RESULT_CH=action-results
|
||||
REDIS_ACTION_RESULTS_GROUP=action-results-group
|
||||
APP_NAME=
|
||||
@@ -0,0 +1 @@
|
||||
# No extra requirements needed
|
||||
@@ -0,0 +1,525 @@
|
||||
# Read a directory
|
||||
# Find python functions
|
||||
# Generate yaml
|
||||
|
||||
# FIXME:
|
||||
# Position, default_value and function in params
|
||||
|
||||
|
||||
|
||||
# TO ADD:
|
||||
# from walkoff_app_sdk.app_base import AppBase
|
||||
# class TheHive(AppBase): <-- Add appbase
|
||||
# __version__ = version within class
|
||||
# app_name = app_name in class
|
||||
# if __name__ == "__main__":
|
||||
# asyncio.run(TheHive.run(), debug=True) <-- APPEND SHIT HERE
|
||||
# async infront of every function?
|
||||
# Add async library to imports
|
||||
|
||||
# Make wrapper class? <-- within app.py
|
||||
|
||||
|
||||
# 1. Generate app.yaml (functions with returns etc)
|
||||
# 2. Generate app.py (with imports to the original function etc
|
||||
# 3. Build requirements.txt based on the items necessary
|
||||
# 4. Check whether it runs?
|
||||
|
||||
import os
|
||||
import yaml
|
||||
import jedi
|
||||
import shutil
|
||||
|
||||
# Testing generator
|
||||
entrypoint_directory = "thehive4py"
|
||||
include_requirements = False
|
||||
if not os.path.exists(entrypoint_directory):
|
||||
include_requirements = True
|
||||
print("Requires library in requirements")
|
||||
|
||||
|
||||
source = '''
|
||||
import %s
|
||||
%s.
|
||||
''' % (entrypoint_directory, entrypoint_directory)
|
||||
splitsource = source.split("\n")
|
||||
|
||||
# Find modules AKA files
|
||||
def get_modules():
|
||||
curline = splitsource[-2]
|
||||
print(splitsource, curline)
|
||||
entrypoint = jedi.Script(source, line=len(splitsource)-1, column=len(curline))
|
||||
|
||||
modules = []
|
||||
completions = entrypoint.completions()
|
||||
for item in completions:
|
||||
if item.type != "module":
|
||||
continue
|
||||
|
||||
|
||||
modules.append(item.name)
|
||||
|
||||
return modules
|
||||
|
||||
def loop_modules(modules, data):
|
||||
# Loop modules AKA files - this is garbage but works lmao
|
||||
for module in modules:
|
||||
modulesplit = list(splitsource)
|
||||
modulesplit[2] = "%s%s." % (modulesplit[2], module)
|
||||
|
||||
#print(modulesplit)
|
||||
source = "\n".join(modulesplit)
|
||||
entrypoint = jedi.Script(source, line=len(modulesplit)-1, column=len(modulesplit[2]))
|
||||
|
||||
# Loop classes in the files
|
||||
for classcompletion in entrypoint.completions():
|
||||
if classcompletion.type != "class":
|
||||
continue
|
||||
|
||||
if not classcompletion.full_name.startswith(modulesplit[2]):
|
||||
continue
|
||||
|
||||
# Same thing again, but for functions within classes
|
||||
# CBA with subclasses etc atm
|
||||
|
||||
#print(classcompletion.full_name, modulesplit[2])
|
||||
|
||||
classplit = list(modulesplit)
|
||||
classplit[2] = "%s." % (classcompletion.full_name)
|
||||
|
||||
#print(modulesplit)
|
||||
source = "\n".join(classplit)
|
||||
entrypoint = jedi.Script(source, line=len(classplit)-1, column=len(classplit[2]))
|
||||
|
||||
# List of functions sorted by their name
|
||||
nameinternalfunctions = []
|
||||
for functioncompletion in entrypoint.completions():
|
||||
if functioncompletion.type != "function":
|
||||
continue
|
||||
|
||||
if not functioncompletion.full_name.startswith(classplit[2]):
|
||||
continue
|
||||
|
||||
nameinternalfunctions.append(functioncompletion)
|
||||
|
||||
#print(nameinternalfunctions)
|
||||
|
||||
# List of functions sorted by their line in the file (reversed)
|
||||
# CODE USED TO ACTUALLY PRINT THE CODE
|
||||
|
||||
#prevnumber = 0
|
||||
#numberinternalfunctions = sorted(nameinternalfunctions, key=lambda k: k.line, reverse=True)
|
||||
numberinternalfunctions = sorted(nameinternalfunctions, key=lambda k: k.line)
|
||||
prevnumber = 0
|
||||
|
||||
origparent = "TheHiveApi"
|
||||
# Predefined functions? - maybe skip: __init__
|
||||
skip_functions = ["__init__"]
|
||||
skip_parameters = [""]
|
||||
cnt = 0
|
||||
for item in numberinternalfunctions:
|
||||
if item.parent().name != origparent:
|
||||
continue
|
||||
|
||||
# FIXME - prolly wrong
|
||||
if item.name in skip_functions or (item.name.startswith("__") and item.name.endswith("__")):
|
||||
continue
|
||||
|
||||
# FIXME - remove
|
||||
#print(item.get_line_code())
|
||||
#if "=" not in item.get_line_code():
|
||||
# continue
|
||||
|
||||
#if item.docstring() in item.get_line_code():
|
||||
# print("NO DOCSTRING FOR: %s. Skipping!" % item.name)
|
||||
# cnt += 1
|
||||
# continue
|
||||
|
||||
curfunction = {
|
||||
"name": item.name,
|
||||
"description": "HEY",
|
||||
}
|
||||
|
||||
params = []
|
||||
curreturn = {}
|
||||
|
||||
function = item.docstring().split("\n")[0]
|
||||
for line in item.docstring().split("\n"):
|
||||
if not line:
|
||||
continue
|
||||
|
||||
linesplit = line.split(" ")
|
||||
try:
|
||||
curname = linesplit[1][:-1]
|
||||
except IndexError as e:
|
||||
print("IndexError: %s. Line: %s" % (e, line))
|
||||
continue
|
||||
|
||||
paramfound = False
|
||||
foundindex = 0
|
||||
cnt = 0
|
||||
for param in params:
|
||||
#print(param["name"], curname)
|
||||
if param["name"] == curname:
|
||||
#print("ALREADY EXISTS: %s" % curname)
|
||||
paramfound = True
|
||||
foundindex = cnt
|
||||
break
|
||||
|
||||
cnt += 1
|
||||
|
||||
# CBA finding a good parser, as that seemed impossible :(
|
||||
# Skipped :return
|
||||
if line.startswith(":param"):
|
||||
if not paramfound:
|
||||
#print("HERE!: %s" % line)
|
||||
|
||||
curparam = {}
|
||||
#print(line)
|
||||
curparam["name"] = curname
|
||||
curparam["description"] = " ".join(linesplit[2:])
|
||||
#print(curparam["description"])
|
||||
if "\r\n" in curparam["description"]:
|
||||
curparam["description"] = " ".join(curparam["description"].split("\r\n"))
|
||||
if "\n" in curparam["description"]:
|
||||
curparam["description"] = " ".join(curparam["description"].split("\n"))
|
||||
|
||||
curparam["function"] = function
|
||||
|
||||
#curparam["docstring"] = item.docstring()
|
||||
params.append(curparam)
|
||||
elif line.startswith(":type"):
|
||||
if paramfound:
|
||||
params[foundindex]["schema"] = {}
|
||||
params[foundindex]["schema"]["type"] = " ".join(linesplit[2:])
|
||||
#print(params)
|
||||
|
||||
#print(line)
|
||||
elif line.startswith(":rtype"):
|
||||
curreturn["type"] = " ".join(linesplit[1:])
|
||||
|
||||
|
||||
# Check whether param is required
|
||||
# FIXME - remove
|
||||
#if len(params) != 0:
|
||||
# print(params)
|
||||
# continue
|
||||
|
||||
#print(function)
|
||||
#print(params)
|
||||
|
||||
# FIXME - this might crash when missing docstrings
|
||||
# FIXME - is also bad splitt (can be written without e.g. spaces
|
||||
# This should maybe be done first? idk
|
||||
fields = function.split("(")[1][:-1].split(", ")
|
||||
if len(params) == 0:
|
||||
# Handle missing docstrings
|
||||
params = []
|
||||
for item in fields:
|
||||
params.append({
|
||||
"name": item,
|
||||
"description": "",
|
||||
"schema": {},
|
||||
"function": function,
|
||||
})
|
||||
|
||||
cnt = 0
|
||||
for param in params:
|
||||
found = False
|
||||
|
||||
for field in fields:
|
||||
if param["name"] in field:
|
||||
if "=" in field:
|
||||
param["required"] = False
|
||||
param["default_value"] = field
|
||||
else:
|
||||
param["required"] = True
|
||||
|
||||
found = True
|
||||
break
|
||||
|
||||
if not param.get("schema"):
|
||||
#print("Defining object schema for %s" % param["name"])
|
||||
param["schema"] = {}
|
||||
param["schema"]["type"] = "object"
|
||||
|
||||
param["position"] = cnt
|
||||
|
||||
if not found:
|
||||
# FIXME - waht here?
|
||||
pass
|
||||
#print("HANDLE NOT FOUND")
|
||||
#print(param)
|
||||
#print(fields)
|
||||
|
||||
cnt += 1
|
||||
|
||||
if len(params) > 0:
|
||||
curfunction["parameters"] = params
|
||||
|
||||
if not curfunction.get("returns"):
|
||||
curfunction["returns"] = {}
|
||||
curfunction["returns"]["schema"] = {}
|
||||
curfunction["returns"]["schema"]["type"] = "object"
|
||||
|
||||
#print(curfunction)
|
||||
try:
|
||||
print("Finished prepping %s with %d parameters and return %s" % (item.name, len(curfunction["parameters"]), curfunction["returns"]["schema"]["type"]))
|
||||
except KeyError as e:
|
||||
print("Error: %s" % e)
|
||||
#print("Finished prepping %s with 0 parameters and return %s" % (item.name, curfunction["returns"]["schema"]["type"]))
|
||||
curfunction["parameters"] = []
|
||||
except AttributeError as e:
|
||||
pass
|
||||
|
||||
try:
|
||||
data["actions"].append(curfunction)
|
||||
except KeyError:
|
||||
data["actions"] = []
|
||||
data["actions"].append(curfunction)
|
||||
|
||||
#return data
|
||||
|
||||
# FIXME
|
||||
#if cnt == breakcnt:
|
||||
# break
|
||||
|
||||
#cnt += 1
|
||||
|
||||
# Check if
|
||||
|
||||
|
||||
# THIS IS TO GET READ THE ACTUAL CODE
|
||||
#functioncode = item.get_line_code(after=prevnumber-item.line-1)
|
||||
#prevnumber = item.line
|
||||
|
||||
# break
|
||||
return data
|
||||
|
||||
# Generates the base information necessary to make an api.yaml file
|
||||
def generate_base_yaml(filename, version, appname):
|
||||
print("Generating base app for library %s with version %s" % (appname, version))
|
||||
data = {
|
||||
"walkoff_version": "0.0.1",
|
||||
"app_version": version,
|
||||
"name": appname,
|
||||
"description": "Autogenerated yaml with @Frikkylikeme's generator",
|
||||
"contact_info": {
|
||||
"name": "@frikkylikeme",
|
||||
"url": "https://github.com/frikky",
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
|
||||
def generate_app(filepath, data):
|
||||
|
||||
tbd = [
|
||||
"library_path",
|
||||
"import_class",
|
||||
"required_init"
|
||||
]
|
||||
|
||||
# FIXME - add to data dynamically and remove
|
||||
data["library_path"] = "thehive4py.api"
|
||||
data["import_class"] = "TheHiveApi"
|
||||
data["required_init"] = {"url": "http://localhost:9000", "principal": "asd"}
|
||||
|
||||
wrapperstring = ""
|
||||
cnt = 0
|
||||
# FIXME - only works for strings currently
|
||||
for key, value in data["required_init"].items():
|
||||
if cnt != len(data["required_init"]):
|
||||
wrapperstring += "%s=\"%s\", " % (key, value)
|
||||
|
||||
cnt += 1
|
||||
|
||||
wrapperstring = wrapperstring[:-2]
|
||||
wrapper = "self.wrapper = %s(%s)" % (data["import_class"], wrapperstring)
|
||||
|
||||
name = data["name"]
|
||||
if ":" in data["name"]:
|
||||
name = data["name"].split(":")[0]
|
||||
|
||||
if not data.get("actions"):
|
||||
print("No actions found for %s in path %s" % (entrypoint_directory, data["library_path"]))
|
||||
print("Folder might be missing (or unexported (__init__.py), library not installed (pip) or library action missing")
|
||||
exit()
|
||||
|
||||
functions = []
|
||||
for action in data["actions"]:
|
||||
internalparamstring = ""
|
||||
paramstring = ""
|
||||
try:
|
||||
for param in action["parameters"]:
|
||||
if param["required"] == False:
|
||||
paramstring += "%s, " % (param["default_value"])
|
||||
else:
|
||||
paramstring += "%s, " % param["name"]
|
||||
except KeyError:
|
||||
action["parameters"] = []
|
||||
|
||||
#internalparamstring += "%s, " % param["name"]
|
||||
|
||||
paramstring = paramstring[:-2]
|
||||
#internalparamstring = internalparamstring[:-2]
|
||||
|
||||
functionstring = ''' async def %s(%s):
|
||||
return self.wrapper.%s(%s)
|
||||
''' % (action["name"], paramstring, action["name"], paramstring)
|
||||
|
||||
functions.append(functionstring)
|
||||
|
||||
filedata = '''from walkoff_app_sdk.app_base import AppBase
|
||||
import asyncio
|
||||
|
||||
from %s import %s
|
||||
|
||||
class %sWrapper(AppBase):
|
||||
|
||||
__version__ = "%s"
|
||||
app_name = "%s"
|
||||
|
||||
def __init__(self, redis, logger, console_logger=None):
|
||||
"""
|
||||
Each app should have this __init__ to set up Redis and logging.
|
||||
:param redis:
|
||||
:param logger:
|
||||
:param console_logger:
|
||||
"""
|
||||
|
||||
super().__init__(redis, logger, console_logger)
|
||||
%s
|
||||
|
||||
%s
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(%sWrapper.run(), debug=True)
|
||||
''' % ( \
|
||||
data["library_path"],
|
||||
data["import_class"],
|
||||
name,
|
||||
data["app_version"],
|
||||
name,
|
||||
wrapper,
|
||||
"\n".join(functions),
|
||||
name
|
||||
)
|
||||
|
||||
# Simple key cleanup
|
||||
for item in tbd:
|
||||
try:
|
||||
del data[item]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
tbd_action = []
|
||||
|
||||
tbd_param = [
|
||||
"position",
|
||||
"default_value",
|
||||
"function"
|
||||
]
|
||||
|
||||
for action in data["actions"]:
|
||||
for param in action["parameters"]:
|
||||
for item in tbd_param:
|
||||
try:
|
||||
del param[item]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
for item in tbd_action:
|
||||
try:
|
||||
del action[item]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# FIXME - add how to initialize the class
|
||||
with open(filepath, "w") as tmp:
|
||||
tmp.write(filedata)
|
||||
|
||||
return data
|
||||
|
||||
def dump_yaml(filename, data):
|
||||
with open(filename, 'w') as outfile:
|
||||
yaml.dump(data, outfile, default_flow_style=False)
|
||||
|
||||
def build_base_structure(appname, version):
|
||||
outputdir = "generated"
|
||||
app_path = "%s/%s" % (outputdir, appname)
|
||||
filepath = "%s/%s" % (app_path, version)
|
||||
srcdir_path = "%s/src" % (filepath)
|
||||
|
||||
directories = [
|
||||
outputdir,
|
||||
app_path,
|
||||
filepath,
|
||||
srcdir_path
|
||||
]
|
||||
|
||||
for directory in directories:
|
||||
try:
|
||||
os.mkdir(directory)
|
||||
except FileExistsError:
|
||||
print("%s already exists. Skipping." % directory)
|
||||
|
||||
# "docker-compose.yml",
|
||||
# "env.txt",
|
||||
filenames = [
|
||||
"Dockerfile",
|
||||
"requirements.txt"
|
||||
]
|
||||
|
||||
#if strings.
|
||||
# include_requirements = False
|
||||
|
||||
for filename in filenames:
|
||||
ret = shutil.copyfile("baseline/%s" % filename, "%s/%s" % (filepath, filename))
|
||||
print("Copied baseline/%s." % filename)
|
||||
|
||||
def move_files(appname, version):
|
||||
applocation = "../../functions/apps/%s" % appname
|
||||
if not os.path.exists("../../functions/apps"):
|
||||
os.mkdir("../../functions/apps")
|
||||
|
||||
if not os.path.exists(applocation):
|
||||
os.mkdir(applocation)
|
||||
|
||||
versionlocation = "%s/%s" % (applocation, version)
|
||||
if not os.path.exists(versionlocation):
|
||||
os.mkdir(versionlocation)
|
||||
|
||||
shutil.rmtree(versionlocation)
|
||||
shutil.move("generated/%s/%s" % (appname, version), versionlocation)
|
||||
|
||||
print("\nMoved files to %s" % versionlocation)
|
||||
|
||||
|
||||
def main():
|
||||
appname = entrypoint_directory
|
||||
version = "0.0.1"
|
||||
output_path = "generated/%s/%s" % (appname, version)
|
||||
api_yaml_path = "%s/api.yaml" % (output_path)
|
||||
app_python_path = "%s/src/app.py" % (output_path)
|
||||
|
||||
# Builds the directory structure for the app
|
||||
build_base_structure(appname, version)
|
||||
|
||||
# Generates the yaml based on input library etc
|
||||
data = generate_base_yaml(api_yaml_path, version, appname)
|
||||
modules = get_modules()
|
||||
data = loop_modules(modules, data)
|
||||
|
||||
# Generates app file
|
||||
data = generate_app(app_python_path, data)
|
||||
|
||||
# Dumps the yaml to specified directory
|
||||
dump_yaml(api_yaml_path, data)
|
||||
|
||||
# Move the file to functions/apps repository
|
||||
move_files(appname, version)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,2 @@
|
||||
jedi
|
||||
pyyaml
|
||||
@@ -0,0 +1,26 @@
|
||||
from golang as builder
|
||||
|
||||
# Add files
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
ADD ./go-app/main.go /app
|
||||
ADD ./go-app/walkoff.go /app
|
||||
ADD ./go-app/docker.go /app
|
||||
ADD ./go-app/codegen.go /app
|
||||
|
||||
ADD ./go-app/go.mod /app
|
||||
|
||||
RUN go get -v
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o webapp .
|
||||
|
||||
# Certificate build
|
||||
FROM alpine:latest as certs
|
||||
RUN apk --update add ca-certificates
|
||||
|
||||
from scratch
|
||||
COPY --from=builder /app/ /
|
||||
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
|
||||
EXPOSE 5001
|
||||
CMD ["./webapp"]
|
||||
@@ -0,0 +1,61 @@
|
||||
# Backend setup
|
||||
1 Go to https://console.cloud.google.com/apis/credentials?project=shuffle-241517&folder&organizationId and get credentials
|
||||
2. Move the file to current folder (or make step 3 be your download folder or w/e)
|
||||
3. export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/Shuffle-2a19ff64af66.json
|
||||
|
||||
# Backend run testserver (appengine)
|
||||
1. Set up gcloud locally
|
||||
```bash
|
||||
dev_appserver.py go-app/ --port=5001 --host=0.0.0.0 --enable_host_checking=false
|
||||
```
|
||||
|
||||
# Backend deploy
|
||||
* I created a simple script that moves the data into your GOPATH and deploys for you. This will require more tests in the future.
|
||||
|
||||
# OpenAPI spec checks
|
||||
Paths:
|
||||
* /path/{variablename}?queryvar= <-- variable
|
||||
* ^variablename needs to be part of parameters too.
|
||||
* ^queryvar needs to be part of parameters too.
|
||||
```
|
||||
parameters:
|
||||
- name: variablename
|
||||
in: path
|
||||
description: Blah blah
|
||||
required: true/false
|
||||
schema:
|
||||
type: string
|
||||
enum: [a, b, c] # <-- not necessary, but could be great
|
||||
- name: queryvar
|
||||
in: query
|
||||
description: blah blah
|
||||
required: true/false
|
||||
schema:
|
||||
type: string
|
||||
enum: [a, b, c]
|
||||
```
|
||||
* requestBody? Not in GET, DELETE & HEAD. Can consume JSON, XML, form data, plai ntext & others. Can use markdown for the description.
|
||||
* Do I care about the response? Maybe :o
|
||||
|
||||
```
|
||||
requestBody:
|
||||
description: Optional kind of description
|
||||
required: false/true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: true
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
fav_number:
|
||||
type: integer
|
||||
required:
|
||||
- name
|
||||
- email
|
||||
encoding:
|
||||
color:
|
||||
style: form
|
||||
explode: false
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
# 2. docker run -p 8000:8000
|
||||
FROM google/cloud-sdk
|
||||
|
||||
EXPOSE 8000
|
||||
CMD ["gcloud", "beta", "emulators", "datastore", "start", "--project=shuffle", "--host-port", "0.0.0.0:8000", "--data-dir=/etc/shuffle"]
|
||||
@@ -0,0 +1,8 @@
|
||||
# Deploys to backend
|
||||
echo "Deploying to appengine."
|
||||
mkdir -p $GOPATH/src/github.com/frikky/shuffle
|
||||
cp -r go-app/* $GOPATH/src/github.com/frikky/shuffle
|
||||
cd $GOPATH/src/github.com/frikky/shuffle
|
||||
go build
|
||||
go test
|
||||
gcloud app deploy $GOPATH/src/github.com/frikky/shuffle/app.yaml
|
||||
@@ -0,0 +1,6 @@
|
||||
## RUN
|
||||
```bash
|
||||
cd shaffuru/
|
||||
dev_appserver.py --port=5001 --host=0.0.0.0 --enable_host_checking=false .
|
||||
```
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
runtime: go111
|
||||
|
||||
env_variables:
|
||||
|
||||
automatic_scaling:
|
||||
max_instances: 1
|
||||
min_instances: 1
|
||||
|
||||
handlers:
|
||||
- url: /api/(.*)
|
||||
script: auto
|
||||
secure: always
|
||||
- url: /static/js/(.*)
|
||||
static_files: build/static/js/\1
|
||||
upload: build/static/js/(.*)
|
||||
secure: always
|
||||
- url: /static/css/(.*)
|
||||
static_files: build/static/css/\1
|
||||
upload: build/static/css/(.*)
|
||||
secure: always
|
||||
- url: /images/(.*)
|
||||
static_files: build/images/\1
|
||||
upload: build/images/(.*)
|
||||
secure: always
|
||||
- url: /(.*\.(json|ico))$
|
||||
static_files: build/\1
|
||||
upload: build/.*\.(json|ico)$
|
||||
secure: always
|
||||
- url: /manifest.json
|
||||
static_files: build/manifest.json
|
||||
upload: build/manifest.json
|
||||
secure: always
|
||||
|
||||
# lol.. wildcard doesn't work with /api/(.*) for some reason
|
||||
- url: /
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /home
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /passwordreset
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /login
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /register
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /workflows
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /workflows/(.*)
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /info/(.*)
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /docs/(.*)
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /docs
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /settings
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /apps
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /contact
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /apps/(.*)
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /register/(.*)
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
- url: /passwordreset/(.*)
|
||||
static_files: build/index.html
|
||||
upload: build/index.html
|
||||
secure: always
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,644 @@
|
||||
package main
|
||||
|
||||
// Docker
|
||||
import (
|
||||
"archive/tar"
|
||||
"path/filepath"
|
||||
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
network "github.com/docker/docker/api/types/network"
|
||||
"github.com/docker/docker/client"
|
||||
natting "github.com/docker/go-connections/nat"
|
||||
"github.com/go-git/go-billy/v5"
|
||||
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
//"google.golang.org/appengine"
|
||||
)
|
||||
|
||||
// Parses a directory with a Dockerfile into a tar for Docker images..
|
||||
func getParsedTar(tw *tar.Writer, baseDir, extra string) error {
|
||||
return filepath.Walk(baseDir, func(file string, fi os.FileInfo, err error) error {
|
||||
if file == baseDir {
|
||||
return nil
|
||||
}
|
||||
|
||||
//log.Printf("File: %s", file)
|
||||
//log.Printf("Fileinfo: %#v", fi)
|
||||
switch mode := fi.Mode(); {
|
||||
case mode.IsDir():
|
||||
// do directory recursion
|
||||
//log.Printf("DIR: %s", file)
|
||||
|
||||
// Append "src" as extra here
|
||||
filenamesplit := strings.Split(file, "/")
|
||||
filename := fmt.Sprintf("%s%s/", extra, filenamesplit[len(filenamesplit)-1])
|
||||
|
||||
tmpExtra := fmt.Sprintf(filename)
|
||||
//log.Printf("TmpExtra: %s", tmpExtra)
|
||||
err = getParsedTar(tw, file, tmpExtra)
|
||||
if err != nil {
|
||||
log.Printf("Directory parse issue: %s", err)
|
||||
return err
|
||||
}
|
||||
case mode.IsRegular():
|
||||
// do file stuff
|
||||
//log.Printf("FILE: %s", file)
|
||||
|
||||
fileReader, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read the actual Dockerfile
|
||||
readFile, err := ioutil.ReadAll(fileReader)
|
||||
if err != nil {
|
||||
log.Printf("Not file: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
filenamesplit := strings.Split(file, "/")
|
||||
filename := fmt.Sprintf("%s%s", extra, filenamesplit[len(filenamesplit)-1])
|
||||
//log.Printf("Filename: %s", filename)
|
||||
tarHeader := &tar.Header{
|
||||
Name: filename,
|
||||
Size: int64(len(readFile)),
|
||||
}
|
||||
|
||||
//Writes the header described for the TAR file
|
||||
err = tw.WriteHeader(tarHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Writes the dockerfile data to the TAR file
|
||||
_, err = tw.Write(readFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Custom TAR builder in memory for Docker images
|
||||
func getParsedTarMemory(fs billy.Filesystem, tw *tar.Writer, baseDir, extra string) error {
|
||||
// This one has to use baseDir + Extra
|
||||
newBase := fmt.Sprintf("%s%s", baseDir, extra)
|
||||
dir, err := fs.ReadDir(newBase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, file := range dir {
|
||||
// Folder?
|
||||
switch mode := file.Mode(); {
|
||||
case mode.IsDir():
|
||||
filename := file.Name()
|
||||
filenamesplit := strings.Split(filename, "/")
|
||||
|
||||
tmpExtra := fmt.Sprintf("%s%s/", extra, filenamesplit[len(filenamesplit)-1])
|
||||
//log.Printf("EXTRA: %s", tmpExtra)
|
||||
err = getParsedTarMemory(fs, tw, baseDir, tmpExtra)
|
||||
if err != nil {
|
||||
log.Printf("Directory parse issue: %s", err)
|
||||
return err
|
||||
}
|
||||
case mode.IsRegular():
|
||||
filenamesplit := strings.Split(file.Name(), "/")
|
||||
filename := fmt.Sprintf("%s%s", extra, filenamesplit[len(filenamesplit)-1])
|
||||
// Newbase
|
||||
path := fmt.Sprintf("%s%s", newBase, file.Name())
|
||||
|
||||
fileReader, err := fs.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
readFile, err := ioutil.ReadAll(fileReader)
|
||||
if err != nil {
|
||||
log.Printf("Not file: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
//log.Printf("Filename: %s", filename)
|
||||
// FIXME - might need the folder from EXTRA here
|
||||
// Name has to be e.g. just "requirements.txt"
|
||||
tarHeader := &tar.Header{
|
||||
Name: filename,
|
||||
Size: int64(len(readFile)),
|
||||
}
|
||||
|
||||
//Writes the header described for the TAR file
|
||||
err = tw.WriteHeader(tarHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Writes the dockerfile data to the TAR file
|
||||
_, err = tw.Write(readFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Custom Docker image builder wrapper in memory
|
||||
func buildImageMemory(fs billy.Filesystem, tags []string, dockerfileFolder string) error {
|
||||
ctx := context.Background()
|
||||
client, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
log.Printf("Unable to create docker client: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
tw := tar.NewWriter(buf)
|
||||
defer tw.Close()
|
||||
|
||||
log.Printf("Setting up memory build structure for folder: %s", dockerfileFolder)
|
||||
err = getParsedTarMemory(fs, tw, dockerfileFolder, "")
|
||||
if err != nil {
|
||||
log.Printf("Tar issue: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
dockerFileTarReader := bytes.NewReader(buf.Bytes())
|
||||
|
||||
// Dockerfile is inside the TAR itself. Not local context
|
||||
buildOptions := types.ImageBuildOptions{
|
||||
Remove: true,
|
||||
Tags: tags,
|
||||
}
|
||||
|
||||
// Build the actual image
|
||||
imageBuildResponse, err := client.ImageBuild(
|
||||
ctx,
|
||||
dockerFileTarReader,
|
||||
buildOptions,
|
||||
)
|
||||
|
||||
log.Printf("IMAGERESPONSE: %#v", imageBuildResponse.Body)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read the STDOUT from the build process
|
||||
defer imageBuildResponse.Body.Close()
|
||||
_, err = io.Copy(os.Stdout, imageBuildResponse.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildImage(tags []string, dockerfileFolder string) error {
|
||||
ctx := context.Background()
|
||||
client, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
log.Printf("Unable to create docker client: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Tags: %s", tags)
|
||||
dockerfileSplit := strings.Split(dockerfileFolder, "/")
|
||||
|
||||
// Create a buffer
|
||||
buf := new(bytes.Buffer)
|
||||
tw := tar.NewWriter(buf)
|
||||
defer tw.Close()
|
||||
baseDir := strings.Join(dockerfileSplit[0:len(dockerfileSplit)-1], "/")
|
||||
|
||||
// Builds the entire folder into buf
|
||||
err = getParsedTar(tw, baseDir, "")
|
||||
if err != nil {
|
||||
log.Printf("Tar issue: %s", err)
|
||||
}
|
||||
|
||||
dockerFileTarReader := bytes.NewReader(buf.Bytes())
|
||||
buildOptions := types.ImageBuildOptions{
|
||||
Remove: true,
|
||||
Tags: tags,
|
||||
}
|
||||
|
||||
// Build the actual image
|
||||
imageBuildResponse, err := client.ImageBuild(
|
||||
ctx,
|
||||
dockerFileTarReader,
|
||||
buildOptions,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read the STDOUT from the build process
|
||||
defer imageBuildResponse.Body.Close()
|
||||
_, err = io.Copy(os.Stdout, imageBuildResponse.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FIXME - very specific for webhooks. Make it easier?
|
||||
func stopWebhook(image string, identifier string) error {
|
||||
ctx := context.Background()
|
||||
|
||||
containername := fmt.Sprintf("%s-%s", image, identifier)
|
||||
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
log.Println("Unable to create docker client")
|
||||
return err
|
||||
}
|
||||
|
||||
// containers, err := cli.ContainerList(ctx, types.ContainerListOptions{
|
||||
// All: true,
|
||||
// })
|
||||
|
||||
if err := cli.ContainerStop(ctx, containername, nil); err != nil {
|
||||
log.Printf("Unable to stop container %s - running removal anyway, just in case: %s", containername, err)
|
||||
}
|
||||
|
||||
removeOptions := types.ContainerRemoveOptions{
|
||||
RemoveVolumes: true,
|
||||
Force: true,
|
||||
}
|
||||
|
||||
if err := cli.ContainerRemove(ctx, containername, removeOptions); err != nil {
|
||||
log.Printf("Unable to remove container: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FIXME - remember to set DOCKER_API_VERSION
|
||||
// FIXME - remove github.com/docker/docker/vendor
|
||||
// FIXME - Library dependencies for NAT is fucked..
|
||||
// https://docs.docker.com/develop/sdk/examples/
|
||||
func deployWebhook(image string, identifier string, path string, port string, callbackurl string, apikey string) error {
|
||||
cli, err := client.NewEnvClient()
|
||||
if err != nil {
|
||||
fmt.Println("Unable to create docker client")
|
||||
return err
|
||||
}
|
||||
|
||||
newport, err := natting.NewPort("tcp", port)
|
||||
if err != nil {
|
||||
fmt.Println("Unable to create docker port")
|
||||
return err
|
||||
}
|
||||
|
||||
// FIXME - logging?
|
||||
|
||||
hostConfig := &container.HostConfig{
|
||||
PortBindings: natting.PortMap{
|
||||
newport: []natting.PortBinding{
|
||||
{
|
||||
HostIP: "0.0.0.0",
|
||||
HostPort: port,
|
||||
},
|
||||
},
|
||||
},
|
||||
RestartPolicy: container.RestartPolicy{
|
||||
Name: "always",
|
||||
},
|
||||
LogConfig: container.LogConfig{
|
||||
Type: "json-file",
|
||||
Config: map[string]string{},
|
||||
},
|
||||
}
|
||||
|
||||
//networkConfig := &network.NetworkSettings{}
|
||||
networkConfig := &network.NetworkingConfig{
|
||||
EndpointsConfig: map[string]*network.EndpointSettings{},
|
||||
}
|
||||
|
||||
test := &network.EndpointSettings{
|
||||
Gateway: "helo",
|
||||
}
|
||||
|
||||
networkConfig.EndpointsConfig["bridge"] = test
|
||||
|
||||
exposedPorts := map[natting.Port]struct{}{
|
||||
newport: struct{}{},
|
||||
}
|
||||
|
||||
config := &container.Config{
|
||||
Image: image,
|
||||
Env: []string{
|
||||
fmt.Sprintf("URIPATH=%s", path),
|
||||
fmt.Sprintf("HOOKPORT=%s", port),
|
||||
fmt.Sprintf("CALLBACKURL=%s", callbackurl),
|
||||
fmt.Sprintf("APIKEY=%s", apikey),
|
||||
fmt.Sprintf("HOOKID=%s", identifier),
|
||||
},
|
||||
ExposedPorts: exposedPorts,
|
||||
Hostname: fmt.Sprintf("%s-%s", image, identifier),
|
||||
}
|
||||
|
||||
cont, err := cli.ContainerCreate(
|
||||
context.Background(),
|
||||
config,
|
||||
hostConfig,
|
||||
networkConfig,
|
||||
fmt.Sprintf("%s-%s", image, identifier),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
cli.ContainerStart(context.Background(), cont.ID, types.ContainerStartOptions{})
|
||||
log.Printf("Container %s is created", cont.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Starts a new webhook
|
||||
func handleStopHookDocker(resp http.ResponseWriter, request *http.Request) {
|
||||
cors := handleCors(resp, request)
|
||||
if cors {
|
||||
return
|
||||
}
|
||||
|
||||
location := strings.Split(request.URL.String(), "/")
|
||||
|
||||
var fileId string
|
||||
if location[1] == "api" {
|
||||
if len(location) <= 4 {
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
fileId = location[4]
|
||||
}
|
||||
|
||||
if len(fileId) != 32 {
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false, "message": "ID not valid"}`))
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
hook, err := getHook(ctx, fileId)
|
||||
if err != nil {
|
||||
log.Printf("Failed getting hook: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Status: %s", hook.Status)
|
||||
log.Printf("Running: %t", hook.Running)
|
||||
if !hook.Running {
|
||||
message := fmt.Sprintf("Error: %s isn't running", hook.Id)
|
||||
log.Println(message)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "message": "%s"}`, message)))
|
||||
return
|
||||
}
|
||||
|
||||
hook.Status = "stopped"
|
||||
hook.Running = false
|
||||
hook.Actions = []HookAction{}
|
||||
err = setHook(ctx, *hook)
|
||||
if err != nil {
|
||||
log.Printf("Failed setting hook: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
image := "webhook"
|
||||
|
||||
// This is here to force stop and remove the old webhook
|
||||
err = stopWebhook(image, fileId)
|
||||
if err != nil {
|
||||
log.Printf("Container stop issue for %s-%s: %s", image, fileId, err)
|
||||
}
|
||||
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(`{"success": true, "message": "Stopped webhook"}`))
|
||||
}
|
||||
|
||||
// THis is an example
|
||||
// Can also be used as base data?
|
||||
var webhook = `{
|
||||
"id": "d6ef8912e8bd37776e654cbc14c2629c",
|
||||
"info": {
|
||||
"url": "http://localhost:5001",
|
||||
"name": "TheHive",
|
||||
"description": "Webhook for TheHive"
|
||||
},
|
||||
"transforms": {},
|
||||
"actions": {},
|
||||
"type": "webhook",
|
||||
"running": false,
|
||||
"status": "stopped"
|
||||
}`
|
||||
|
||||
// Starts a new webhook
|
||||
func handleDeleteHookDocker(resp http.ResponseWriter, request *http.Request) {
|
||||
ctx := context.Background()
|
||||
cors := handleCors(resp, request)
|
||||
if cors {
|
||||
return
|
||||
}
|
||||
|
||||
location := strings.Split(request.URL.String(), "/")
|
||||
|
||||
var fileId string
|
||||
if location[1] == "api" {
|
||||
if len(location) <= 4 {
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
fileId = location[4]
|
||||
}
|
||||
|
||||
if len(fileId) != 32 {
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false, "message": "ID not valid"}`))
|
||||
return
|
||||
}
|
||||
|
||||
err := DeleteKey(ctx, "hooks", fileId)
|
||||
if err != nil {
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false, "message": "Can't delete"}`))
|
||||
return
|
||||
}
|
||||
|
||||
image := "webhook"
|
||||
|
||||
// This is here to force stop and remove the old webhook
|
||||
err = stopWebhook(image, fileId)
|
||||
if err != nil {
|
||||
log.Printf("Container stop issue for %s-%s: %s", image, fileId, err)
|
||||
resp.Write([]byte(`{"success": false, "message": "Couldn't stop webhook"}`))
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(`{"success": true, "message": "Deleted webhook"}`))
|
||||
}
|
||||
|
||||
// Starts a new webhook
|
||||
func handleStartHookDocker(resp http.ResponseWriter, request *http.Request) {
|
||||
cors := handleCors(resp, request)
|
||||
if cors {
|
||||
return
|
||||
}
|
||||
|
||||
location := strings.Split(request.URL.String(), "/")
|
||||
|
||||
var fileId string
|
||||
if location[1] == "api" {
|
||||
if len(location) <= 4 {
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
fileId = location[4]
|
||||
}
|
||||
|
||||
if len(fileId) != 32 {
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false, "message": "ID not valid"}`))
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
hook, err := getHook(ctx, fileId)
|
||||
if err != nil {
|
||||
log.Printf("Failed getting hook: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
if len(hook.Info.Url) == 0 {
|
||||
log.Printf("Hook url can't be empty.")
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Status: %s", hook.Status)
|
||||
log.Printf("Running: %t", hook.Running)
|
||||
if hook.Running || hook.Status == "Running" {
|
||||
message := fmt.Sprintf("Error: %s is already running", hook.Id)
|
||||
log.Println(message)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "message": "%s"}`, message)))
|
||||
return
|
||||
}
|
||||
|
||||
// FIXME - verify?
|
||||
// FIXME - static port? Generate from available range.
|
||||
image := "webhook"
|
||||
filepath := "/webhook"
|
||||
baseUrl := "http://localhost"
|
||||
callbackUrl := "http://localhost:8001"
|
||||
|
||||
// This is here to force stop and remove the old webhook
|
||||
err = stopWebhook(image, fileId)
|
||||
if err != nil {
|
||||
log.Printf("Container stop issue for %s-%s: %s", image, fileId, err)
|
||||
}
|
||||
|
||||
// Dynamic ish ports
|
||||
var startPort int64 = 5001
|
||||
var endPort int64 = 5010
|
||||
port := findAvailablePorts(startPort, endPort)
|
||||
if len(port) == 0 {
|
||||
message := fmt.Sprintf("Not ports available in the range %d-%d", startPort, endPort)
|
||||
log.Println(message)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "message": "%s"}`, message)))
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
hook.Status = "running"
|
||||
hook.Running = true
|
||||
|
||||
// Set this for more than just hooks?
|
||||
if hook.Type == "webhook" {
|
||||
hook.Info.Url = fmt.Sprintf("%s:%s%s", baseUrl, port, filepath)
|
||||
}
|
||||
err = setHook(ctx, *hook)
|
||||
if err != nil {
|
||||
log.Printf("Failed setting hook: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
// Cloud run? Let's make a generic webhook that can be deployed easily
|
||||
log.Printf("Should run a webhook with the following: \nUrl: %s\nId: %s\n", hook.Info.Url, hook.Id)
|
||||
|
||||
// FIXME - set port based on what the user specified / what was generated
|
||||
// FIXME - add nonstatic APIKEY
|
||||
apiKey := "ASD"
|
||||
|
||||
err = deployWebhook(image, fileId, filepath, port, callbackUrl, apiKey)
|
||||
if err != nil {
|
||||
log.Printf("Failed starting container %s-%s: %s", image, fileId, err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
// FIXME - get some real data?
|
||||
log.Printf("Successfully started %s-%s on port %s with filepath %s", image, fileId, port, filepath)
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(`{"success": true, "message": "Started webhook"}`))
|
||||
return
|
||||
}
|
||||
|
||||
func hookTest() {
|
||||
var hook Hook
|
||||
err := json.Unmarshal([]byte(webhook), &hook)
|
||||
log.Println(webhook)
|
||||
if err != nil {
|
||||
log.Printf("Failed hook unmarshaling: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
err = setHook(ctx, hook)
|
||||
if err != nil {
|
||||
log.Printf("Failed setting hook: %s", err)
|
||||
}
|
||||
|
||||
returnHook, err := getHook(ctx, hook.Id)
|
||||
if err != nil {
|
||||
log.Printf("Failed getting hook: %s", err)
|
||||
}
|
||||
|
||||
if len(returnHook.Id) > 0 {
|
||||
log.Printf("Success! - %s", returnHook.Id)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
# Base our app image off of the WALKOFF App SDK image
|
||||
FROM frikky/shuffle:app_sdk as base
|
||||
|
||||
# We're going to stage away all of the bloat from the build tools so lets create a builder stage
|
||||
FROM base as builder
|
||||
|
||||
# Install all alpine build tools needed for our pip installs
|
||||
RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev
|
||||
|
||||
# Install all of our pip packages in a single directory that we can copy to our base image later
|
||||
RUN mkdir /install
|
||||
WORKDIR /install
|
||||
COPY requirements.txt /requirements.txt
|
||||
RUN pip install --prefix="/install" -r /requirements.txt
|
||||
|
||||
# Switch back to our base image and copy in all of our built packages and source code
|
||||
FROM base
|
||||
COPY --from=builder /install /usr/local
|
||||
COPY src /app
|
||||
|
||||
# Install any binary dependencies needed in our final image - this can be a lot of different stuff
|
||||
RUN apk --no-cache add --update libmagic
|
||||
|
||||
# Finally, lets run our app!
|
||||
WORKDIR /app
|
||||
CMD python app.py --log-level DEBUG
|
||||
@@ -0,0 +1,3 @@
|
||||
# No extra requirements needed
|
||||
requests
|
||||
urllib3
|
||||
@@ -0,0 +1,26 @@
|
||||
# Base our app image off of the WALKOFF App SDK image
|
||||
FROM frikky/shuffle:app_sdk as base
|
||||
|
||||
# We're going to stage away all of the bloat from the build tools so lets create a builder stage
|
||||
FROM base as builder
|
||||
|
||||
# Install all alpine build tools needed for our pip installs
|
||||
RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev
|
||||
|
||||
# Install all of our pip packages in a single directory that we can copy to our base image later
|
||||
RUN mkdir /install
|
||||
WORKDIR /install
|
||||
COPY requirements.txt /requirements.txt
|
||||
RUN pip install --prefix="/install" -r /requirements.txt
|
||||
|
||||
# Switch back to our base image and copy in all of our built packages and source code
|
||||
FROM base
|
||||
COPY --from=builder /install /usr/local
|
||||
COPY src /app
|
||||
|
||||
# Install any binary dependencies needed in our final image - this can be a lot of different stuff
|
||||
RUN apk --no-cache add --update libmagic
|
||||
|
||||
# Finally, lets run our app!
|
||||
WORKDIR /app
|
||||
CMD python app.py --log-level DEBUG
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
# No extra requirements needed
|
||||
requests
|
||||
urllib3
|
||||
@@ -0,0 +1,29 @@
|
||||
import requests
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from walkoff_app_sdk.app_base import AppBase
|
||||
|
||||
class Asd37fff3ea5fa10cdde521f21134320c26(AppBase):
|
||||
"""
|
||||
Autogenerated class by Shuffler
|
||||
"""
|
||||
|
||||
__version__ = "1.0"
|
||||
app_name = "Asd37fff3ea5fa10cdde521f21134320c26"
|
||||
|
||||
def __init__(self, redis, logger, console_logger=None):
|
||||
self.verify = False
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
super().__init__(redis, logger, console_logger)
|
||||
|
||||
async def get_hello(self, apikey):
|
||||
headers={}
|
||||
url=f"https://google.com/lol"
|
||||
headers["what"] = apikey
|
||||
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(Asd37fff3ea5fa10cdde521f21134320c26.run(), debug=True)
|
||||
@@ -0,0 +1,26 @@
|
||||
# Base our app image off of the WALKOFF App SDK image
|
||||
FROM frikky/shuffle:app_sdk as base
|
||||
|
||||
# We're going to stage away all of the bloat from the build tools so lets create a builder stage
|
||||
FROM base as builder
|
||||
|
||||
# Install all alpine build tools needed for our pip installs
|
||||
RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev
|
||||
|
||||
# Install all of our pip packages in a single directory that we can copy to our base image later
|
||||
RUN mkdir /install
|
||||
WORKDIR /install
|
||||
COPY requirements.txt /requirements.txt
|
||||
RUN pip install --prefix="/install" -r /requirements.txt
|
||||
|
||||
# Switch back to our base image and copy in all of our built packages and source code
|
||||
FROM base
|
||||
COPY --from=builder /install /usr/local
|
||||
COPY src /app
|
||||
|
||||
# Install any binary dependencies needed in our final image - this can be a lot of different stuff
|
||||
RUN apk --no-cache add --update libmagic
|
||||
|
||||
# Finally, lets run our app!
|
||||
WORKDIR /app
|
||||
CMD python app.py --log-level DEBUG
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
name: Uber API
|
||||
is_valid: true
|
||||
id: 6a707aa0-892b-42a0-b0a4-b10b6b56e6e5
|
||||
link: https://api.uber.com
|
||||
app_version: 1.0.0
|
||||
generated: true
|
||||
sharing: false
|
||||
verified: false
|
||||
tested: false
|
||||
owner: 2501f368-edf2-4fee-bb7e-457d65d7410c
|
||||
private_id: 547f1803-edf7-433f-8016-d508ed978f65
|
||||
description: Move your app forward with the Uber API
|
||||
environment: cloud
|
||||
smallimage: ""
|
||||
large_image: ""
|
||||
contact_info:
|
||||
name: ""
|
||||
url: ""
|
||||
actions:
|
||||
- description: The Price Estimates endpoint returns an estimated price range for each
|
||||
product offered at a given location. The price estimate is provided as a formatted
|
||||
string with the full price range and the localized currency symbol.<br><br>The
|
||||
response also includes low and high estimates, and the [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217)
|
||||
currency code for situations requiring currency conversion. When surge is active
|
||||
for a particular product, its surge_multiplier will be greater than 1, but the
|
||||
price estimate already factors in this multiplier.
|
||||
name: get_price_estimates
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters:
|
||||
- description: Latitude component of start location.
|
||||
name: start_latitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Longitude component of start location.
|
||||
name: start_longitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Latitude component of end location.
|
||||
name: end_latitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Longitude component of end location.
|
||||
name: end_longitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
- description: The Time Estimates endpoint returns ETAs for all products offered at
|
||||
a given location, with the responses expressed as integers in seconds. We recommend
|
||||
that this endpoint be called every minute to provide the most accurate, up-to-date
|
||||
ETAs.
|
||||
name: get_time_estimates
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters:
|
||||
- description: Latitude component of start location.
|
||||
name: start_latitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Longitude component of start location.
|
||||
name: start_longitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Unique customer identifier to be used for experience customization.
|
||||
name: customer_uuid
|
||||
example: ""
|
||||
multiline: false
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- description: Unique identifier representing a specific product for a given latitude
|
||||
& longitude.
|
||||
name: product_id
|
||||
example: ""
|
||||
multiline: false
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
- description: The User Activity endpoint returns data about a user's lifetime activity
|
||||
with Uber. The response will include pickup locations and times, dropoff locations
|
||||
and times, the distance of past requests, and information about which products
|
||||
were requested.<br><br>The history array in the response will have a maximum length
|
||||
based on the limit parameter. The response value count may exceed limit, therefore
|
||||
subsequent API requests may be necessary.
|
||||
name: get_user_activity
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters:
|
||||
- description: Offset the list of returned results by this amount. Default is zero.
|
||||
name: offset
|
||||
example: ""
|
||||
multiline: false
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- description: Number of items to retrieve. Default is 5, maximum is 100.
|
||||
name: limit
|
||||
example: ""
|
||||
multiline: false
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
- description: The User Profile endpoint returns information about the Uber user that
|
||||
has authorized with the application.
|
||||
name: get_user_profile
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters: []
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
- description: The Products endpoint returns information about the Uber products offered
|
||||
at a given location. The response includes the display name and other details
|
||||
about each product, and lists the products in the proper display order.
|
||||
name: get_product_types
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters:
|
||||
- description: Latitude component of location.
|
||||
name: latitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Longitude component of location.
|
||||
name: longitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
authentication:
|
||||
required: true
|
||||
parameters:
|
||||
- description: ""
|
||||
id: ""
|
||||
name: ""
|
||||
example: ""
|
||||
value: ""
|
||||
multiline: false
|
||||
required: true
|
||||
in: ""
|
||||
scheme: ""
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# No extra requirements needed
|
||||
requests
|
||||
urllib3
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import requests
|
||||
import asyncio
|
||||
import json
|
||||
import urllib3
|
||||
|
||||
from walkoff_app_sdk.app_base import AppBase
|
||||
|
||||
class UberAPI547f1803edf7433f8016d508ed978f65(AppBase):
|
||||
"""
|
||||
Autogenerated class by Shuffler
|
||||
"""
|
||||
|
||||
__version__ = "1.0"
|
||||
app_name = "UberAPI547f1803edf7433f8016d508ed978f65"
|
||||
|
||||
def __init__(self, redis, logger, console_logger=None):
|
||||
self.verify = False
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
super().__init__(redis, logger, console_logger)
|
||||
|
||||
async def get_price_estimates(self, start_latitude, start_longitude, end_latitude, end_longitude):
|
||||
headers={}
|
||||
url=f"https://api.uber.com/estimates/price?start_latitude={start_latitude}&start_longitude={start_longitude}&end_latitude={end_latitude}&end_longitude={end_longitude}"
|
||||
|
||||
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
async def get_time_estimates(self, start_latitude, start_longitude, customer_uuid="", product_id="", ):
|
||||
headers={}
|
||||
url=f"https://api.uber.com/estimates/time?start_latitude={start_latitude}&start_longitude={start_longitude}"
|
||||
|
||||
|
||||
if customer_uuid:
|
||||
url += f"&customer_uuid={customer_uuid}"
|
||||
if product_id:
|
||||
url += f"&product_id={product_id}"
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
async def get_user_activity(self, offset="", limit="", ):
|
||||
headers={}
|
||||
url=f"https://api.uber.com/history"
|
||||
|
||||
|
||||
if offset:
|
||||
url += f"&offset={offset}"
|
||||
if limit:
|
||||
url += f"&limit={limit}"
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
async def get_user_profile(self):
|
||||
headers={}
|
||||
url=f"https://api.uber.com/me"
|
||||
|
||||
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
async def get_product_types(self, latitude, longitude):
|
||||
headers={}
|
||||
url=f"https://api.uber.com/products?latitude={latitude}&longitude={longitude}"
|
||||
|
||||
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(UberAPI547f1803edf7433f8016d508ed978f65.run(), debug=True)
|
||||
@@ -0,0 +1,26 @@
|
||||
# Base our app image off of the WALKOFF App SDK image
|
||||
FROM frikky/shuffle:app_sdk as base
|
||||
|
||||
# We're going to stage away all of the bloat from the build tools so lets create a builder stage
|
||||
FROM base as builder
|
||||
|
||||
# Install all alpine build tools needed for our pip installs
|
||||
RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev
|
||||
|
||||
# Install all of our pip packages in a single directory that we can copy to our base image later
|
||||
RUN mkdir /install
|
||||
WORKDIR /install
|
||||
COPY requirements.txt /requirements.txt
|
||||
RUN pip install --prefix="/install" -r /requirements.txt
|
||||
|
||||
# Switch back to our base image and copy in all of our built packages and source code
|
||||
FROM base
|
||||
COPY --from=builder /install /usr/local
|
||||
COPY src /app
|
||||
|
||||
# Install any binary dependencies needed in our final image - this can be a lot of different stuff
|
||||
RUN apk --no-cache add --update libmagic
|
||||
|
||||
# Finally, lets run our app!
|
||||
WORKDIR /app
|
||||
CMD python app.py --log-level DEBUG
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
name: Uber API
|
||||
is_valid: true
|
||||
id: 547f1803-edf7-433f-8016-d508ed978f65
|
||||
link: https://api.uber.com
|
||||
app_version: 1.0.0
|
||||
generated: true
|
||||
sharing: false
|
||||
verified: false
|
||||
tested: false
|
||||
owner: 2501f368-edf2-4fee-bb7e-457d65d7410c
|
||||
private_id: 795ed46f-2dbd-43d6-9d0c-1f6b9cce14ea
|
||||
description: Move your app forward with the Uber API
|
||||
environment: cloud
|
||||
smallimage: ""
|
||||
large_image: ""
|
||||
contact_info:
|
||||
name: ""
|
||||
url: ""
|
||||
actions:
|
||||
- description: The Price Estimates endpoint returns an estimated price range for each
|
||||
product offered at a given location. The price estimate is provided as a formatted
|
||||
string with the full price range and the localized currency symbol.<br><br>The
|
||||
response also includes low and high estimates, and the [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217)
|
||||
currency code for situations requiring currency conversion. When surge is active
|
||||
for a particular product, its surge_multiplier will be greater than 1, but the
|
||||
price estimate already factors in this multiplier.
|
||||
name: get_price_estimates
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters:
|
||||
- description: Latitude component of start location.
|
||||
name: start_latitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Longitude component of start location.
|
||||
name: start_longitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Latitude component of end location.
|
||||
name: end_latitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Longitude component of end location.
|
||||
name: end_longitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
- description: The Time Estimates endpoint returns ETAs for all products offered at
|
||||
a given location, with the responses expressed as integers in seconds. We recommend
|
||||
that this endpoint be called every minute to provide the most accurate, up-to-date
|
||||
ETAs.
|
||||
name: get_time_estimates
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters:
|
||||
- description: Latitude component of start location.
|
||||
name: start_latitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Longitude component of start location.
|
||||
name: start_longitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Unique customer identifier to be used for experience customization.
|
||||
name: customer_uuid
|
||||
example: ""
|
||||
multiline: false
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- description: Unique identifier representing a specific product for a given latitude
|
||||
& longitude.
|
||||
name: product_id
|
||||
example: ""
|
||||
multiline: false
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
- description: The User Activity endpoint returns data about a user's lifetime activity
|
||||
with Uber. The response will include pickup locations and times, dropoff locations
|
||||
and times, the distance of past requests, and information about which products
|
||||
were requested.<br><br>The history array in the response will have a maximum length
|
||||
based on the limit parameter. The response value count may exceed limit, therefore
|
||||
subsequent API requests may be necessary.
|
||||
name: get_user_activity
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters:
|
||||
- description: Offset the list of returned results by this amount. Default is zero.
|
||||
name: offset
|
||||
example: ""
|
||||
multiline: false
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- description: Number of items to retrieve. Default is 5, maximum is 100.
|
||||
name: limit
|
||||
example: ""
|
||||
multiline: false
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
- description: The User Profile endpoint returns information about the Uber user that
|
||||
has authorized with the application.
|
||||
name: get_user_profile
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters: []
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
- description: The Products endpoint returns information about the Uber products offered
|
||||
at a given location. The response includes the display name and other details
|
||||
about each product, and lists the products in the proper display order.
|
||||
name: get_product_types
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters:
|
||||
- description: Latitude component of location.
|
||||
name: latitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Longitude component of location.
|
||||
name: longitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
authentication:
|
||||
required: true
|
||||
parameters:
|
||||
- description: ""
|
||||
id: ""
|
||||
name: ""
|
||||
example: ""
|
||||
value: ""
|
||||
multiline: false
|
||||
required: true
|
||||
in: ""
|
||||
scheme: ""
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# No extra requirements needed
|
||||
requests
|
||||
urllib3
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import requests
|
||||
import asyncio
|
||||
import json
|
||||
import urllib3
|
||||
|
||||
from walkoff_app_sdk.app_base import AppBase
|
||||
|
||||
class UberAPI795ed46f2dbd43d69d0c1f6b9cce14ea(AppBase):
|
||||
"""
|
||||
Autogenerated class by Shuffler
|
||||
"""
|
||||
|
||||
__version__ = "1.0"
|
||||
app_name = "UberAPI795ed46f2dbd43d69d0c1f6b9cce14ea"
|
||||
|
||||
def __init__(self, redis, logger, console_logger=None):
|
||||
self.verify = False
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
super().__init__(redis, logger, console_logger)
|
||||
|
||||
async def get_price_estimates(self, start_latitude, start_longitude, end_latitude, end_longitude):
|
||||
headers={}
|
||||
url=f"https://api.uber.com/estimates/price?start_latitude={start_latitude}&start_longitude={start_longitude}&end_latitude={end_latitude}&end_longitude={end_longitude}"
|
||||
|
||||
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
async def get_time_estimates(self, start_latitude, start_longitude, customer_uuid="", product_id="", ):
|
||||
headers={}
|
||||
url=f"https://api.uber.com/estimates/time?start_latitude={start_latitude}&start_longitude={start_longitude}"
|
||||
|
||||
|
||||
if customer_uuid:
|
||||
url += f"&customer_uuid={customer_uuid}"
|
||||
if product_id:
|
||||
url += f"&product_id={product_id}"
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
async def get_user_activity(self, offset="", limit="", ):
|
||||
headers={}
|
||||
url=f"https://api.uber.com/history"
|
||||
|
||||
|
||||
if offset:
|
||||
url += f"&offset={offset}"
|
||||
if limit:
|
||||
url += f"&limit={limit}"
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
async def get_user_profile(self):
|
||||
headers={}
|
||||
url=f"https://api.uber.com/me"
|
||||
|
||||
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
async def get_product_types(self, latitude, longitude):
|
||||
headers={}
|
||||
url=f"https://api.uber.com/products?latitude={latitude}&longitude={longitude}"
|
||||
|
||||
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(UberAPI795ed46f2dbd43d69d0c1f6b9cce14ea.run(), debug=True)
|
||||
@@ -0,0 +1,26 @@
|
||||
# Base our app image off of the WALKOFF App SDK image
|
||||
FROM frikky/shuffle:app_sdk as base
|
||||
|
||||
# We're going to stage away all of the bloat from the build tools so lets create a builder stage
|
||||
FROM base as builder
|
||||
|
||||
# Install all alpine build tools needed for our pip installs
|
||||
RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev
|
||||
|
||||
# Install all of our pip packages in a single directory that we can copy to our base image later
|
||||
RUN mkdir /install
|
||||
WORKDIR /install
|
||||
COPY requirements.txt /requirements.txt
|
||||
RUN pip install --prefix="/install" -r /requirements.txt
|
||||
|
||||
# Switch back to our base image and copy in all of our built packages and source code
|
||||
FROM base
|
||||
COPY --from=builder /install /usr/local
|
||||
COPY src /app
|
||||
|
||||
# Install any binary dependencies needed in our final image - this can be a lot of different stuff
|
||||
RUN apk --no-cache add --update libmagic
|
||||
|
||||
# Finally, lets run our app!
|
||||
WORKDIR /app
|
||||
CMD python app.py --log-level DEBUG
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
name: Uber API
|
||||
is_valid: true
|
||||
id: 795ed46f-2dbd-43d6-9d0c-1f6b9cce14ea
|
||||
link: ""
|
||||
app_version: 1.0.0
|
||||
generated: true
|
||||
sharing: false
|
||||
verified: false
|
||||
tested: false
|
||||
owner: 2501f368-edf2-4fee-bb7e-457d65d7410c
|
||||
private_id: f97a44dd-f1c3-48ae-b46f-ceb19ce3039c
|
||||
description: Move your app forward with the Uber API
|
||||
environment: cloud
|
||||
smallimage: ""
|
||||
large_image: ""
|
||||
contact_info:
|
||||
name: ""
|
||||
url: ""
|
||||
actions:
|
||||
- description: The Time Estimates endpoint returns ETAs for all products offered at
|
||||
a given location, with the responses expressed as integers in seconds. We recommend
|
||||
that this endpoint be called every minute to provide the most accurate, up-to-date
|
||||
ETAs.
|
||||
name: get_time_estimates
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters:
|
||||
- description: The URL of the app
|
||||
name: url
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Latitude component of start location.
|
||||
name: start_latitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Longitude component of start location.
|
||||
name: start_longitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Unique customer identifier to be used for experience customization.
|
||||
name: customer_uuid
|
||||
example: ""
|
||||
multiline: false
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- description: Unique identifier representing a specific product for a given latitude
|
||||
& longitude.
|
||||
name: product_id
|
||||
example: ""
|
||||
multiline: false
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
- description: The User Activity endpoint returns data about a user's lifetime activity
|
||||
with Uber. The response will include pickup locations and times, dropoff locations
|
||||
and times, the distance of past requests, and information about which products
|
||||
were requested.<br><br>The history array in the response will have a maximum length
|
||||
based on the limit parameter. The response value count may exceed limit, therefore
|
||||
subsequent API requests may be necessary.
|
||||
name: get_user_activity
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters:
|
||||
- description: The URL of the app
|
||||
name: url
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Offset the list of returned results by this amount. Default is zero.
|
||||
name: offset
|
||||
example: ""
|
||||
multiline: false
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- description: Number of items to retrieve. Default is 5, maximum is 100.
|
||||
name: limit
|
||||
example: ""
|
||||
multiline: false
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
- description: The User Profile endpoint returns information about the Uber user that
|
||||
has authorized with the application.
|
||||
name: get_user_profile
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters:
|
||||
- description: The URL of the app
|
||||
name: url
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
- description: The Products endpoint returns information about the Uber products offered
|
||||
at a given location. The response includes the display name and other details
|
||||
about each product, and lists the products in the proper display order.
|
||||
name: get_product_types
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters:
|
||||
- description: The URL of the app
|
||||
name: url
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Latitude component of location.
|
||||
name: latitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Longitude component of location.
|
||||
name: longitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
- description: The Price Estimates endpoint returns an estimated price range for each
|
||||
product offered at a given location. The price estimate is provided as a formatted
|
||||
string with the full price range and the localized currency symbol.<br><br>The
|
||||
response also includes low and high estimates, and the [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217)
|
||||
currency code for situations requiring currency conversion. When surge is active
|
||||
for a particular product, its surge_multiplier will be greater than 1, but the
|
||||
price estimate already factors in this multiplier.
|
||||
name: get_price_estimates
|
||||
nodetype: action
|
||||
environment: cloud
|
||||
sharing: false
|
||||
privateid: ""
|
||||
appid: ""
|
||||
tested: false
|
||||
parameters:
|
||||
- description: The URL of the app
|
||||
name: url
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Latitude component of start location.
|
||||
name: start_latitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Longitude component of start location.
|
||||
name: start_longitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Latitude component of end location.
|
||||
name: end_latitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: Longitude component of end location.
|
||||
name: end_longitude
|
||||
example: ""
|
||||
multiline: false
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
returns:
|
||||
schema:
|
||||
type: string
|
||||
authentication:
|
||||
required: true
|
||||
parameters:
|
||||
- description: ""
|
||||
id: ""
|
||||
name: ""
|
||||
example: ""
|
||||
value: ""
|
||||
multiline: false
|
||||
required: true
|
||||
in: ""
|
||||
scheme: ""
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# No extra requirements needed
|
||||
requests
|
||||
urllib3
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import requests
|
||||
import asyncio
|
||||
import json
|
||||
import urllib3
|
||||
|
||||
from walkoff_app_sdk.app_base import AppBase
|
||||
|
||||
class UberAPIf97a44ddf1c348aeb46fceb19ce3039c(AppBase):
|
||||
"""
|
||||
Autogenerated class by Shuffler
|
||||
"""
|
||||
|
||||
__version__ = "1.0"
|
||||
app_name = "UberAPIf97a44ddf1c348aeb46fceb19ce3039c"
|
||||
|
||||
def __init__(self, redis, logger, console_logger=None):
|
||||
self.verify = False
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
super().__init__(redis, logger, console_logger)
|
||||
|
||||
async def get_time_estimates(self, baseurl, start_latitude, start_longitude, customer_uuid="", product_id="", ):
|
||||
headers={}
|
||||
url=f"{baseurl}/estimates/time?start_latitude={start_latitude}&start_longitude={start_longitude}"
|
||||
|
||||
|
||||
if customer_uuid:
|
||||
url += f"&customer_uuid={customer_uuid}"
|
||||
if product_id:
|
||||
url += f"&product_id={product_id}"
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
async def get_user_activity(self, baseurl, offset="", limit="", ):
|
||||
headers={}
|
||||
url=f"{baseurl}/history"
|
||||
|
||||
|
||||
if offset:
|
||||
url += f"&offset={offset}"
|
||||
if limit:
|
||||
url += f"&limit={limit}"
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
async def get_user_profile(self, baseurl):
|
||||
headers={}
|
||||
url=f"{baseurl}/me"
|
||||
|
||||
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
async def get_product_types(self, baseurl, latitude, longitude):
|
||||
headers={}
|
||||
url=f"{baseurl}/products?latitude={latitude}&longitude={longitude}"
|
||||
|
||||
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
async def get_price_estimates(self, baseurl, start_latitude, start_longitude, end_latitude, end_longitude):
|
||||
headers={}
|
||||
url=f"{baseurl}/estimates/price?start_latitude={start_latitude}&start_longitude={start_longitude}&end_latitude={end_latitude}&end_longitude={end_longitude}"
|
||||
|
||||
|
||||
return requests.get(url, headers=headers).text
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(UberAPIf97a44ddf1c348aeb46fceb19ce3039c.run(), debug=True)
|
||||
@@ -0,0 +1,26 @@
|
||||
# Base our app image off of the WALKOFF App SDK image
|
||||
FROM frikky/shuffle:app_sdk as base
|
||||
|
||||
# We're going to stage away all of the bloat from the build tools so lets create a builder stage
|
||||
FROM base as builder
|
||||
|
||||
# Install all alpine build tools needed for our pip installs
|
||||
RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev
|
||||
|
||||
# Install all of our pip packages in a single directory that we can copy to our base image later
|
||||
RUN mkdir /install
|
||||
WORKDIR /install
|
||||
COPY requirements.txt /requirements.txt
|
||||
RUN pip install --prefix="/install" -r /requirements.txt
|
||||
|
||||
# Switch back to our base image and copy in all of our built packages and source code
|
||||
FROM base
|
||||
COPY --from=builder /install /usr/local
|
||||
COPY src /app
|
||||
|
||||
# Install any binary dependencies needed in our final image - this can be a lot of different stuff
|
||||
RUN apk --no-cache add --update libmagic
|
||||
|
||||
# Finally, lets run our app!
|
||||
WORKDIR /app
|
||||
CMD python app.py --log-level DEBUG
|
||||
@@ -0,0 +1,31 @@
|
||||
name: a
|
||||
is_valid: true
|
||||
id: 31e2332c-8baa-41e9-98af-c2457d64d946
|
||||
link: ""
|
||||
app_version: 1.0.0
|
||||
generated: true
|
||||
sharing: false
|
||||
verified: false
|
||||
tested: false
|
||||
owner: c2dbe917-f982-40ed-9045-462872c4ca1e
|
||||
private_id: 757f74fead843a8d9b5a954d2fadfa69
|
||||
description: ""
|
||||
environment: cloud
|
||||
smallimage: ""
|
||||
large_image: ""
|
||||
contact_info:
|
||||
name: ""
|
||||
url: ""
|
||||
actions: []
|
||||
authentication:
|
||||
required: true
|
||||
parameters:
|
||||
- description: ""
|
||||
id: ""
|
||||
name: ""
|
||||
example: ""
|
||||
value: ""
|
||||
multiline: false
|
||||
required: true
|
||||
in: ""
|
||||
scheme: ""
|
||||
@@ -0,0 +1,3 @@
|
||||
# No extra requirements needed
|
||||
requests
|
||||
urllib3
|
||||
@@ -0,0 +1,23 @@
|
||||
import requests
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from walkoff_app_sdk.app_base import AppBase
|
||||
|
||||
class a757f74fead843a8d9b5a954d2fadfa69(AppBase):
|
||||
"""
|
||||
Autogenerated class by Shuffler
|
||||
"""
|
||||
|
||||
__version__ = "1.0"
|
||||
app_name = "a757f74fead843a8d9b5a954d2fadfa69"
|
||||
|
||||
def __init__(self, redis, logger, console_logger=None):
|
||||
self.verify = False
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
super().__init__(redis, logger, console_logger)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(a757f74fead843a8d9b5a954d2fadfa69.run(), debug=True)
|
||||
@@ -0,0 +1,26 @@
|
||||
# Base our app image off of the WALKOFF App SDK image
|
||||
FROM frikky/shuffle:app_sdk as base
|
||||
|
||||
# We're going to stage away all of the bloat from the build tools so lets create a builder stage
|
||||
FROM base as builder
|
||||
|
||||
# Install all alpine build tools needed for our pip installs
|
||||
RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev
|
||||
|
||||
# Install all of our pip packages in a single directory that we can copy to our base image later
|
||||
RUN mkdir /install
|
||||
WORKDIR /install
|
||||
COPY requirements.txt /requirements.txt
|
||||
RUN pip install --prefix="/install" -r /requirements.txt
|
||||
|
||||
# Switch back to our base image and copy in all of our built packages and source code
|
||||
FROM base
|
||||
COPY --from=builder /install /usr/local
|
||||
COPY src /app
|
||||
|
||||
# Install any binary dependencies needed in our final image - this can be a lot of different stuff
|
||||
RUN apk --no-cache add --update libmagic
|
||||
|
||||
# Finally, lets run our app!
|
||||
WORKDIR /app
|
||||
CMD python app.py --log-level DEBUG
|
||||
@@ -0,0 +1,31 @@
|
||||
name: test
|
||||
is_valid: true
|
||||
id: a29d4143-fa3b-4456-86c4-fee2d402ddf6
|
||||
link: https://test.com
|
||||
app_version: 1.0.0
|
||||
generated: true
|
||||
sharing: false
|
||||
verified: false
|
||||
tested: false
|
||||
owner: c2dbe917-f982-40ed-9045-462872c4ca1e
|
||||
private_id: f383aa4f34d36802daf465e2af5f7aa1
|
||||
description: test
|
||||
environment: cloud
|
||||
smallimage: ""
|
||||
large_image: ""
|
||||
contact_info:
|
||||
name: ""
|
||||
url: ""
|
||||
actions: []
|
||||
authentication:
|
||||
required: true
|
||||
parameters:
|
||||
- description: ""
|
||||
id: ""
|
||||
name: ""
|
||||
example: ""
|
||||
value: ""
|
||||
multiline: false
|
||||
required: true
|
||||
in: ""
|
||||
scheme: ""
|
||||
@@ -0,0 +1,3 @@
|
||||
# No extra requirements needed
|
||||
requests
|
||||
urllib3
|
||||
@@ -0,0 +1,23 @@
|
||||
import requests
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from walkoff_app_sdk.app_base import AppBase
|
||||
|
||||
class testf383aa4f34d36802daf465e2af5f7aa1(AppBase):
|
||||
"""
|
||||
Autogenerated class by Shuffler
|
||||
"""
|
||||
|
||||
__version__ = "1.0"
|
||||
app_name = "testf383aa4f34d36802daf465e2af5f7aa1"
|
||||
|
||||
def __init__(self, redis, logger, console_logger=None):
|
||||
self.verify = False
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
super().__init__(redis, logger, console_logger)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(testf383aa4f34d36802daf465e2af5f7aa1.run(), debug=True)
|
||||
@@ -0,0 +1,31 @@
|
||||
module shuffle
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.57.0
|
||||
cloud.google.com/go/datastore v1.1.0
|
||||
cloud.google.com/go/pubsub v1.3.1
|
||||
cloud.google.com/go/storage v1.7.0
|
||||
github.com/basgys/goxml2json v1.1.0
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker v1.13.1
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/getkin/kin-openapi v0.8.0
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/go-git/go-billy/v5 v5.0.0
|
||||
github.com/go-git/go-git/v5 v5.0.0
|
||||
github.com/google/go-github/v28 v28.1.1
|
||||
github.com/gorilla/mux v1.7.4
|
||||
github.com/h2non/filetype v1.0.12
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
google.golang.org/api v0.23.0
|
||||
google.golang.org/appengine v1.6.6
|
||||
google.golang.org/genproto v0.0.0-20200507105951-43844f6eee31
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
gopkg.in/yaml.v3 v3.0.0-20200506231410-2ff61e1afc86
|
||||
)
|
||||
@@ -0,0 +1,400 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0 h1:EpMNVUorLiZIELdMZbCYX/ByTFCdoYopYAGxaGVz9ms=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.6.0/go.mod h1:hyFDG0qSGdHNz8Q6nDN8rYIkld0q/+5uBZaelxiDLfE=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.7.0 h1:DzdLPI8Em+DEk7IzA2a10ivq3mxIEASC9GeNJ6FFt5Q=
|
||||
cloud.google.com/go/storage v1.7.0/go.mod h1:jGMIBwF+L/tL6WN/W5InNgYYu4HP0DvGB6rQ1mufWfs=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/basgys/goxml2json v1.1.0 h1:4ln5i4rseYfXNd86lGEB+Vi652IsIXIvggKM/BhUKVw=
|
||||
github.com/basgys/goxml2json v1.1.0/go.mod h1:wH7a5Np/Q4QoECFIU8zTQlZwZkrilY0itPfecMw41Dw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
|
||||
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/getkin/kin-openapi v0.8.0 h1:a6TQjTqwkyscC4/hShJX7WhCVE+4bi9lzw61XHQW5hE=
|
||||
github.com/getkin/kin-openapi v0.8.0/go.mod h1:zZQMFkVgRHCdhgb6ihCTIo9dyDZFvX0k/xAKqw1FhPw=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/go-billy v4.2.0+incompatible h1:Z6QtVXd5tjxUtcODLugkJg4WaZnGg13CD8qB9pr+7q0=
|
||||
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
|
||||
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-git v4.7.0+incompatible h1:+W9rgGY4DOKKdX2x6HxSR7HNeTxqiKrOvKnuittYVdA=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
||||
github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
|
||||
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
|
||||
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/h2non/filetype v1.0.12 h1:yHCsIe0y2cvbDARtJhGBTD2ecvqMSTvlIcph9En/Zao=
|
||||
github.com/h2non/filetype v1.0.12/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88=
|
||||
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5 h1:WQ8q63x+f/zpC8Ac1s9wLElVoHhm32p6tudrU72n1QA=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e h1:hq86ru83GdWTlfQFZGO4nZJTU4Bs2wfHl8oFHRaXsfc=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200409170454-77362c5149f0/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.21.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.23.0 h1:YlvGEOq2NA2my8cZ/9V8BcEO9okD48FlJcdqN0xJL3s=
|
||||
google.golang.org/api v0.23.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200409111301-baae70f3302d/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200507105951-43844f6eee31 h1:Bz1qTn2YRWV+9OKJtxHJiQKCiXIdf+kwuKXdt9cBxyU=
|
||||
google.golang.org/genproto v0.0.0-20200507105951-43844f6eee31/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200506231410-2ff61e1afc86 h1:OfFoIUYv/me30yv7XlMy4F9RJw8DEm8WQ6QG1Ph4bH0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200506231410-2ff61e1afc86/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Executable
BIN
Binary file not shown.
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
# docker stop nginx
|
||||
# docker rm nginx
|
||||
# docker rmi nginx
|
||||
#
|
||||
# echo "Running build for website"
|
||||
# sudo npm run build
|
||||
# docker build . -t nginx
|
||||
|
||||
echo "Starting server"
|
||||
docker run -it \
|
||||
-p 5001:5001 \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
--env DATASTORE_EMULATOR_HOST=192.168.3.6:8000 \
|
||||
frikky/shuffle:backend
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
curl http://localhost:5001/api/v1/execution_cleanup -H "Authorization: Bearer e08c6f22-9a55-4557-b008-04388cc51fb0"
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
curl -XPOST http://localhost:5001/api/v1/workflows/1d9d8ce2-566e-4c3f-8a37-5d6c7d2000b5/execute -d '{"execution_argument":""}' -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26"
|
||||
|
||||
|
||||
curl -XPOST http://localhost:5001/api/v1/workflows/1d9d8ce2-566e-4c3f-8a37-5d6c7d2000b5/execute -d '{"execution_argument":""}' -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYwZjQwNjBlNThkNzVmZDNmNzBiZWZmODhjNzk0YTc3NTMyN2FhMzEiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL3NodWZmbGVyLmlvL2FwaS92MS93b3JrZmxvd3MvMWQ5ZDhjZTItNTY2ZS00YzNmLThhMzctNWQ2YzdkMjAwMGI1L2V4ZWN1dGUiLCJhenAiOiIxMDMwNzY3ODIwNjE0MjQ2MTg0MjIiLCJlbWFpbCI6InNjaGVkdWxlckBzaHVmZmxlLTI0MTUxNy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJleHAiOjE1NjU1Mjc1NTEsImlhdCI6MTU2NTUyMzk1MSwiaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTAzMDc2NzgyMDYxNDI0NjE4NDIyIn0.r0EDq9fjhf_5CPTiltyfk_L3uYJp577Uy0yYPcCAl2nv50_z_oUtbWGBpQLL8gcj-NGd3g4E52Qur8k6hCMIQweLS6WAb1279vGffEoCNDfkWb3Oy-yJGP1kzwLvqFJqnHLkSWYXNWvSyWnEimW8Rryx_m1BXS5wcA8l4NIr83kS7fPZrTwjnwFSeGSThwk91DVARzapQb8r0GEgOUyHZ1aBXnV98mikzSUt-5xFKe9eMdD22YJAj0Ru-DxAxs5nOqghX4PMRysWjshjOMrlR1piPWxqAmewp8YKZDCQ5gXskpeAFBDoULT971Wsx_NCohnJsFqx1JfPS9ZYMTW2oQ"
|
||||
@@ -0,0 +1,24 @@
|
||||
#curl localhost:5000/api/v1/hooks
|
||||
|
||||
# Starts a webhook
|
||||
# curl localhost:5000/api/v1/hooks/d6ef8912e8bd37776e654cbc14c2629c/start
|
||||
|
||||
# Runs a request towards the webhook created
|
||||
#curl -XPOST localhost:5002/webhook -d '{"helo": "hi"}'
|
||||
|
||||
# Gets the ID of a new hook
|
||||
|
||||
#curl -X PUT localhost:5001/api/v1/hooks/e6f77059e6d469a6c4c314cc06d5a4c0 -d '{"name": "asd", "description": "hola", "type": "webhook", "id": "e6f77059e6d469a6c4c314cc06d5a4c0", "status": "stopped", "info": {"name": "lul", "url": "http://test"}}'
|
||||
|
||||
#curl -X POST localhost:5000/api/v1/hooks/new -d '{"name": "asd", "description": "hola", "type": "webhook"}'
|
||||
|
||||
#curl -X POST "https://europe-west1-shuffle-241517.cloudfunctions.net/webhook_982995716e67c3a549092d3a3a7921cd" -H "Content-Type:application/json" -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26" --data '{"name":"Keyboard Cat"}' -v
|
||||
|
||||
|
||||
#jcurl http://localhost:5001/api/v1/hooks/b4ba07c9-45d4-41f2-b260-83c8e99eba0c -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26"
|
||||
#curl https://shuffler.io/api/v1/hooks/b4ba07c9-45d4-41f2-b260-83c8e99eba0c -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26"
|
||||
#curl -X POST "http://localhost:8080" -H "Content-Type:application/json" -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26" --data '{"test": {"hello": "HEYOOOO"}}' -v
|
||||
|
||||
#curl -X POST "https://europe-west1-shuffle-241517.cloudfunctions.net/webhook_3ceff795-ce9a-43a2-a2f5-d4401a6e772d" -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26" --data 'wut'
|
||||
curl POST "https://europe-west1-shuffler.cloudfunctions.net/outlooktrigger_be4dbb0a-d396-4544-bc36-e57d1bdb2e40" -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26" --data 'wut' -vvv
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# This is a script to test a function by itself
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
def invoke(url, headers, message):
|
||||
# Used for testing
|
||||
try:
|
||||
ret = requests.post(url, headers=headers, json=message, timeout=5)
|
||||
print(ret.text)
|
||||
print(ret.status_code)
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
print(f"Requesterror: {e}")
|
||||
|
||||
def invoke_multi(url, headers, message):
|
||||
cnt = 0
|
||||
maxcnt = 100
|
||||
print("Running %d requests towards %s." % (maxcnt, url))
|
||||
while(1):
|
||||
try:
|
||||
ret = requests.post(url, headers=headers, json=message, timeout=1)
|
||||
print(ret.status_code)
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
print(f"Connectionerror: {e}")
|
||||
except requests.exceptions.ReadTimeout as e:
|
||||
print(f"Readtimeout: {e}")
|
||||
|
||||
cnt += 1
|
||||
if cnt == maxcnt:
|
||||
break
|
||||
|
||||
print("Done :)")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Specific thingies for hello_world
|
||||
message = {
|
||||
"parameters": [{
|
||||
"id_": "asd",
|
||||
"name": "call",
|
||||
"value": "REPEAT THIS DATA PLEASE THANKS",
|
||||
"variant": "STATIC_VALUE",
|
||||
}],
|
||||
"name": "repeat_back_to_me",
|
||||
"execution_id": "asd",
|
||||
"label": "",
|
||||
"position": "",
|
||||
"app_name": "hello_world",
|
||||
"app_version": "1.0.0",
|
||||
"label": "lul",
|
||||
"priority": "1",
|
||||
"id_": "test",
|
||||
"id": "test",
|
||||
"authorization": "hey",
|
||||
}
|
||||
|
||||
apikey = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjYwZjQwNjBlNThkNzVmZDNmNzBiZWZmODhjNzk0YTc3NTMyN2FhMzEiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL3NodWZmbGVyLmlvL2FwaS92MS93b3JrZmxvd3MvMWQ5ZDhjZTItNTY2ZS00YzNmLThhMzctNWQ2YzdkMjAwMGI1L2V4ZWN1dGUiLCJhenAiOiIxMDMwNzY3ODIwNjE0MjQ2MTg0MjIiLCJlbWFpbCI6InNjaGVkdWxlckBzaHVmZmxlLTI0MTUxNy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJleHAiOjE1NjU1Mjc1NTEsImlhdCI6MTU2NTUyMzk1MSwiaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTAzMDc2NzgyMDYxNDI0NjE4NDIyIn0.r0EDq9fjhf_5CPTiltyfk_L3uYJp577Uy0yYPcCAl2nv50_z_oUtbWGBpQLL8gcj-NGd3g4E52Qur8k6hCMIQweLS6WAb1279vGffEoCNDfkWb3Oy-yJGP1kzwLvqFJqnHLkSWYXNWvSyWnEimW8Rryx_m1BXS5wcA8l4NIr83kS7fPZrTwjnwFSeGSThwk91DVARzapQb8r0GEgOUyHZ1aBXnV98mikzSUt-5xFKe9eMdD22YJAj0Ru-DxAxs5nOqghX4PMRysWjshjOMrlR1piPWxqAmewp8YKZDCQ5gXskpeAFBDoULT971Wsx_NCohnJsFqx1JfPS9ZYMTW2oQ"
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {apikey}"
|
||||
}
|
||||
|
||||
location = "europe-west2"
|
||||
functionname = "hello-world-1-0-6"
|
||||
project = "shuffler"
|
||||
|
||||
url = f"https://{location}-{project}.cloudfunctions.net/{functionname}"
|
||||
print(url)
|
||||
invoke(url, headers, message)
|
||||
#invoke_multi(url, headers, message)
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
curl localhost:5000/api/v1/schedules/apps
|
||||
@@ -0,0 +1,2 @@
|
||||
# Fails cus of unmarshal
|
||||
curl -XPOST http://localhost:5001/api/v1/workflows/1d9d8ce2-566e-4c3f-8a37-5d6c7d2000b5/schedule -d '{"name": "hey", "frequency": "*/1 * * * *", "execution_argument": "{\"test\": \"hey\"}"}' -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYwZjQwNjBlNThkNzVmZDNmNzBiZWZmODhjNzk0YTc3NTMyN2FhMzEiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL3NodWZmbGVyLmlvL2FwaS92MS93b3JrZmxvd3MvMWQ5ZDhjZTItNTY2ZS00YzNmLThhMzctNWQ2YzdkMjAwMGI1L2V4ZWN1dGUiLCJhenAiOiIxMDMwNzY3ODIwNjE0MjQ2MTg0MjIiLCJlbWFpbCI6InNjaGVkdWxlckBzaHVmZmxlLTI0MTUxNy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJleHAiOjE1NjU1Mjc1NTEsImlhdCI6MTU2NTUyMzk1MSwiaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tIiwic3ViIjoiMTAzMDc2NzgyMDYxNDI0NjE4NDIyIn0.r0EDq9fjhf_5CPTiltyfk_L3uYJp577Uy0yYPcCAl2nv50_z_oUtbWGBpQLL8gcj-NGd3g4E52Qur8k6hCMIQweLS6WAb1279vGffEoCNDfkWb3Oy-yJGP1kzwLvqFJqnHLkSWYXNWvSyWnEimW8Rryx_m1BXS5wcA8l4NIr83kS7fPZrTwjnwFSeGSThwk91DVARzapQb8r0GEgOUyHZ1aBXnV98mikzSUt-5xFKe9eMdD22YJAj0Ru-DxAxs5nOqghX4PMRysWjshjOMrlR1piPWxqAmewp8YKZDCQ5gXskpeAFBDoULT971Wsx_NCohnJsFqx1JfPS9ZYMTW2oQ"
|
||||
@@ -0,0 +1,3 @@
|
||||
#curl -X POST -H "Content-Type: application/json" shuffler.io/functions/sendmail -H "Authorization: Bearer " -d '{"target": "frikky@shuffler.io", "body": "Hey, this is a body for something to look at", "subject": "SOS check me", "type": "alert", "sender_company": "shuffler"}'
|
||||
|
||||
curl -X POST -H "Content-Type: application/json" localhost:5001/functions/sendmail -H "Authorization: Bearer " -d '{"targets": ["frikky@shuffler.io", "rheyix.yt@gmail.com"], "body": "Hey, this is a body for something to look at", "subject": "SOS check me", "type": "alert", "sender_company": "shuffler"}'
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
# Should give 401
|
||||
curl localhost:5000/api/v1/uploadResult/asdasd -d {}
|
||||
echo
|
||||
|
||||
# Should give 200 if it exists
|
||||
curl localhost:5000/api/v1/uploadResult/e07910a06a086c83ba41827aa00b26ed -d '{"title": "helo","description": "wut", "type": "hi", "source": "wutface", "sourceRef": "halvor hei"}'
|
||||
@@ -0,0 +1 @@
|
||||
curl -H "Content-Type: application/json" localhost:5001/api/v1/triggers/9e845679-5843-4959-a76c-a6d664e9df35 -H "Authorization: Bearer 377469e8-dd5d-4521-8d9e-416d8d2f6fd4"
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
curl http://localhost:5001/ws -H "Connections: Upgrade"
|
||||
|
||||
#curl -X POST "https://europe-west1-shuffle-241517.cloudfunctions.net/webhook_982995716e67c3a549092d3a3a7921cd" -H "Content-Type:application/json" -H "Authorization: Bearer 144308d0-6aab-4d4f-8bb2-75189281ee26" --data '{"name":"Keyboard Cat"}' -v
|
||||
@@ -0,0 +1,202 @@
|
||||
[
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"app_name": "hello_world",
|
||||
"app_version": "1.0.0",
|
||||
"errors": [],
|
||||
"id_": "2686a5d4-531d-158f-6b1a-0c1d23481304",
|
||||
"is_valid": true,
|
||||
"label": "check_bool",
|
||||
"name": "check_bool",
|
||||
"environment": "cloud",
|
||||
"parameters": [],
|
||||
"position": {
|
||||
"x": 329.98133726556375,
|
||||
"y": 160.01013778166904
|
||||
},
|
||||
"priority": 3
|
||||
}
|
||||
],
|
||||
"branches": [
|
||||
{
|
||||
"destination_id": "6478ecae-b10e-88e9-e34d-9bbe6aff393d",
|
||||
"id_": "5fd6a357-ae33-b1af-5dc2-0306efa28887",
|
||||
"source_id": "2686a5d4-531d-158f-6b1a-0c1d23481304"
|
||||
},
|
||||
{
|
||||
"destination_id": "2686a5d4-531d-158f-6b1a-0c1d23481304",
|
||||
"id_": "d46d0b05-5757-5084-c339-70ac63985781",
|
||||
"source_id": "6478ecae-b10e-88e9-e34d-9bbe6aff393d"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"app_name": "Builtin",
|
||||
"app_version": "1.0.0",
|
||||
"conditional": "",
|
||||
"errors": [],
|
||||
"id_": "6478ecae-b10e-88e9-e34d-9bbe6aff393d",
|
||||
"is_valid": true,
|
||||
"label": "Condition",
|
||||
"name": "Condition",
|
||||
"position": {
|
||||
"x": 320.97142988802364,
|
||||
"y": 394.9753467582139
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "",
|
||||
"errors": [],
|
||||
"id_": "a5f82cfd-0f38-3474-20e2-f757f3718707",
|
||||
"is_valid": true,
|
||||
"name": "asd2",
|
||||
"start": "2686a5d4-531d-158f-6b1a-0c1d23481304",
|
||||
"tags": [],
|
||||
"transforms": [],
|
||||
"triggers": [],
|
||||
"workflow_variables": []
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"app_name": "hello_world",
|
||||
"app_version": "1.0.0",
|
||||
"errors": [],
|
||||
"id_": "51df7c4f-b856-1aca-402b-9fec660b6505",
|
||||
"is_valid": true,
|
||||
"label": "wut",
|
||||
"name": "hello_world",
|
||||
"parameters": [],
|
||||
"position": {
|
||||
"x": 250,
|
||||
"y": 150
|
||||
},
|
||||
"priority": 3
|
||||
},
|
||||
{
|
||||
"app_name": "hello_world",
|
||||
"app_version": "1.0.0",
|
||||
"errors": [],
|
||||
"id_": "36975212-3b9a-2e4c-4ad7-0ee5a6325842",
|
||||
"is_valid": true,
|
||||
"label": "check_bool",
|
||||
"name": "check_bool",
|
||||
"parameters": [],
|
||||
"position": {
|
||||
"x": 241.00207363715955,
|
||||
"y": 294.9896318142021
|
||||
},
|
||||
"priority": 3
|
||||
}
|
||||
],
|
||||
"branches": [
|
||||
{
|
||||
"destination_id": "36975212-3b9a-2e4c-4ad7-0ee5a6325842",
|
||||
"id_": "44231b8f-4331-764e-0c1d-ccbc901d1309",
|
||||
"source_id": "51df7c4f-b856-1aca-402b-9fec660b6505"
|
||||
},
|
||||
{
|
||||
"destination_id": "51df7c4f-b856-1aca-402b-9fec660b6505",
|
||||
"id_": "9b3dc561-e879-4877-c32e-ab7149d83b39",
|
||||
"source_id": "36975212-3b9a-2e4c-4ad7-0ee5a6325842"
|
||||
}
|
||||
],
|
||||
"conditions": [],
|
||||
"description": "",
|
||||
"errors": [],
|
||||
"id_": "4e437698-fc18-29d3-e875-969b57354685",
|
||||
"is_valid": true,
|
||||
"name": "hi",
|
||||
"start": "51df7c4f-b856-1aca-402b-9fec660b6505",
|
||||
"tags": [],
|
||||
"transforms": [],
|
||||
"triggers": [],
|
||||
"workflow_variables": []
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"app_name": "hello_world",
|
||||
"app_version": "1.0.0",
|
||||
"errors": [],
|
||||
"id_": "d0dbd1c4-dd61-6d4a-70e1-ac05dad0fe1f",
|
||||
"is_valid": true,
|
||||
"label": "i am bool",
|
||||
"name": "check_bool",
|
||||
"parameters": [],
|
||||
"position": {
|
||||
"x": 377.9998262128892,
|
||||
"y": 330.01190441708906
|
||||
},
|
||||
"priority": 3
|
||||
},
|
||||
{
|
||||
"app_name": "hello_world",
|
||||
"app_version": "1.0.0",
|
||||
"errors": [],
|
||||
"id_": "39353c1c-b179-152c-f977-615a15a5de37",
|
||||
"is_valid": true,
|
||||
"label": "hello_world",
|
||||
"name": "hello_world",
|
||||
"parameters": [],
|
||||
"position": {
|
||||
"x": 375.2590614780782,
|
||||
"y": 160.6226941577116
|
||||
},
|
||||
"priority": 3
|
||||
}
|
||||
],
|
||||
"branches": [
|
||||
{
|
||||
"destination_id": "d0dbd1c4-dd61-6d4a-70e1-ac05dad0fe1f",
|
||||
"id_": "7c81993f-5fd6-fc83-2f41-8074d6c8dc25",
|
||||
"source_id": "39353c1c-b179-152c-f977-615a15a5de37"
|
||||
}
|
||||
],
|
||||
"conditions": [],
|
||||
"description": "",
|
||||
"errors": [],
|
||||
"id_": "af8467be-43ca-d38b-3f7e-9aeb922fb21a",
|
||||
"is_valid": true,
|
||||
"name": "new!",
|
||||
"start": "39353c1c-b179-152c-f977-615a15a5de37",
|
||||
"tags": [],
|
||||
"transforms": [],
|
||||
"triggers": [],
|
||||
"workflow_variables": []
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
{
|
||||
"app_name": "Builtin",
|
||||
"app_version": "1.0.0",
|
||||
"errors": [],
|
||||
"id_": "7dddec9a-b493-8b58-9234-1b74dd9b420a",
|
||||
"is_valid": true,
|
||||
"label": "Boolean",
|
||||
"name": "Boolean",
|
||||
"parameters": [],
|
||||
"position": {
|
||||
"x": 350,
|
||||
"y": 250
|
||||
},
|
||||
"priority": 3
|
||||
}
|
||||
],
|
||||
"branches": [],
|
||||
"conditions": [],
|
||||
"description": "",
|
||||
"errors": [
|
||||
"Action Builtin.Boolean does not exist"
|
||||
],
|
||||
"id_": "07bae551-21e6-c0f5-4db3-81e8a1e8a805",
|
||||
"is_valid": false,
|
||||
"name": "wutface",
|
||||
"start": "7dddec9a-b493-8b58-9234-1b74dd9b420a",
|
||||
"tags": [],
|
||||
"transforms": [],
|
||||
"triggers": [],
|
||||
"workflow_variables": []
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
#curl -X POST -H "Content-Type: application/json" localhost:5001/api/v1/workflows/3d14ca4a-67bd-8dfb-2673-2864f1ccf59c/execute -d '{"workflow_id": "3d14ca4a-67bd-8dfb-2673-2864f1ccf59c", "execution_id": "eaaa8d19-a761-12b8-cac2-f34eb50c3711"}'
|
||||
|
||||
curl -X POST -H "Content-Type: application/json" https://shuffle-241517.appspot.com/api/v1/workflows/3d14ca4a-67bd-8dfb-2673-2864f1ccf59c/execute -d '{"workflow_id": "3d14ca4a-67bd-8dfb-2673-2864f1ccf59c", "execution_id": "eaaa8d19-a761-12b8-cac2-f34eb50c3711"}'
|
||||
|
||||
#curl -X POST http://localhost:5001/api/v1/workflows/streams -H "Content-Type: application/json" \
|
||||
# -d '{"execution_id": "eaaa8d19-a761-12b8-cac2-f34eb50c3711",
|
||||
# "result": "hello_result",
|
||||
# "started_at": 1562309342,
|
||||
# "authorization": "afcc298d-c6c2-4b0d-8221-1603b44d072d",
|
||||
# "status": "ABORTED",
|
||||
# "action": {
|
||||
# "app_name": "hi",
|
||||
# "app_version": "ho",
|
||||
# "id_": "this_is_an_id",
|
||||
# "label": "wut",
|
||||
# "name": "stream_testing",
|
||||
# "parameters": [],
|
||||
# "position": {
|
||||
# "x": 100,
|
||||
# "y": 100
|
||||
# },
|
||||
# "priority": 1
|
||||
# }}'
|
||||
@@ -0,0 +1,15 @@
|
||||
# Get all workflows
|
||||
curl localhost:5001/api/v1/workflows
|
||||
|
||||
# Get A workflow
|
||||
#curl localhost:5001/api/v1/workflows/a5f82cfd-0f38-3474-20e2-f757f3718707
|
||||
|
||||
# NEW workflow
|
||||
# curl -XPOST localhost:5001/api/v1/workflows -d '{"tags":[],"actions":[],"branches":[],"conditions":[{"label":"Condition","app_name":"Builtin","name":"Condition","conditional":"","id_":"18165f42-aab9-6c9a-0ef4-4e5a37a3b2ad","app_version":"1.0.0","position":{"x":224,"y":168}}],"workflow_variables":[],"name":"asd","start":"18165f42-aab9-6c9a-0ef4-4e5a37a3b2ad","id_":"3d14ca4a-67bd-8dfb-2673-2864f1ccf59c"}'
|
||||
|
||||
# Add a workflow
|
||||
#curl localhost:5001/api/v1/workflows/a5f82cfd-0f38-3474-20e2-f757f3718707 -d '{"actions":[{"app_name":"hello_world","app_version":"1.0.0","errors":[],"id_":"2686a5d4-531d-158f-6b1a-0c1d23481304","is_valid":true,"label":"check_bool","name":"check_bool","parameters":[],"position":{"x":329.98133726556375,"y":160.01013778166904},"priority":3}],"branches":[{"destination_id":"6478ecae-b10e-88e9-e34d-9bbe6aff393d","id_":"5fd6a357-ae33-b1af-5dc2-0306efa28887","source_id":"2686a5d4-531d-158f-6b1a-0c1d23481304"},{"destination_id":"2686a5d4-531d-158f-6b1a-0c1d23481304","id_":"d46d0b05-5757-5084-c339-70ac63985781","source_id":"6478ecae-b10e-88e9-e34d-9bbe6aff393d"}],"conditions":[{"app_name":"Builtin","app_version":"1.0.0","conditional":"","errors":[],"id_":"6478ecae-b10e-88e9-e34d-9bbe6aff393d","is_valid":true,"label":"Condition","name":"Condition","position":{"x":320.97142988802364,"y":394.9753467582139}}],"description":"","errors":[],"id_":"a5f82cfd-0f38-3474-20e2-f757f3718707","is_valid":true,"name":"asd2","start":"2686a5d4-531d-158f-6b1a-0c1d23481304","tags":[],"transforms":[],"triggers":[],"workflow_variables":[]}'
|
||||
|
||||
# Execute a workflow
|
||||
# curl -H "Content-Type: application/json" localhost:5001/api/v1/workflows/3d14ca4a-67bd-8dfb-2673-2864f1ccf59c/execute
|
||||
#curl -X POST -H "Content-Type: application/json" localhost:5001/api/v1/workflows/3d14ca4a-67bd-8dfb-2673-2864f1ccf59c/execute -d '{"workflow_id": "3d14ca4a-67bd-8dfb-2673-2864f1ccf59c", "execution_id": "eaaa8d19-a761-12b8-cac2-f34eb50c3711"}'
|
||||
@@ -0,0 +1,15 @@
|
||||
# Build environment
|
||||
# production environment
|
||||
from golang as builder
|
||||
|
||||
RUN go get github.com/gorilla/handlers
|
||||
RUN go get github.com/gorilla/mux
|
||||
|
||||
WORKDIR /app
|
||||
COPY webhook.go /app/webhook.go
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o webhook .
|
||||
|
||||
from scratch
|
||||
COPY --from=builder /app/ /
|
||||
|
||||
CMD ["./webhook"]
|
||||
@@ -0,0 +1,7 @@
|
||||
# Steps to deploy to Google cloud function
|
||||
1.
|
||||
```bash
|
||||
zip webhook.zip *
|
||||
```
|
||||
2. Go to google cloud bucket and upload the zip
|
||||
3. Go to worker for webhook and upload
|
||||
@@ -0,0 +1,44 @@
|
||||
package function
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetUserDetails - Get one user's details from randomuser.me API
|
||||
func GetUserDetails(w http.ResponseWriter, r *http.Request) {
|
||||
randomUserClient := http.Client{
|
||||
Timeout: time.Second * 3,
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://randomuser.me/api/", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err2 := randomUserClient.Do(req)
|
||||
if err2 != nil {
|
||||
log.Fatal(err2)
|
||||
return
|
||||
}
|
||||
|
||||
body, err3 := ioutil.ReadAll(res.Body)
|
||||
if err3 != nil {
|
||||
log.Fatal(err3)
|
||||
}
|
||||
|
||||
var o map[string]interface{}
|
||||
json.Unmarshal([]byte(body), &o)
|
||||
|
||||
results := o["results"].([]interface{})
|
||||
result := results[0].(map[string]interface{})
|
||||
|
||||
result["generator"] = "google-cloud-function"
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(result)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
docker build . -t gcr.io/shuffle-241517/webhook
|
||||
docker push gcr.io/shuffle-241517/webhook
|
||||
|
||||
gcloud beta run deploy webhook --image gcr.io/shuffle-241517/webhook
|
||||
@@ -0,0 +1,16 @@
|
||||
docker stop webhook
|
||||
docker rm webhook
|
||||
docker rmi webhook
|
||||
|
||||
docker build . -t webhook
|
||||
docker run -d \
|
||||
-e "HOOKPORT=5001" \
|
||||
-e "URIPATH=/webhook" \
|
||||
-e "CALLBACKURL=http://192.168.159.151:5000/api/v1/hooks/d6ef8912e8bd37776e654cbc14c2629c/result" \
|
||||
-p 5001:5001 \
|
||||
--name webhook \
|
||||
-h webhook \
|
||||
--restart always \
|
||||
webhook
|
||||
|
||||
docker logs -f webhook
|
||||
@@ -0,0 +1,260 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
type Info struct {
|
||||
Url string `json:"url" datastore:"url"`
|
||||
Name string `json:"name" datastore:"name"`
|
||||
Description string `json:"description" datastore:"description"`
|
||||
}
|
||||
|
||||
// Actions to be done by webhooks etc
|
||||
// Field is the actual field to use from json
|
||||
type HookAction struct {
|
||||
Type string `json:"type" datastore:"type"`
|
||||
Name string `json:"name" datastore:"name"`
|
||||
Id string `json:"id" datastore:"id"`
|
||||
Field string `json:"field" datastore:"field"`
|
||||
}
|
||||
|
||||
type Hook struct {
|
||||
Id string `json:"id" datastore:"id"`
|
||||
Info Info `json:"info" datastore:"info"`
|
||||
Transforms struct{} `json:"transforms" datastore:"transforms"`
|
||||
Actions []HookAction `json:"actions" datastore:"actions"`
|
||||
Type string `json:"type" datastore:"type"`
|
||||
Status string `json:"status" datastore:"status"`
|
||||
Running bool `json:"running" datastore:"running"`
|
||||
}
|
||||
|
||||
var hook Hook
|
||||
|
||||
func handleWorkflowAction(request *http.Request, action HookAction) error {
|
||||
//log.Printf("WORKFLOW!: %#v", action)
|
||||
log.Printf("Should execute workflow %s", action.Id)
|
||||
|
||||
callbackUrl := os.Getenv("CALLBACKURL")
|
||||
apikey := os.Getenv("APIKEY")
|
||||
fullUrl := fmt.Sprintf("%s/api/v1/workflows/%s/execute", callbackUrl, action.Id)
|
||||
|
||||
// ret = requests.post(fullurl, headers=headers, json=data)
|
||||
//if ret.status_code != 202:
|
||||
// print(ret.text)
|
||||
// print(ret.status_code)
|
||||
// print("Exiting workflows - run queue")
|
||||
// exit()
|
||||
|
||||
body, err := ioutil.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Execute a workflow
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest(
|
||||
"POST",
|
||||
fullUrl,
|
||||
bytes.NewBuffer(body),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error making http request: %s", req)
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, apikey))
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Printf("Error in http request: %s", req)
|
||||
}
|
||||
|
||||
log.Printf("%#v", resp)
|
||||
return nil
|
||||
}
|
||||
|
||||
// FIXME - refresh hook information once in a while. Compare timestamps or something
|
||||
func callback(resp http.ResponseWriter, request *http.Request) {
|
||||
//apikey = os.Getenv("APIKEY")
|
||||
//hookId = os.Getenv("HOOKID")
|
||||
|
||||
handledWorkflowIds := []string{}
|
||||
for _, item := range hook.Actions {
|
||||
if item.Type == "" {
|
||||
log.Printf("CONTINUE AAS EMPTY ITEM: %#v", item)
|
||||
continue
|
||||
}
|
||||
|
||||
if item.Type == "workflow" {
|
||||
found := false
|
||||
for _, workflowId := range handledWorkflowIds {
|
||||
if item.Id == workflowId {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
continue
|
||||
}
|
||||
|
||||
handledWorkflowIds = append(handledWorkflowIds, item.Id)
|
||||
err := handleWorkflowAction(request, item)
|
||||
if err != nil {
|
||||
log.Printf("Error in workflow exec: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME - send the webhookdata to a logging service? Idk
|
||||
//body, err := ioutil.ReadAll(request.Body)
|
||||
//if err != nil {
|
||||
// log.Println("Failed reading body")
|
||||
// resp.WriteHeader(401)
|
||||
// resp.Write([]byte(fmt.Sprintf(`{"success": false}`)))
|
||||
// return
|
||||
//}
|
||||
|
||||
//callback, err := http.Post(callbackUrl, "application/json", bytes.NewBuffer(body))
|
||||
//if err != nil {
|
||||
// log.Printf("Failed sending callback to %s", callbackUrl)
|
||||
//}
|
||||
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
|
||||
return
|
||||
}
|
||||
|
||||
func loadConfiguration(fullUrl string, apikey string) error {
|
||||
client := &http.Client{}
|
||||
|
||||
req, err := http.NewRequest(
|
||||
"GET",
|
||||
fullUrl,
|
||||
nil,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error making http request: %s", req)
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", fmt.Sprintf(`Bearer %s`, apikey))
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Printf("Error in http request: %s", req)
|
||||
return err
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Printf("Error reading response: %s", req)
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &hook)
|
||||
if err != nil {
|
||||
log.Printf("Failed unmarshaling hook API", req)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("%#v", hook)
|
||||
log.Println(hook.Actions)
|
||||
return nil
|
||||
}
|
||||
|
||||
func webhook() {
|
||||
// FIXME - remove static
|
||||
ip := "0.0.0.0"
|
||||
|
||||
// Basic webserver stuff
|
||||
baseFilePath := os.Getenv("URIPATH")
|
||||
basePort := os.Getenv("HOOKPORT")
|
||||
callbackUrl := os.Getenv("CALLBACKURL")
|
||||
apikey := os.Getenv("APIKEY")
|
||||
hookId := os.Getenv("HOOKID")
|
||||
|
||||
if len(baseFilePath) == 0 {
|
||||
log.Println("Env URIPATH not set")
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
if len(basePort) == 0 {
|
||||
log.Println("Env HOOKPORT not set")
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
if len(callbackUrl) == 0 {
|
||||
log.Println("Env CALLBACKURL not set")
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
if len(apikey) == 0 {
|
||||
log.Println("Env APIKEY not set")
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
if len(hookId) == 0 {
|
||||
log.Println("Env HOOKID not set")
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
log.Println("Loading hook configuration")
|
||||
err := loadConfiguration(
|
||||
fmt.Sprintf("%s/api/v1/hooks/%s", callbackUrl, hookId),
|
||||
apikey,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Error loading config: %s", err)
|
||||
}
|
||||
|
||||
// Optional
|
||||
// if len(callbackOpts) == 0 {
|
||||
// log.Println("Env CALLBACKOPTS not set")
|
||||
// os.Exit(3)
|
||||
// }
|
||||
|
||||
port := fmt.Sprintf(":%s", basePort)
|
||||
log.Printf("Starting webhook on %s%s with path %s", ip, port, baseFilePath)
|
||||
|
||||
// Routing
|
||||
mux := mux.NewRouter()
|
||||
mux.SkipClean(true)
|
||||
|
||||
// FIXME - Add path for updating the hook? Can be a specific POST requeuest from backend
|
||||
mux.HandleFunc(baseFilePath, callback).Methods("POST")
|
||||
|
||||
handlers.LoggingHandler(os.Stdout, mux)
|
||||
loggedRouter := handlers.LoggingHandler(os.Stdout, mux)
|
||||
|
||||
err = http.ListenAndServe(
|
||||
port,
|
||||
loggedRouter,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("ListenAndServer: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func F(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
w.Write([]byte(r.Header.Get("X-Forwarded-For")))
|
||||
}
|
||||
|
||||
func main() {
|
||||
webhook()
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
version: '3'
|
||||
# Remove database port
|
||||
# Remove backend port
|
||||
services:
|
||||
frontend:
|
||||
build: ./frontend
|
||||
image: frikky/shuffle:frontend
|
||||
container_name: shuffle-frontend
|
||||
hostname: shuffle-frontend
|
||||
ports:
|
||||
- "3001:80"
|
||||
networks:
|
||||
- shuffle
|
||||
restart: unless-stopped
|
||||
database:
|
||||
build: ./backend/database
|
||||
image: frikky/shuffle:database
|
||||
container_name: shuffle-database
|
||||
hostname: shuffle-database
|
||||
ports:
|
||||
- "8000:8000"
|
||||
networks:
|
||||
- shuffle
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /etc/shuffle:/etc/shuffle
|
||||
backend:
|
||||
build: ./backend
|
||||
image: frikky/shuffle:backend
|
||||
container_name: shuffle-backend
|
||||
hostname: ${BACKEND_HOSTNAME}
|
||||
ports:
|
||||
- "${BACKEND_PORT}:${BACKEND_PORT}"
|
||||
networks:
|
||||
- shuffle
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
environment:
|
||||
- ORG_ID=${ORG_ID}
|
||||
- DATASTORE_EMULATOR_HOST=shuffle-database:8000
|
||||
- BACKEND_PORT=${BACKEND_PORT}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- database
|
||||
- frontend
|
||||
orborus:
|
||||
build: ./functions/onprem/orborus
|
||||
image: frikky/shuffle:orborus
|
||||
container_name: shuffle-orborus
|
||||
hostname: shuffle-orborus
|
||||
networks:
|
||||
- shuffle
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
environment:
|
||||
- ORG_ID=${ORG_ID}
|
||||
- ENVIRONMENT_NAME=${ENVIRONMENT_NAME}
|
||||
- BASE_URL=http://${OUTER_HOSTNAME}:${BACKEND_PORT}
|
||||
restart: unless-stopped
|
||||
app_sdk:
|
||||
build: ./functions/onprem/app_sdk
|
||||
image: frikky/shuffle:app_sdk
|
||||
worker:
|
||||
build: ./functions/onprem/worker
|
||||
image: frikky/shuffle:worker
|
||||
networks:
|
||||
shuffle:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,33 @@
|
||||
# Build environment
|
||||
FROM node as builder
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
ENV PATH /usr/src/app/node_modules/.bin:$PATH
|
||||
COPY package.json /usr/src/app/package.json
|
||||
|
||||
RUN npm install --verbose
|
||||
|
||||
COPY . /usr/src/app
|
||||
|
||||
RUN npm run-script build
|
||||
|
||||
# Production environment
|
||||
from nginx:latest
|
||||
|
||||
RUN mkdir -p /usr/share/nginx/html/build
|
||||
RUN mkdir -p /usr/share/nginx/html/css
|
||||
RUN mkdir -p /usr/share/nginx/html/js
|
||||
RUN mkdir -p /usr/share/nginx/html/img
|
||||
|
||||
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
|
||||
COPY --from=builder /usr/src/app/certs/fullchain.pem /etc/nginx/fullchain.cert.pem
|
||||
COPY --from=builder /usr/src/app/certs/privkey.pem /etc/nginx/privkey.pem
|
||||
|
||||
# Test
|
||||
COPY --from=builder /usr/src/app/nginxtest.conf /etc/nginx/nginx.conf
|
||||
# Prod
|
||||
#COPY --from=builder /usr/src/app/nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
@@ -0,0 +1,3 @@
|
||||
npm run build
|
||||
rm -rf ../backend/go-app/build
|
||||
cp -r build/ ../backend/go-app/build
|
||||
@@ -0,0 +1,10 @@
|
||||
This directory contains your keys and certificates.
|
||||
|
||||
`privkey.pem` : the private key for your certificate.
|
||||
`fullchain.pem`: the certificate file used in most server software.
|
||||
`chain.pem` : used for OCSP stapling in Nginx >=1.3.7.
|
||||
`cert.pem` : will break many server configurations, and should not be used
|
||||
without reading further documentation (see link below).
|
||||
|
||||
We recommend not moving these files. For more information, see the Certbot
|
||||
User Guide at https://certbot.eff.org/docs/using.html#where-are-my-certificates.
|
||||
@@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFRzCCBC+gAwIBAgISA2OOAaaFtVzoqzLXCBV19fpjMA0GCSqGSIb3DQEBCwUA
|
||||
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
|
||||
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTAyMDkxMjQxMzlaFw0x
|
||||
OTA1MTAxMjQxMzlaMBMxETAPBgNVBAMTCGVuZGFvLmlvMIIBIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAQ8AMIIBCgKCAQEAr4nj/G0MxSEWHnD0oC+jS03ofE6hXC87z0h6iNh5
|
||||
lRdVoh9o2H22iQNTmWOWB56oqdBQEOeCcNFexYOzL9S91IVy3Q+hDev5rkJ44uFP
|
||||
0VtgKbTc/aKIpEcSYPda2b1lb1mAYpJ/HqvzMDrJ+izCpTLEs2h+mD+2LAx7Ie3Z
|
||||
OE+mFhU7aYdJ7rbUob7BT9cxzbp4CLGjaZHFKrC1uafSSh3bFFlXJpYmleunGVS9
|
||||
4BUuveZdWJGLFMN1cAJiMGLewsKp5YXKB9eiH6gAg90udRaanPz3NgFa/8qju9iA
|
||||
64YJcDNr9Aj04EIntRbPJm286+TrJbaf57ESCS32cqaDEwIDAQABo4ICXDCCAlgw
|
||||
DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAM
|
||||
BgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTaQOYhr73PSHSd+P+b7FJhYcVPwDAfBgNV
|
||||
HSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEwLgYI
|
||||
KwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcwLwYI
|
||||
KwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcvMBMG
|
||||
A1UdEQQMMAqCCGVuZGFvLmlvMEwGA1UdIARFMEMwCAYGZ4EMAQIBMDcGCysGAQQB
|
||||
gt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5cHQub3Jn
|
||||
MIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHYA4mlLribo6UAJ6IYbtjuD1D7n/nSI
|
||||
+6SPKJMBnd3x2/4AAAFo0n3+fgAABAMARzBFAiBebCfZn5HQoIloL8iS7Q2U9DE/
|
||||
IlMLvhK0UFlhr1u5agIhAN84nfBT1slMt86E95tvvYbEHcAfapogmJmOxSoI5ati
|
||||
AHUAY/Lbzeg7zCzPC3KEJ1drM6SNYXePvXWmOLHHaFRL2I0AAAFo0n4AegAABAMA
|
||||
RjBEAiARET8ZHf3qcahgGa9O440U7/Zbfi5+WO0XRlN/cVQZBgIgKQpHRXv9urpk
|
||||
MS+tz9jvsxERUYEEjRgsSnM1Bs3AF44wDQYJKoZIhvcNAQELBQADggEBADPUIj7w
|
||||
z+He68As1CFu9tAwGMeLZDgXtPsybAOY9cWm9aL5sC5RldK+ob/5Xi2aa7YEeeij
|
||||
VP44lOjXtr3EmGm34vN9pL7JyAhYAmEZToyJEt4dbyCbLhO0IT9Po4ropiXk7ze0
|
||||
CRNEg2CCdXkG7KLHIxP6Am1vr1/TA8IC4lf0ocYJs5sU4/7Bj8eViuZ7F8ZcJo1t
|
||||
O24jBw5m48fbmmDWkcc3YHVD55sIys3RDEQMLABgIxgN+kqRqfCslREpPYWa/o2T
|
||||
jOt99S/zjdeRgGJfO8XKtI2TH8ZMHNyKaJYjmdX0CcZFmm2E8SZRYuB4j/KUdd8a
|
||||
xje2fid5pAJf2Tk=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,27 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
|
||||
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
|
||||
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
|
||||
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
|
||||
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
|
||||
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
|
||||
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
|
||||
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
|
||||
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
|
||||
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
|
||||
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
|
||||
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
|
||||
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
|
||||
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
|
||||
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
|
||||
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
|
||||
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
|
||||
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
|
||||
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
|
||||
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
|
||||
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,58 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFRzCCBC+gAwIBAgISA2OOAaaFtVzoqzLXCBV19fpjMA0GCSqGSIb3DQEBCwUA
|
||||
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
|
||||
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTAyMDkxMjQxMzlaFw0x
|
||||
OTA1MTAxMjQxMzlaMBMxETAPBgNVBAMTCGVuZGFvLmlvMIIBIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAQ8AMIIBCgKCAQEAr4nj/G0MxSEWHnD0oC+jS03ofE6hXC87z0h6iNh5
|
||||
lRdVoh9o2H22iQNTmWOWB56oqdBQEOeCcNFexYOzL9S91IVy3Q+hDev5rkJ44uFP
|
||||
0VtgKbTc/aKIpEcSYPda2b1lb1mAYpJ/HqvzMDrJ+izCpTLEs2h+mD+2LAx7Ie3Z
|
||||
OE+mFhU7aYdJ7rbUob7BT9cxzbp4CLGjaZHFKrC1uafSSh3bFFlXJpYmleunGVS9
|
||||
4BUuveZdWJGLFMN1cAJiMGLewsKp5YXKB9eiH6gAg90udRaanPz3NgFa/8qju9iA
|
||||
64YJcDNr9Aj04EIntRbPJm286+TrJbaf57ESCS32cqaDEwIDAQABo4ICXDCCAlgw
|
||||
DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAM
|
||||
BgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTaQOYhr73PSHSd+P+b7FJhYcVPwDAfBgNV
|
||||
HSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEwLgYI
|
||||
KwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcwLwYI
|
||||
KwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcvMBMG
|
||||
A1UdEQQMMAqCCGVuZGFvLmlvMEwGA1UdIARFMEMwCAYGZ4EMAQIBMDcGCysGAQQB
|
||||
gt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5cHQub3Jn
|
||||
MIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHYA4mlLribo6UAJ6IYbtjuD1D7n/nSI
|
||||
+6SPKJMBnd3x2/4AAAFo0n3+fgAABAMARzBFAiBebCfZn5HQoIloL8iS7Q2U9DE/
|
||||
IlMLvhK0UFlhr1u5agIhAN84nfBT1slMt86E95tvvYbEHcAfapogmJmOxSoI5ati
|
||||
AHUAY/Lbzeg7zCzPC3KEJ1drM6SNYXePvXWmOLHHaFRL2I0AAAFo0n4AegAABAMA
|
||||
RjBEAiARET8ZHf3qcahgGa9O440U7/Zbfi5+WO0XRlN/cVQZBgIgKQpHRXv9urpk
|
||||
MS+tz9jvsxERUYEEjRgsSnM1Bs3AF44wDQYJKoZIhvcNAQELBQADggEBADPUIj7w
|
||||
z+He68As1CFu9tAwGMeLZDgXtPsybAOY9cWm9aL5sC5RldK+ob/5Xi2aa7YEeeij
|
||||
VP44lOjXtr3EmGm34vN9pL7JyAhYAmEZToyJEt4dbyCbLhO0IT9Po4ropiXk7ze0
|
||||
CRNEg2CCdXkG7KLHIxP6Am1vr1/TA8IC4lf0ocYJs5sU4/7Bj8eViuZ7F8ZcJo1t
|
||||
O24jBw5m48fbmmDWkcc3YHVD55sIys3RDEQMLABgIxgN+kqRqfCslREpPYWa/o2T
|
||||
jOt99S/zjdeRgGJfO8XKtI2TH8ZMHNyKaJYjmdX0CcZFmm2E8SZRYuB4j/KUdd8a
|
||||
xje2fid5pAJf2Tk=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
|
||||
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
|
||||
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
|
||||
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
|
||||
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
|
||||
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
|
||||
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
|
||||
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
|
||||
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
|
||||
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
|
||||
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
|
||||
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
|
||||
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
|
||||
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
|
||||
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
|
||||
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
|
||||
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
|
||||
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
|
||||
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
|
||||
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
|
||||
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,34 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF+TCCBOGgAwIBAgISA8rwfiC3p89WYm/X9RR3XhzTMA0GCSqGSIb3DQEBCwUA
|
||||
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
|
||||
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODExMDkyMTEzNTNaFw0x
|
||||
OTAyMDcyMTEzNTNaMBMxETAPBgNVBAMTCGVuZGFvLmlvMIIBIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAQ8AMIIBCgKCAQEA2NIGtVlzW3fktmv74YG23LKLM6xIxn/mIPt4PgZ+
|
||||
K3407GryQueToAnG5iTXTox5UnyArvqxAe5cTonrlWTai9Do5CuhrKI22EMpwrSl
|
||||
Uo38dtNu2AHLENrY+aFzEwsVvkWGpIQ8/Y63T8M9ohiInF/S5ZPojIJxy7OVQ3io
|
||||
MdLbb1atYfBcnt0DFKyMC1yxDyYj8w1jvwQWlKGBYQeRd2MTWEjHO7CJPgiRwiLi
|
||||
rFBlDWnFz8GKntXLHI2pYL6+X4WgG9XfwzSN/w+ju48JAgCc6yTxvaQxQDpHst3D
|
||||
kv4CXNTP3gt2RSIOkQldBQzi/FgBLDv/qSuXAnLGsIgk5QIDAQABo4IDDjCCAwow
|
||||
DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAM
|
||||
BgNVHRMBAf8EAjAAMB0GA1UdDgQWBBThSBGBk0ZSYT1ptylvWQGZqdkBDjAfBgNV
|
||||
HSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEwLgYI
|
||||
KwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcwLwYI
|
||||
KwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcvMBMG
|
||||
A1UdEQQMMAqCCGVuZGFvLmlvMIH+BgNVHSAEgfYwgfMwCAYGZ4EMAQIBMIHmBgsr
|
||||
BgEEAYLfEwEBATCB1jAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlw
|
||||
dC5vcmcwgasGCCsGAQUFBwICMIGeDIGbVGhpcyBDZXJ0aWZpY2F0ZSBtYXkgb25s
|
||||
eSBiZSByZWxpZWQgdXBvbiBieSBSZWx5aW5nIFBhcnRpZXMgYW5kIG9ubHkgaW4g
|
||||
YWNjb3JkYW5jZSB3aXRoIHRoZSBDZXJ0aWZpY2F0ZSBQb2xpY3kgZm91bmQgYXQg
|
||||
aHR0cHM6Ly9sZXRzZW5jcnlwdC5vcmcvcmVwb3NpdG9yeS8wggECBgorBgEEAdZ5
|
||||
AgQCBIHzBIHwAO4AdQB0ftqDMa0zEJEhnM4lT0Jwwr/9XkIgCMY3NXnmEHvMVgAA
|
||||
AWb6ieWoAAAEAwBGMEQCIE8+auAtQOLY4U1yoEDvCTQULv4PS9Xdo1uJzQF5nhBA
|
||||
AiBsfKR2UMxxIatzPOQPtuRrVEAv4+tX4o/ch5e6TAnOCAB1ACk8UZZUyDlluqpQ
|
||||
/FgH1Ldvv1h6KXLcpMMM9OVFR/R4AAABZvqJ6BEAAAQDAEYwRAIgKj2gwlC/9AIF
|
||||
hrx8nl2oEwxUoi/b4ZCCdQpz8JsJhOACIHRkuo56NErZR87lVtI0oZItZ6nzFPQz
|
||||
BFaxx89eBJKPMA0GCSqGSIb3DQEBCwUAA4IBAQCFURbxUFKAF3CJRiaUsRrjFDuc
|
||||
R25Dm86QgDqDLGkYf7/y5by2/amNzBbBLvcqUhScpjtalqO38Efw+2+c/UBGTQZ7
|
||||
C8vU5KrjG6bnOmoHx/t+ml4E88omMlEmlRIEiL/6yfpOnCCv+3IHJjUw8iPuH7+j
|
||||
57Vhwtq6mOvId2QfDfL86ELgXKvBKfbqac7YfzUHVyB4WeXagfpYiObpspTud0tZ
|
||||
Yf5Qs7g8GLkXfpkOJHG1+91Hi7+Pb1cCAqkkgIwFG4oeBdxAkxkaIyBRk3ZfnXYw
|
||||
9G1qofLrs2UbLUhIEyMgavI7kKwLg1pwsYs4Yn+RVR5hZPkj0ZUnc4gW7wCP
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,12 @@
|
||||
Traceback (most recent call last):
|
||||
File "/usr/bin/certbot", line 11, in <module>
|
||||
load_entry_point('certbot==0.10.2', 'console_scripts', 'certbot')()
|
||||
File "/usr/lib/python2.7/dist-packages/certbot/main.py", line 836, in main
|
||||
setup_logging(config)
|
||||
File "/usr/lib/python2.7/dist-packages/certbot/main.py", line 700, in setup_logging
|
||||
config, logfile=logfile, fmt=file_fmt)
|
||||
File "/usr/lib/python2.7/dist-packages/certbot/main.py", line 667, in setup_log_file_handler
|
||||
raise errors.Error(_PERM_ERR_FMT.format(error))
|
||||
Error: The following error was encountered:
|
||||
[Errno 13] Permission denied: '/var/log/letsencrypt/letsencrypt.log'
|
||||
If running as non-root, set --config-dir, --logs-dir, and --work-dir to writeable paths.
|
||||
@@ -0,0 +1,27 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
|
||||
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
|
||||
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
|
||||
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
|
||||
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
|
||||
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
|
||||
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
|
||||
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
|
||||
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
|
||||
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
|
||||
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
|
||||
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
|
||||
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
|
||||
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
|
||||
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
|
||||
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
|
||||
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
|
||||
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
|
||||
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
|
||||
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
|
||||
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,61 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF+TCCBOGgAwIBAgISA8rwfiC3p89WYm/X9RR3XhzTMA0GCSqGSIb3DQEBCwUA
|
||||
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
|
||||
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xODExMDkyMTEzNTNaFw0x
|
||||
OTAyMDcyMTEzNTNaMBMxETAPBgNVBAMTCGVuZGFvLmlvMIIBIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAQ8AMIIBCgKCAQEA2NIGtVlzW3fktmv74YG23LKLM6xIxn/mIPt4PgZ+
|
||||
K3407GryQueToAnG5iTXTox5UnyArvqxAe5cTonrlWTai9Do5CuhrKI22EMpwrSl
|
||||
Uo38dtNu2AHLENrY+aFzEwsVvkWGpIQ8/Y63T8M9ohiInF/S5ZPojIJxy7OVQ3io
|
||||
MdLbb1atYfBcnt0DFKyMC1yxDyYj8w1jvwQWlKGBYQeRd2MTWEjHO7CJPgiRwiLi
|
||||
rFBlDWnFz8GKntXLHI2pYL6+X4WgG9XfwzSN/w+ju48JAgCc6yTxvaQxQDpHst3D
|
||||
kv4CXNTP3gt2RSIOkQldBQzi/FgBLDv/qSuXAnLGsIgk5QIDAQABo4IDDjCCAwow
|
||||
DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAM
|
||||
BgNVHRMBAf8EAjAAMB0GA1UdDgQWBBThSBGBk0ZSYT1ptylvWQGZqdkBDjAfBgNV
|
||||
HSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcBAQRjMGEwLgYI
|
||||
KwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlwdC5vcmcwLwYI
|
||||
KwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlwdC5vcmcvMBMG
|
||||
A1UdEQQMMAqCCGVuZGFvLmlvMIH+BgNVHSAEgfYwgfMwCAYGZ4EMAQIBMIHmBgsr
|
||||
BgEEAYLfEwEBATCB1jAmBggrBgEFBQcCARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlw
|
||||
dC5vcmcwgasGCCsGAQUFBwICMIGeDIGbVGhpcyBDZXJ0aWZpY2F0ZSBtYXkgb25s
|
||||
eSBiZSByZWxpZWQgdXBvbiBieSBSZWx5aW5nIFBhcnRpZXMgYW5kIG9ubHkgaW4g
|
||||
YWNjb3JkYW5jZSB3aXRoIHRoZSBDZXJ0aWZpY2F0ZSBQb2xpY3kgZm91bmQgYXQg
|
||||
aHR0cHM6Ly9sZXRzZW5jcnlwdC5vcmcvcmVwb3NpdG9yeS8wggECBgorBgEEAdZ5
|
||||
AgQCBIHzBIHwAO4AdQB0ftqDMa0zEJEhnM4lT0Jwwr/9XkIgCMY3NXnmEHvMVgAA
|
||||
AWb6ieWoAAAEAwBGMEQCIE8+auAtQOLY4U1yoEDvCTQULv4PS9Xdo1uJzQF5nhBA
|
||||
AiBsfKR2UMxxIatzPOQPtuRrVEAv4+tX4o/ch5e6TAnOCAB1ACk8UZZUyDlluqpQ
|
||||
/FgH1Ldvv1h6KXLcpMMM9OVFR/R4AAABZvqJ6BEAAAQDAEYwRAIgKj2gwlC/9AIF
|
||||
hrx8nl2oEwxUoi/b4ZCCdQpz8JsJhOACIHRkuo56NErZR87lVtI0oZItZ6nzFPQz
|
||||
BFaxx89eBJKPMA0GCSqGSIb3DQEBCwUAA4IBAQCFURbxUFKAF3CJRiaUsRrjFDuc
|
||||
R25Dm86QgDqDLGkYf7/y5by2/amNzBbBLvcqUhScpjtalqO38Efw+2+c/UBGTQZ7
|
||||
C8vU5KrjG6bnOmoHx/t+ml4E88omMlEmlRIEiL/6yfpOnCCv+3IHJjUw8iPuH7+j
|
||||
57Vhwtq6mOvId2QfDfL86ELgXKvBKfbqac7YfzUHVyB4WeXagfpYiObpspTud0tZ
|
||||
Yf5Qs7g8GLkXfpkOJHG1+91Hi7+Pb1cCAqkkgIwFG4oeBdxAkxkaIyBRk3ZfnXYw
|
||||
9G1qofLrs2UbLUhIEyMgavI7kKwLg1pwsYs4Yn+RVR5hZPkj0ZUnc4gW7wCP
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
|
||||
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
|
||||
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
|
||||
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
|
||||
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
|
||||
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
|
||||
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
|
||||
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
|
||||
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
|
||||
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
|
||||
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
|
||||
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
|
||||
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
|
||||
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
|
||||
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
|
||||
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
|
||||
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
|
||||
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
|
||||
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
|
||||
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
|
||||
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDY0ga1WXNbd+S2
|
||||
a/vhgbbcsoszrEjGf+Yg+3g+Bn4rfjTsavJC55OgCcbmJNdOjHlSfICu+rEB7lxO
|
||||
ieuVZNqL0OjkK6GsojbYQynCtKVSjfx2027YAcsQ2tj5oXMTCxW+RYakhDz9jrdP
|
||||
wz2iGIicX9Llk+iMgnHLs5VDeKgx0ttvVq1h8Fye3QMUrIwLXLEPJiPzDWO/BBaU
|
||||
oYFhB5F3YxNYSMc7sIk+CJHCIuKsUGUNacXPwYqe1cscjalgvr5fhaAb1d/DNI3/
|
||||
D6O7jwkCAJzrJPG9pDFAOkey3cOS/gJc1M/eC3ZFIg6RCV0FDOL8WAEsO/+pK5cC
|
||||
csawiCTlAgMBAAECggEALGoxp5qQT+9qcZgDO/mnbORCMa6cJdVzXdRFmGcaF2y+
|
||||
CKQLxnrLud/m16Q6WsPZ4nTQo4eFFQEv9YK5OJw1TKjZ3Eu3zbZZB8oSVulMaHHf
|
||||
grPjI+qSH3zFL7XL7d26cYSqhS71k3dVw8gZ6wedjHLwr8ixvX7HMtQxwmWIBtRr
|
||||
Sj9zYyqkoBLDWrhoGh7GlAHHiPw4cFY7XRaAbq36JYhkO+VUK7BRJMMPwSSMbmx1
|
||||
S3O8PhXy5he893HVRAHhQq8xIup/Q+PktB39xgQX0M+mjMoqD9mJ0fgaKnff8K3U
|
||||
C3ovU9YmAR9VTGE/1S4yeT5BreXGbCwXKGSHO4KBLQKBgQDtZWjbtUCHyuiNk8pp
|
||||
17mgpWEZOHl7+hehO6uN2GwE4bdrBRxK3b+ZQRJqVRQnhQRkYA4x1vVOG05A0b2C
|
||||
QJBERd66XWMZHs3820kexo+Lw0XgAaM5bIoW8VZxAhHL9J49piiboawp/sNlFPVl
|
||||
HL0x5ztkP/7QHRwvInY9giVNAwKBgQDpz9S7N/wNwQf6NrqbboYpUkA8WcAjGdzi
|
||||
gsgK47rm2Nvs5CTWgVykTVokN/HjUCnkzNcET2e0lwF3M2Z6f3wyWiwngrGCWVyL
|
||||
YXgcMT080CPWvfpVZ/kjkOORsD4zhceVs/ECJDPwNH7rKderILxQld7BQ+PcY32n
|
||||
5iu4L3Cd9wKBgQDIUWsjAhX6v+BuHwiNOYicoxCFHJ9+WvF3jwdbAQVdNS31s3FF
|
||||
R0q2wi8M5M/F7Ttgi6FOswl5qBbnIVTdRSe3RJIGBmlpuBGvTUatHnXgRJ2AwUD/
|
||||
YrQ/WdRMNafYx4iDkuLvOIQzR5OPhxjvAkovguQd2tZkvDudFmJZ0qkxCwKBgAKK
|
||||
R31hNlPP7Q5C2fQmVz7Lrfb54qR++29ajGHR543qfTktoVjTPvZqALi0AuS9Ujcy
|
||||
IoPhePb1TQbGgC6EhAzn/eLQAK2x2teIz40+27N1b2490iJrZsGEKXTDvWlB4tE1
|
||||
i0DCs+3AJEDZy4YIbQNTHYBsBGW4jvzr+z2dBlQxAoGAZaQ8pL6wnaXqCQA/ULO+
|
||||
lcF5c98pOLHpNTE19zodDEpG1OmavHgDhThOXJYfogkeAJaoovaQ5hHS/YLkXVOU
|
||||
Xd2JN5FLbAwLN1jorB5tH1+c2SOOXaGoi7BTM9/mcUkJqYIthK4SoYw0oO2nphGZ
|
||||
Mm8KDWDzu7D3r3XB8Xpl8ng=
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCvieP8bQzFIRYe
|
||||
cPSgL6NLTeh8TqFcLzvPSHqI2HmVF1WiH2jYfbaJA1OZY5YHnqip0FAQ54Jw0V7F
|
||||
g7Mv1L3UhXLdD6EN6/muQnji4U/RW2AptNz9ooikRxJg91rZvWVvWYBikn8eq/Mw
|
||||
Osn6LMKlMsSzaH6YP7YsDHsh7dk4T6YWFTtph0nuttShvsFP1zHNungIsaNpkcUq
|
||||
sLW5p9JKHdsUWVcmliaV66cZVL3gFS695l1YkYsUw3VwAmIwYt7CwqnlhcoH16If
|
||||
qACD3S51Fpqc/Pc2AVr/yqO72IDrhglwM2v0CPTgQie1Fs8mbbzr5Osltp/nsRIJ
|
||||
LfZypoMTAgMBAAECggEAVrRic0WeACTWmxSqEBFXtBN4WSYxl6oQ/dLMC5n5fAX5
|
||||
m0mubPizV7vX6DUDXyIkJzSox6DCkl0oVaE2udJ4LWf5E23r3EeJnWRh95bY6Q0c
|
||||
U51tZq6IlbQxRMoJCvH9D2IDAy1fMtQ2G8V+DF9diWGpPQDJPwSBRAKqM1kbaS9E
|
||||
40zoKzzkYWQYHnNrgiLnUpT6LxVJrKQo4B/HxhoovlQ+Uu8oRbK+JdRvKTWQ7ELV
|
||||
evRtXxHSeq+22TdTysDfalOVWudaUSe2CqzX90mGzxerA7Baorb0Fas9nhTPFZus
|
||||
pqOYJPDUSU0SPj939lArlsAHCpUY2/NOK6tOeotQ0QKBgQDX7I/GmqiN4BXnmlBC
|
||||
fvix1BSQNs0d1fVMWiVXhgjUnTWc7oyEnE5tDkS6OyUKFHiK+xYxhy9rjis8ch+h
|
||||
CfyafADku6y3lG2QwktDA48cRavzmRZhwbbDqsRLIKRUqJyon8Xn4jsX0QTTHM75
|
||||
jJPSFLny0nsvIbQgixz7D0FrmQKBgQDQHnOIRvcYYGWV9kxGw+l9I+JNnX8RZWmD
|
||||
u9fZnqUQ01NtR6DHhmes73GtvRmhEeEQgICwpmBY65cUYHYkQ6WmhxdPGrSxLLCn
|
||||
539nvAhVs5WgFbEGsV7alFob1A2kbMWxnmvtk/mmaXyFf1D7jgDn9mabtsXlUVzk
|
||||
2OW+/mAviwKBgH0LGSQ5sn0fFXBflDU+FMXe/N2bbuPlYT4LQm7SR1DhjgfIugWe
|
||||
A2jyh2iWEdgpjLlnuS2LjTGrTmyd5qU7BFdukpHkAZz6zwyzCCTR7dHAo1jaAfUw
|
||||
2qerwCuFxsTkNH0Oseycvf4H5NRhbZdlCsHxkNIHhKEB0q+6AFmANzGZAoGBAK5r
|
||||
Zu4mc34Fg/3Lfp+sH2oWgdoEjfL5aBXUfxFOaQ6R3ZBvgGrX1NnVjxNJsUoRXuti
|
||||
lvf1aDf9sg+MzFaWLyVCgKF2Q0tdpewCEv/QUbfpmxOgOk3epx1PSbma3ZgY5RJ4
|
||||
MAHbi1YRgB+t1SQOHepJ0jLcWjxXFSBiOyH3tIsDAoGANQ6zOvNV/Mjkj82AJZGa
|
||||
bW21TcMWBZhCJZc7jI93S9oCJl+qp9eHjUVbsg6pf+8MWOmMtXFLgC/idJF8tB1E
|
||||
OzsW2xoKKSstj8JdAPsx8vR2kkLfkXPMT6ymOA7Lbc4ZwKSDbwo5xdAR8qBAnkic
|
||||
xIKG1OZD55i9MuHng2uBJBc=
|
||||
-----END PRIVATE KEY-----
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user