Let's take a look at Python line by line (token acquisition with Python, parameter store registration / update, test failure after deployment to success)

Hello. How are you?

When old videos are distributed on the net, You will see it with nostalgia. Nostalgia is just entertainment.

Previously about Serverless Framework, Python, etc. While seeing and listening to experts and being taught I remembered while experiencing various new things Those findings? Impressions? I would like to leave it here.

First, let's take a look at handler.py line by line.

There was such a description, what kind of content is it? When you hear Python, it reminds you of a muscular black boxer. That person is called Balrog in the overseas version, isn't it?

handler.py


from util import logging_decorator, build_response_decorator, get_logger

import app

logger = get_logger('INFO')

@logging_decorator
def xxxx_handler(event, context):
    return app._token(event)

from util import logging_decorator, build_response_decorator, get_logger --First, use the three functions logging_decorator, build_response_decorator, get_logger from the module named ʻutil (literally translated unit: util.py is pre-created here). Declares -[util is an abbreviation for utility, which is a name that is often given to modules that define general-purpose functions that are customarily used in projects](https://teratail.com/questions) / 233152) That's right --In this case, ʻutil.py is stored in/ layers / python /(the contents of util.py will be omitted). ――When ʻutil.pyis in a different hierarchy fromhandler.py, why can you call it without writing the path to the file? --This seems to involve ʻAWS Lambda Layers, AWS Lambda Layers eliminates the need to include libraries in a package by creating common content such as libraries as layers -Last time, I wrote this vaguely $ {self: custom.requirements_layer} ――The library to be called was stored in a common layer (literally "layer" and "hierarchy"). ――It takes time other than deploying (uploading the settings and files required for AWS), so subdividing in this way will shorten the time. -Differences in modules, packages and libraries

import app --This is what is described below. In the same hierarchy

logger = get_logger('INFO') --This is a way to track events that happened when the software is running ――In this, ʻINFO seems to be confirmation that what was expected happened`.

@logging_decorator --@ is a decorator (the person in charge of decorating the sales floor, showcase, and show window), and [a function for adding or changing processing to an existing function]( https://www.sejuku.net/blog/25130#:~:text=%E3%83%87%E3%82%B3%E3%83%AC%E3%83%BC%E3%82%BF% E3% 81% A8% E3% 81% AF% E3% 80% 81% E3% 81% 99% E3% 81% A7% E3% 81% AB% E3% 81% 82% E3% 82% 8B,% E5 % 87% BA% E6% 9D% A5% E3% 82% 8B% E3% 82% 88% E3% 81% 86% E3% 81% AB% E3% 81% AA% E3% 82% 8A% E3% 81 It looks like% BE% E3% 81% 99% E3% 80% 82) -[To be able to add or change functions to existing functions without directly changing the contents](https://www.sejuku.net/blog/25130#:~:text =% E3% 83% 87% E3% 82% B3% E3% 83% AC% E3% 83% BC% E3% 82% BF% E3% 81% A8% E3% 81% AF% E3% 80% 81% E3% 81% 99% E3% 81% A7% E3% 81% AB% E3% 81% 82% E3% 82% 8B,% E5% 87% BA% E6% 9D% A5% E3% 82% 8B% E3 % 82% 88% E3% 81% 86% E3% 81% AB% E3% 81% AA% E3% 82% 8A% E3% 81% BE% E3% 81% 99% E3% 80% 82) -It is convenient in that you can change the process in the middle of the function as you like by converting it into a function and specifying the function as an argument. —— Make a removable clip (@decorator) for the ribbon or heart first, and then attach the removable clip (@decorator) for the ribbon or heart to a piece of clothing to make it look like a woman's outfit. Is it something you can tell (usually you have to say "this is women's clothing")? Is it wrong, or is it confusing? What kind of clothes do you usually have to say, "This is women's clothes"?

def xxxx_handler(event, context): --def is an abbreviation for define in English. That's right (it was mistaken for Default). --xxxx_handler and the arguments event and context are called in the defined function

return app._token(event) --The return statement is an image of pressing the enter key as a turn. --Turn the enter key with event as an argument with the _token function in app.py! (I'm tired of researching a little) --_token Why is this prefixed with _? -[_function (x): # You can define a function as "internal" by adding an underscore before the function. It's similar to the Private attribute in other programming languages, but Python has virtually no Private attribute. In the explanation of PEP8, it is explained as weak internal use, and when from M import *, the function starting with one underscore is not imported, but if it is a function in class, call the function with classx._func () Can do](https://medium.com/lsc-psd/pythonic%E8%89%B2%E3%80%85-python%E3%81%AE%E3%82%A2%E3%83%B3 % E3% 83% 80% E3% 83% BC% E3% 82% B9% E3% 82% B3% E3% 82% A2-% E3% 82% 92% E4% BD% BF% E3% 81% 84% E3% 81% 93% E3% 81% AA% E3% 81% 9D% E3% 81% 86-3c132842eeef) That's right. -_single_leading_underscore: Indicates "use only internally". For example, from M import * does not import objects with names that start with an underscore (Thank you for pointing this out). )


Next, let's take a look at app.py line by line.

There was such a description. Python used for Serverless Framework. I've also made it quite hidden (xxx), so I'm sorry if the variable flow is wrong. For the processing content, use the specified ID and password, get the token information (json format) from the specified location, and It is called , which extracts only the token information and registers it in the parameter store.

app.py


import json
import boto3
import requests
import os

def _token(event):
    
    try:
        # ===================================================================
        #Token acquisition process
        # ===================================================================
        
        #Use boto3 and use parameter store
        client = boto3.client('ssm')

        #Get secret key
        # get_Get the value of the parameter with parameter
        response_get = client.get_parameter(
            Name = '/xxx/xxxx/xxxx',
            WithDecryption = True
        )
        client_secret_get = response_get['Parameter']['Value']

        #List of keys required to get tokens
        CLIENT_SECRET = xxxxx_xxxx_get
        CLIENT_ID = os.environ['XXXX']
        XXXX_URL = "https://xxxxx/xxxx"

        #That summer we got the key
        xxxx_xxxx_key = {
            'xxxx_secret': XXXX_SECRET,
            'xxxx_id': XXXX_ID,
        }

        #Access the token information and enter the required information
        response_xxxx = requests.post(XXXX_URL, data=xxxx_xxxx_key)

        #Output token information
        #The result is in JSON format, so decode it
        xxxx_data = json.loads(response_xxxx.text)

        #Extract only the access token value from the token information
        xxxx_xxxx_data = xxxx_data.get('access_token')

        # ===================================================================
        #The token information obtained above- AWS Systems Manager -Send to parameter store
        # ===================================================================

        # put_Update parameter value with parameter
        response_put = client.put_parameter(
            Name = '/xxxx/xxxx',
            Value = xxxx_xxxx_xxxx,
            Type = 'SecureString',
            Overwrite = True
        )

import json -JSON JavaScript Object Notation, a type of data format encoder and decoder

import boto3 --The boto used here named after the freshwater dolphins native to the Amazon That's right. --boto3 says Using the AWS SDK for Python (Boto3), you can start using AWS quickly. --The SDK is a package of programs, APIs, sample code, etc. so that you can develop applications with less effort / 07 / news06.html)

import requests --Requests is Python's HTTP communication library, which makes it easy to get information on websites and collect images That's right. --Also, Python has a library called urllib as standard, but Requests is simpler than that, and you can write programs in a way that is easy for people to see and understand / 19195) That's right. Hmm! It's a good thing

import os -Mainly file and directory operations are possible, you can get a list and path of files, and you can create a new file directory Yes is

def _token(event): ――I remember def earlier --This is the function that handler.py is trying to call. --event is a formal argument name of variable to put in parentheses when defining function

 try:

--There is a try-except statement, and ["Exception handling: Processing when an error may occur at runtime even if you write grammatically correct code"](https: // www.sejuku.net/blog/23044) That's right --Actually, the following is added as "error handling" after the try: description (the contents here are omitted here, someday ...)

#Error handling
    except Exception as error:
        data = {'request': event}
        logger.exception(error, extra=dict(data))

        return_params = {
            'message': 'An error has occurred. Please try the operation again.{}'.format(str(error))
        }
        return {
            'statusCode': 500,
            'body': return_params
        }

--In other words, If an exception occurs in the code written in the try block, the process written in the except block is executed Seems to be

     client = boto3.client('ssm')

--The boto3.client here means [when using the Client API, first call boto3.client ('service name = ssm: Amazon Simple Systems Manager')" (https://dev.classmethod. jp / articles / boto3-client-api-and-resource-api /) --The official documentation for boto3 A low-level client representing Amazon Simple Systems Manager (SSM): --By the way, the low level API that appears here is Low level → You can specify small things (= you need to control fine branches yourself), High level → You only have to specify what you want to do ( = It is used to mean (you cannot specify details) That's right.

     response_get = client.get_parameter(

――First of all, I went to the parameter store screen in advance and registered the key I received in advance. --↓↓↓↓ It's the orange part here.

image.png

――We will create it on such a screen (the name is easy to understand, the explanation is easy to understand, and the key is written in the value field with a standard and safe character string)

image.png

--After that, the registered key (JIjlksjdfaijklaIJ + IJ ... a character string like this) is fetched with the get_parameter function.

         Name = '/xxx/xxxx/xxxx',

--Write your registered name here ――The reason why / is written here is that you put / in the name, it does not overlap with other registered keys, and it is a notation that this key belongs to the hierarchy. To make it easier to understand

         WithDecryption = True

--Here, [Returns the value for the parameter name that is compounded by WithDecryption = True and stored in the parameter store](https://dev.classmethod.jp/articles/get-data-from-system-manager-parameter -store-using-boto3-ja /) It seems --Since it was encrypted when it was registered, it will be decrypted here "Decrypt" is a word that refers to the operation itself, so it is not called "decrypt". Don't do it

     )

--Closed parentheses

     client_secret_get = response_get['Parameter']['Value']

-Why split the list with ['Parameter'] ['Value']? ――There is not much information (what kind of word do you look up?), But [['spam','ham'] + ['egg'] # Combine list](https://pycamp.pycon.jp/textbook / 4_collections.html # use-list) --I haven't tried it, but ['Parameter','Value'] I thought this was fine, but here it means ['Parameter' to get the Valuepart ofinParameter'. ] ['Value']and the list may be separated --Other ['Parameter'] [0] ['Value'] I found such a description, but this is ['spam','ham','egg'] [0] #list Get the 0th of -[Parameters are organized hierarchically, so it makes sense for me to access them like a dictionary in a dictionary](https://medium.com/@nqbao/how-to-use-aws- It looks like ssm-parameter-store-easily-in-python-94fda04fea84) -[Output the key value of section1](https://qiita.com/kikuchiTakuya/items/53990fca06fb9ba1d8a7#%E3%81%A8%E3%82%8A%E3%81%82%E3%81%88 % E3% 81% 9A% E4% BD% 95% E3% 81% 8B% E5% 80% A4% E3% 82% 92% E5% 8F% 96% E3% 82% 8A% E5% 87% BA% E3 % 81% 97% E3% 81% A6% E3% 81% BF% E3% 82% 8B) It seems that the list is divided.

     CLIENT_SECRET = xxxxx_xxxx_get

--Here, set the secret key obtained above to a variable called CLIENT_SECRET (number even though it is a character).

     CLIENT_ID = os.environ['XXXX']

--Here we use the first imported ʻos` -os.environ () is used to get, write to and overwrite environment variables That's right. --On the Lambda function screen, the ID written in the environment variable that is deployed and written in serverless.yml is obtained here (XXXX actually has the environment variable name written).

     XXXX_URL = "https://xxxxx/xxxx"

--I put the URL of the token location in a variable called XXXX_URL

     xxxx_xxxx_key = {
         'xxxx_secret': XXXX_SECRET,
         'xxxx_id': XXXX_ID,

--Here, we use the dictionary type of the list and put the keys together in the variable xxxx_xxxx_key. --It is not good for security to write the secret key and ID required to get the token in the same place (hard coding: [It is better to write the process and value separately in another place Source code Write it directly in [https://wa3.i-3-i.info/word12866.html)), so divide the secret key into the parameter store and the ID into the Lambda environment variable. Since I registered, I need a description to get them

     }

-}: Curly braces (Namikakko), [Currently, "curly braces" are preferable to the term "curly braces"](https://business-textbooks.com/type-of -parenthesis / # toc-7)

     response_xxxx = requests.post(XXXX_URL, data=xxxx_xxxx_key)

--Send form-encoded data, like an HTML form. To do this, simply pass a dictionary as the data argument. The dictionary of data is automatically form-encoded when a request is made --I put multiple keys in the dictionary type, make it a variable called xxxx_xxxx_key, and pass that variable as an argument of data.

     xxxx_data = json.loads(response_xxxx.text)

――You can actually get the token here, but it was in so-called JSON format. -Json loads decodes (converts to a manageable type) a string That's right. --So in anticipation of that, here we use the json.loads function of ʻimport jsonto call it on one line. --Why do you add.text` after response_xxxx? -You can get the response body in text format by using .text for the response -The response body is the place where "the contents of the file that the other party wanted" is written ――By the way, I was surprised that json.load and json.loads are different. json.dump and json.dumps are also different.

     xxxx_xxxx_data = xxxx_data.get('access_token')

--Get the contents written as ʻaccess_tokenfrom xxxx_data --For the.getpart, [you can use the get () method to get an arbitrary value (default value) without causing an error if the key does not exist](https://note.nkmk. me / python-dict-get /) --[Returns the value for key if key is in the dictionary, default otherwise. If no default is given, it defaults to None. So this method never throws a KeyError](https://docs.python.org/ja/3/library/stdtypes.html#dict.get) ――Why don't you wantKeyError` to appear? I don't want it to appear because it's an error -The program is forcibly terminated when KeyError is output Yes, I don't want it to appear.

     response_put = client.put_parameter(

――This time, put (put, paste) the contents that you got at the top. ――The mechanism is the same as get_parameter.

         Name = '/xxxx/xxxx',

――The name of the parameter store you want to put (/ is included, but it is not the URL but the naming rule decided here)

         Value = xxxx_xxxx_xxxx,

--Write an easy-to-understand explanation here

         Type = 'SecureString',

--This is interesting and if it is String, the character will be sent as it is, but if it is SecureString, it will be *****. ――It's security

image.png

         Overwrite = True

――This time, we are rewriting the token once a week, so overwriting is OK.

     )

――Why isn't this a curly brace? -[] Is a list, () is a tuple, {} is a dictionary -[Use square brackets ([]) to define the list and separate the elements to include with commas (,) ['spam','egg', 0.5]](https://pycamp.pycon. jp / textbook / 4_collections.html) -[Use parentheses (()) to define tuples and separate the elements to include with commas (,) ('spam','ham', 4)](https://pycamp.pycon .jp / textbook / 4_collections.html) -Tuples can be used much like lists, except that you can't change elements. -Tuples are used for function return values and values for settings that you want to be immutable -Dictionary {...}, unlike lists, does not have an order for each element. It has a key and a corresponding value instead -[To define a dictionary, enclose each element in curly braces ({}) and write the key and value in a colon (:). Separate the value with the next key with a comma (,) ʻuser_info = {'user_name':'taro','last_name':'Yamada'} `](https://pycamp.pycon.jp/textbook/4_collections .html) seems ――In other words, the previous one is in dictionary format, and this is in tuple format (elements that do not require keys and you want to make them immutable).


At the end `It was long this time too! ``

I quoted and referred to the link on this page, We would like to thank the authors of the official documents and articles. Thank you very much.

Well, at this stage, something like this happened next. No matter how many times I tried, after deploying, the test on the Lambda screen failed!

Why did the test on the Lambda screen fail?

The Python version I had set in Lambda (ie serverless.yml) was 3.8, When I was editing locally, the Python environment was 3.7. I thought it would switch to the latest when it was deployed (away from the local), but it wasn't. I set the Python version of my local environment to 3.8 and deployed it and it worked fine. Please be careful as well.

Recommended Posts

Let's take a look at Python line by line (token acquisition with Python, parameter store registration / update, test failure after deployment to success)
Send a message to LINE with Python (LINE Notify)
Read line by line from a file with Python