ЁЯз▒ рдзрдбрд╛ 02 тАФ Resources рдЖрдгрд┐ providers: рдЖрд░рд╛рдЦрдбреНрдпрд╛рдд рдХрд╛рдп рд▓рд┐рд╣рд┐рддрд╛ рдпреЗрддреЗ
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: рдзрдбрд╛ 02 / 12 ┬╖ рдорд╛рдЧреЗ: lesson-01-why-iac ┬╖ рдкреБрдвреЗ: lesson-03-plan-apply
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбрд╛ 01, рдЖрдгрд┐ рдЖрд░рд╛рдЦрдбреНрдпрд╛рдЪрд╛ рд╢рдмреНрджрд╕рдВрдЧреНрд░рд╣. рдПрдХ resource block campus рд╡рд░реАрд▓ рдПрдХрд╛ рдЧреЛрд╖реНрдЯреАрдЪреЗ
рд╡рд░реНрдгрди рдХрд░рддреЛ. рдПрдХрд╛ provider рд▓рд╛ рддреНрдпрд╛ рдкреНрд░рдХрд╛рд░рдЪреА рдЧреЛрд╖реНрдЯ рдХрд╢реА рдмрд╛рдВрдзрд╛рдпрдЪреА рддреЗ рдорд╛рд╣реАрдд рдЕрд╕рддреЗ рдЖрдгрд┐ рддреЛ рдПрдХ
schema рдкреНрд░рд╕рд┐рджреНрдз рдХрд░рддреЛ: рдХреЛрдгрддреЗ arguments required рдЖрд╣реЗрдд, рдХреЛрдгрддреНрдпрд╛рдВрдирд╛ defaults рдЖрд╣реЗрдд, рдХреЛрдгрддреЗ campus
рд╕реНрд╡рддрдГ рднрд░рддреЛ (computed, рдЬрд╕реЗ id), рдЖрдгрд┐ рдХреЛрдгрддреЗ рдирд╡реАрди object рдмрд╛рдВрдзрд▓реНрдпрд╛рд╢рд┐рд╡рд╛рдп рдмрджрд▓рддрд╛ рдпреЗрдд рдирд╛рд╣реАрдд
(ForceNew). iac/demo.py рдордзреАрд▓ resources(), iac/cloud.py рдордзреАрд▓ SCHEMAS рдЖрдгрд┐
FakeCloud, iac/engine.py рдордзреАрд▓ to_hcl() рдЖрдгрд┐ validate().
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
рдЖрд░рд╛рдЦрдбрд╛ рдПрдХрд╛ рдЦрд╛рд╕ form рд╡рд░ рд▓рд┐рд╣рд┐рд▓рд╛ рдЬрд╛рддреЛ. ЁЯУЛ Form рд╡рд░реАрд▓ рдкреНрд░рддреНрдпреЗрдХ рдЪреМрдХрдЯ рдХрд╛рдп рдмрд╛рдВрдзрд╛рдпрдЪреЗ рддреЗ рд╕рд╛рдВрдЧрддреЗ:
Room called
lab┬╖ name: chem-lab ┬╖ floor: 2 ┬╖ seats: 30
рдЦреЛрд▓реНрдпрд╛ рдмрд╛рдВрдзрдгрд╛рд▒реНрдпрд╛ рдХрдВрддреНрд░рд╛рдЯрджрд╛рд░рд╛рдХрдбреЗ рдПрдХ рдирд┐рдпрдо-рдкреБрд╕реНрддрдХ рдЕрд╕рддреЗ. рддреНрдпрд╛рдд рд▓рд┐рд╣рд┐рд▓реЗрд▓реЗ рдЕрд╕рддреЗ:
- name тАФ рд╣реЗ рддреБрдореНрд╣реА рд▓рд┐рд╣рд╛рдпрд▓рд╛рдЪ рд╣рд╡реЗ. ЁЯЦКя╕П
- floor тАФ рд░рд┐рдХрд╛рдореЗ рд╕реЛрдбрд▓реЗ рддрд░ floor 1. рдЖрдгрд┐ рдЦреЛрд▓реА рдПрдХрджрд╛ рдмрд╛рдВрдзрд▓реА рдХреА рддреБрдореНрд╣реА рддреА рджреБрд╕рд▒реНрдпрд╛ floor рд╡рд░ рд╣рд▓рд╡реВ рд╢рдХрдд рдирд╛рд╣реА. рддреНрдпрд╛рд╕рд╛рдареА рдирд╡реА рдЦреЛрд▓реА рд▓рд╛рдЧреЗрд▓. ЁЯз▒
- seats тАФ рдХрд╛рд╣реА рди рд╕рд╛рдВрдЧрд┐рддрд▓реНрдпрд╛рд╕ 30. рд╣реЗ рдирдВрддрд░ рдмрджрд▓рддрд╛ рдпреЗрддреЗ.
- id тАФ рд╣реЗ рд▓рд┐рд╣реВ рдирдХрд╛. рдЦреЛрд▓реА рдмрд╛рдВрдзрд▓реА рдХреА campus рдкреНрд░рддреНрдпреЗрдХ рдЦреЛрд▓реАрд▓рд╛ рддрд┐рдЪрд╛ рд╕реНрд╡рддрдГрдЪрд╛ рдирдВрдмрд░ рджреЗрддреЛ, рдЬрд╕реЗ
room-01. ЁЯП╖я╕П
Gate рдЪреНрдпрд╛ form рдордзреНрдпреЗ room рдирд╛рд╡рд╛рдЪреА рдПрдХ рдЪреМрдХрдЯ рдЕрд╕рддреЗ. рддрд┐рдереЗ рддреБрдореНрд╣реА рдирдВрдмрд░ рд▓рд┐рд╣реАрдд рдирд╛рд╣реА. рддреБрдореНрд╣реА "lab рдЪрд╛ id" рдЕрд╕реЗ рд▓рд┐рд╣рд┐рддрд╛, рдЖрдгрд┐ lab рддрдпрд╛рд░ рдЭрд╛рд▓реНрдпрд╛рд╡рд░ рдХрдВрддреНрд░рд╛рдЯрджрд╛рд░ рдЦрд░рд╛ рдирдВрдмрд░ рднрд░рддреЛ.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
plans["ЁЯУЬ the plans<br/>resource school_room lab<br/>name, floor, seats"]
schema["ЁЯУЦ provider schema<br/>name: required<br/>floor: default 1, ForceNew<br/>seats: default 30<br/>id: computed"]
prov["ЁЯС╖ provider<br/>(a plugin)"]
api["ЁЯПл campus API<br/>POST /rooms"]
plans --> prov
schema --- prov
prov -->|"create"| api
api -->|"id = room-01"| prov
ЁЯЧ║я╕П рд░реЗрдЦрд╛рдЯрд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/terraform/lesson-diagrams.html#l02
тЭУ рдХрд╛рдп
- Resource block тАФ
resource "TYPE" "NAME" { ... }.TYPEрд╕рд╛рдВрдЧрддреЗ рдХреЛрдгрддреНрдпрд╛ рдкреНрд░рдХрд╛рд░рдЪреА рдЧреЛрд╖реНрдЯ (aws_s3_bucket,school_room),NAMEрд╣реЗ рддрд┐рдЪреНрдпрд╛рд╕рд╛рдареА рддреБрдордЪреЗ рд╕реНрдерд╛рдирд┐рдХ рд▓реЗрдмрд▓ (lab). рджреЛрдиреНрд╣реА рдорд┐рд│реВрди address рдмрдирддреЛ:school_room.lab. рдирд╛рд╡ рдлрдХреНрдд рдЖрд░рд╛рдЦрдбреНрдпрд╛рд╕рд╛рдареА рдЕрд╕рддреЗ; рдЦрд▒реНрдпрд╛ object рд▓рд╛ cloud рдХрдбреВрди id рдорд┐рд│рддреЛ. - Arguments тАФ block рдЪреНрдпрд╛ рдЖрдд рддреБрдореНрд╣реА рдЬреЗ рдард░рд╡рддрд╛ рддреЗ (
name = "chem-lab"). - Attributes тАФ object рддрдпрд╛рд░ рдЭрд╛рд▓реНрдпрд╛рд╡рд░ рддреНрдпрд╛рдЪреНрдпрд╛рдХрдбреЗ рдЕрд╕рд▓реЗрд▓реЗ рд╕рд░реНрд╡ рдХрд╛рд╣реА, cloud рдирд┐рд╡рдбрддреЗ рддреЗ
computed рд╕реБрджреНрдзрд╛ (
id, рдПрдХ ARN, рдПрдХ IP address). - Reference тАФ рджреБрд╕рд▒реНрдпрд╛ block рдордзреАрд▓
school_room.lab.idрдЪрд╛ рдЕрд░реНрде "lab рддрдпрд╛рд░ рдЭрд╛рд▓реНрдпрд╛рд╡рд░ рддрд┐рдЪрд╛ id". рдЦрд▒реНрдпрд╛ HCL рдордзреНрдпреЗ рддреБрдореНрд╣реА рддреЗ рд╕рд░рд│ рд▓рд┐рд╣рд┐рддрд╛:room_id = school_room.lab.id. Lab рддреЗ Python string рдордзреНрдпреЗ"${school_room.lab.id}"рдЕрд╕реЗ рд▓рд┐рд╣рд┐рддреЗ; рдЕрд░реНрде рддреЛрдЪ. - Provider тАФ рдПрдХ API рдУрд│рдЦрдгрд╛рд░рд╛ plugin:
hashicorp/aws,hashicorp/google,hashicorp/kubernetes,cloudflare/cloudflareрдЖрдгрд┐ Terraform Registry рд╡рд░реАрд▓ рдЖрдгрдЦреА рд╣рдЬрд╛рд░реЛ. Terraform core planning рдХрд░рддреЗ; provider create, read, update рдЖрдгрд┐ delete calls рдХрд░рддреЛ. - Schema тАФ рдкреНрд░рддреНрдпреЗрдХ resource type рд╕рд╛рдареА provider рдЪреЗ рдирд┐рдпрдо-рдкреБрд╕реНрддрдХ: argument names, types, required рдХреА optional, defaults, computed, ForceNew (рдмрджрд▓рд▓реЗ рддрд░ replace), sensitive (output рдордзреНрдпреЗ рд▓рдкрд╡рд╛).
- Data source тАФ
data "TYPE" "NAME"рдЖрдзреАрдЪ рдЕрд╕реНрддрд┐рддреНрд╡рд╛рдд рдЕрд╕рд▓реЗрд▓реА рдЧреЛрд╖реНрдЯ рд╡рд╛рдЪрддреЗ, рддрд┐рдЪреЗ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрди рди рдХрд░рддрд╛ (рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, рд╕рд░реНрд╡рд╛рдд рдирд╡реАрди machine image). Lab рдордзреНрдпреЗ рдПрдХрд╣реА рдирд╛рд╣реА; рдзрдбрд╛ 09 import рдХрд░рдгреНрдпрд╛рдЪреА рд╕рдВрдмрдВрдзрд┐рдд рдХрд▓реНрдкрдирд╛ рджрд╛рдЦрд╡рддреЛ.
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг schema рдореБрд│реЗрдЪ рдХрдВрддреНрд░рд╛рдЯрджрд╛рд░ рди рдмрд╛рдВрдзрддрд╛ plan рдХрд░реВ рд╢рдХрддреЛ. рддреНрдпрд╛рд▓рд╛ рдорд╛рд╣реАрдд рдЕрд╕рддреЗ рдХреА
seats рдЬрд╛рдЧреАрдЪ рдмрджрд▓реВ рд╢рдХрддреЗ рдкрдг floor рдирд╛рд╣реА, рдЖрдгрд┐ id campus рдХрдбреВрди рдпреЗрддреЛ. рддреЛ
рдХреЛрдгрддрд╛рд╣реА API call рдХрд░рдгреНрдпрд╛рдЖрдзреА рдЪреВрдХ рдирд╛рдХрд╛рд░реВ рд╢рдХрддреЛ: рдЕрдиреЛрд│рдЦреА argument, рд╣рд░рд╡рд▓реЗрд▓рд╛ required argument,
рдЖрдХрдбреНрдпрд╛рдЪреНрдпрд╛ рдЬрд╛рдЧреА рдордЬрдХреВрд░. Provider рд╡реЗрдЧрд│рд╛ рдЕрд╕рд▓реНрдпрд╛рдореБрд│реЗ Terraform core рддреЗрдЪ рд░рд╛рд╣рддреЗ
рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ API рд╕рд╛рдареА рдПрдХ plugin рдмреЛрд▓рдгреНрдпрд╛рдЪреЗ рдХрд╛рдо рдХрд░рддреЛ.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
iac/cloud.py рдордзреАрд▓ SCHEMAS рд╣реЗ рддреАрди types рд╕рд╛рдареАрдЪреЗ provider schema
рдЖрд╣реЗ. with_defaults() рдЖрд░рд╛рдЦрдбреНрдпрд╛рдиреЗ рд╕реЛрдбрд▓реЗрд▓реЗ defaults рднрд░рддреЗ. FakeCloud.create()
object рд╕рд╛рдард╡рддреЗ, room-01 рд╕рд╛рд░рдЦрд╛ id рдирд┐рд╡рдбрддреЗ, рдЖрдгрд┐ API call cloud.calls рдордзреНрдпреЗ рдиреЛрдВрджрд╡рддреЗ.
Engine рдЪреЗ validate() config рд▓рд╛ SCHEMAS рд╡рд┐рд░реБрджреНрдз рддрдкрд╛рд╕рддреЗ. to_hcl() config dict
HCL рдореНрд╣рдгреВрди print рдХрд░рддреЗ, рдореНрд╣рдгрдЬреЗ рддреБрдореНрд╣реА рддреЗ рдЦрд▒реНрдпрд╛ .tf file рд╢реА рддрд╛рдбреВрди рдкрд╛рд╣реВ рд╢рдХрддрд╛.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
python3 iac/demo.py resources
python3 - <<'EOF'
import sys; sys.path.insert(0, "iac")
from cloud import FakeCloud, with_defaults
from engine import plan, apply, validate
from demo import CAMPUS
print("defaults:", with_defaults("school_locker", {"room_id": "room-01", "label": "a"}))
cloud = FakeCloud(); st, steps, msg = apply(plan(CAMPUS, None, cloud), cloud)
print(msg)
for call in cloud.calls: print(" ", call)
print("gate points at:", st["resources"]["school_gate.main"]["attrs"]["room_id"])
print(validate({"resource": {"school_room": {"x": {"name": "x", "id": "room-99"}}, "school_pool": {"p": {}}}}))
EOF
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
resources рдЕрд╕реЗ print рдХрд░рддреЗ:
name required ┬╖ string
floor default 1 ┬╖ number ┬╖ forces a new room if changed
seats default 30 ┬╖ number
tags default {} ┬╖ map
id computed ┬╖ string
тФАтФА apply тЖТ Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
the provider called the campus API: ['POST /rooms тЖТ room-01']
рддреБрдордЪрд╛ snippet рдЕрд╕реЗ print рдХрд░рддреЛ:
defaults: {'room_id': 'room-01', 'label': 'a', 'size': 'm'}
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
POST /rooms тЖТ room-01
POST /gates тЖТ gate-01
POST /lockers тЖТ locker-01
POST /lockers тЖТ locker-02
POST /lockers тЖТ locker-03
gate points at: room-01
['school_room.x: An argument named "id" is not expected here.', 'Invalid resource type: the provider does not support resource type "school_pool".']
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
рдЖрд░рд╛рдЦрдбреНрдпрд╛рдиреЗ рдХрдзреАрдЪ room-01 рдореНрд╣рдЯрд▓реЗ рдирд╛рд╣реА; campus рдиреЗ рддреЗ рдирд┐рд╡рдбрд▓реЗ, рдЖрдгрд┐ рдЦреЛрд▓реА рддрдпрд╛рд░ рдЭрд╛рд▓реНрдпрд╛рд╡рд░ gate рдЪрд╛ room_id
рддреНрдпрд╛рдиреЗ рднрд░рд▓рд╛ рдЧреЗрд▓рд╛. рдЦреЛрд▓реА gate рдЖрдгрд┐ lockers рдЪреНрдпрд╛ рдЖрдзреА рддрдпрд╛рд░ рдЭрд╛рд▓реА
(рдХрд╛ рддреЗ рдзрдбрд╛ 06 рд╕рдордЬрд╛рд╡рддреЛ). рдЖрдгрд┐ validate рдиреЗ campus рд▓рд╛ рдЕрдЬрд┐рдмрд╛рдд call рди рдХрд░рддрд╛ рджреЛрди рдЪреБрдХрд╛
рдирд╛рдХрд╛рд░рд▓реНрдпрд╛: id computed рдЖрд╣реЗ, рдореНрд╣рдгреВрди рдЖрд░рд╛рдЦрдбрд╛ рддреЗ рдард░рд╡реВ рд╢рдХрдд рдирд╛рд╣реА, рдЖрдгрд┐ school_pool рд╣рд╛
provider рд▓рд╛ рдорд╛рд╣реАрдд рдЕрд╕рд▓реЗрд▓рд╛ type рдирд╛рд╣реА.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- reference рдРрд╡рдЬреА id hard-code рдХрд░рдгреЗ (
room_id = "room-01") тАФ рдордЧ рдЖрд░рд╛рдЦрдбреНрдпрд╛рд▓рд╛ рдХреНрд░рдо рдХрд│рдд рдирд╛рд╣реА, рдЖрдгрд┐ рдкреБрдиреНрд╣рд╛ рдмрд╛рдВрдзрд▓реЗрд▓реА рдЦреЛрд▓реА gate рдмрд┐рдШрдбрд╡рддреЗ - resource рдЪреЗ рдирд╛рд╡ (
lab, рдЖрд░рд╛рдЦрдбреНрдпрд╛рддреАрд▓ рдПрдХ рд▓реЗрдмрд▓) рдЖрдгрд┐ object рдЪрд╛nameargument рдХрд┐рдВрд╡рд╛ рддреНрдпрд╛рдЪрд╛ id рдпрд╛рдВрдд рдЧрд▓реНрд▓рдд рдХрд░рдгреЗ - provider versions pin рди рдХрд░рдгреЗ тАФ рдирд╡реА major version schemas рдЖрдгрд┐ defaults рдмрджрд▓реВ рд╢рдХрддреЗ
- computed attribute рдард░рд╡рдгреНрдпрд╛рдЪрд╛ рдкреНрд░рдпрддреНрди рдХрд░рдгреЗ
- рдЬрд┐рдереЗ data source рдпреЛрдЧреНрдп рдЖрд╣реЗ рддрд┐рдереЗ resource рд╡рд╛рдкрд░рдгреЗ (рддреБрдореНрд╣рд╛рд▓рд╛ shared network рд╡рд╛рдЪрд╛рдпрдЪреЗ рдЖрд╣реЗ, рддреНрдпрд╛рдЪреЗ рдорд╛рд▓рдХ рд╡реНрд╣рд╛рдпрдЪреЗ рдирд╛рд╣реА)
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд
рдЦрд▒реНрдпрд╛ account рд╡рд░ тАФ рдЦрд░рд╛ provider рдЖрдгрд┐ рдЦрд░рд╛ resource. terraform init provider download
рдХрд░рддреЗ рдЖрдгрд┐ рдиреЗрдордХреА version .terraform.lock.hcl рдордзреНрдпреЗ рдиреЛрдВрджрд╡рддреЗ (рддреА file commit рдХрд░рд╛):
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0" # any 6.x, never 7.0 by surprise
}
}
}
provider "aws" {
region = "ap-south-1"
}
resource "aws_s3_bucket" "timetables" {
bucket = "school-timetables-example" # changing a bucket's name forces a new bucket
tags = { owner = "works-office" }
}
resource "aws_s3_bucket_versioning" "timetables" {
bucket = aws_s3_bucket.timetables.id # a reference: plain HCL, no quotes
versioning_configuration { status = "Enabled" }
}
terraform init # installs hashicorp/aws 6.x, writes .terraform.lock.hcl
terraform providers # which providers this config needs
terraform validate # "Success! The configuration is valid."
registry.terraform.io рд╡рд░реАрд▓ provider documentation рдореНрд╣рдгрдЬреЗ рд╡рд╛рдЪрддрд╛ рдпреЗрдгреНрдпрд╛рдЬреЛрдЧреНрдпрд╛ рд╕реНрд╡рд░реВрдкрд╛рддреАрд▓ schema: рдкреНрд░рддреНрдпреЗрдХ argument рд╕рд╛рдареА рддреЗ Required/Optional рд╕рд╛рдВрдЧрддреЗ рдЖрдгрд┐ рдмрджрд▓рд╛рдореБрд│реЗ рдирд╡реАрди resource рдХреЗрд╡реНрд╣рд╛ рд▓рд╛рдЧрддреЛ рддреЗ рдиреЛрдВрджрд╡рддреЗ.
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: resource рд▓рд┐рд╣рд┐рдгреНрдпрд╛рдЖрдзреА provider docs рдордзреАрд▓ рддреНрдпрд╛рдЪреЗ рдкрд╛рди рд╡рд╛рдЪрд╛: рдХреЛрдгрддреЗ arguments replacement рдШрдбрд╡рддрд╛рдд, рдХреЛрдгрддреЗ computed рдЖрд╣реЗрдд, рдЖрдгрд┐ рддреЛ рдХреЛрдгрддрд╛ import id рдЕрдкреЗрдХреНрд╖рд┐рдд рдзрд░рддреЛ. рдкреБрдврдЪрд╛ рдмрджрд▓ рдкрдЯрдХрди рд╣реЛрдгрд╛рд░рд╛ update рдЕрд╕реЗрд▓ рдХреА рдкреБрдиреНрд╣рд╛ рдмрд╛рдВрдзрдХрд╛рдо, рд╣реЗ рддреЗ рдкрд╛рдирдЪ рдард░рд╡рддреЗ.
тПня╕П рдкреБрдвреЗ
рдЖрддрд╛ рдХрдВрддреНрд░рд╛рдЯрджрд╛рд░рд╛рд▓рд╛ рдЖрд░рд╛рдЦрдбреНрдпрд╛рдЪрд╛ рдЕрд░реНрде рдХрд│рддреЛ. рдкреБрдвреЗ: рддреЛ рдХрд╛рдп рдХрд░рд╛рдпрдЪреЗ рддреЗ рдХрд╕реЗ рдард░рд╡рддреЛ тАФ рдореНрд╣рдгрдЬреЗ plan: create, update, replace рдХрд┐рдВрд╡рд╛ destroy.
git checkout lesson-03-plan-apply